OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions | |
6 * are met: | |
7 * 1. Redistributions of source code must retain the above copyright | |
8 * notice, this list of conditions and the following disclaimer. | |
9 * 2. Redistributions in binary form must reproduce the above copyright | |
10 * notice, this list of conditions and the following disclaimer in the | |
11 * documentation and/or other materials provided with the distribution. | |
12 * | |
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 */ | |
25 | |
26 #include "config.h" | |
27 #include "ScrollView.h" | |
28 | |
29 #include "Chrome.h" | |
30 #include "ChromeClient.h" | |
31 #include "FloatRect.h" | |
32 #include "FocusController.h" | |
33 #include "Frame.h" | |
34 #include "FrameView.h" | |
35 #include "GraphicsContext.h" | |
36 #include "IntRect.h" | |
37 #include "NotImplemented.h" | |
38 #include "Page.h" | |
39 #include "PlatformScrollBar.h" | |
40 #include "PlatformMouseEvent.h" | |
41 #include "PlatformWheelEvent.h" | |
42 #include "Range.h" | |
43 #include "RenderTheme.h" | |
44 #include "ScrollBar.h" | |
45 #include "SkiaUtils.h" | |
46 #include "WidgetClientWin.h" | |
47 #include <algorithm> | |
48 #include <wtf/Assertions.h> | |
49 #include <wtf/HashSet.h> | |
50 | |
51 #undef LOG | |
52 #include "base/gfx/platform_canvas_win.h" | |
53 #include "webkit/glue/webframe_impl.h" | |
54 #include "webkit/glue/webview_impl.h" | |
55 | |
56 using namespace std; | |
57 | |
58 namespace WebCore { | |
59 | |
60 class ScrollView::ScrollViewPrivate : public ScrollbarClient { | |
61 public: | |
62 ScrollViewPrivate(ScrollView* view) | |
63 : m_view(view) | |
64 , m_hasStaticBackground(false) | |
65 , m_scrollbarsSuppressed(false) | |
66 , m_inUpdateScrollbars(false) | |
67 , m_scrollbarsAvoidingResizer(0) | |
68 , m_vScrollbarMode(ScrollbarAuto) | |
69 , m_hScrollbarMode(ScrollbarAuto) | |
70 , m_visible(false) | |
71 , m_attachedToWindow(false) | |
72 , m_panScrollIconPoint(0,0) | |
73 , m_drawPanScrollIcon(false) | |
74 { | |
75 } | |
76 | |
77 ~ScrollViewPrivate() | |
78 { | |
79 setHasHorizontalScrollbar(false); | |
80 setHasVerticalScrollbar(false); | |
81 } | |
82 | |
83 void setHasHorizontalScrollbar(bool hasBar); | |
84 void setHasVerticalScrollbar(bool hasBar); | |
85 | |
86 virtual void valueChanged(Scrollbar*); | |
87 virtual IntRect windowClipRect() const; | |
88 virtual bool isActive() const; | |
89 | |
90 void scrollBackingStore(const IntSize& scrollDelta); | |
91 | |
92 // Get the vector containing the result from the FindInPage operation. | |
93 const Vector<RefPtr<Range> >* getTickmarks() const; | |
94 | |
95 // Retrieves the index of the active tickmark for a given frame. If the | |
96 // frame does not have an active tickmark (for example if the active | |
97 // tickmark resides in another frame) this function returns kNoTickmark. | |
98 size_t ScrollView::ScrollViewPrivate::getActiveTickmarkIndex() const; | |
99 | |
100 // This is a helper function for accessing the bitmaps that have been cached | |
101 // in the renderer. | |
102 const SkBitmap* GetPreloadedBitmapFromRenderer(int resource_id) const; | |
103 | |
104 // Highlight the matches found during FindInPage operation. | |
105 void highlightMatches(GraphicsContext* context) const; | |
106 | |
107 // Highlights the node selected in the DOM inspector. | |
108 void highlightInspectedNode(GraphicsContext* context, Frame* frame) const; | |
109 | |
110 // Highlight a certain Range on the page. | |
111 void highlightRange(HDC hdc, HDC mem_dc, RefPtr<Range> range) const; | |
112 | |
113 void setAllowsScrolling(bool); | |
114 bool allowsScrolling() const; | |
115 | |
116 ScrollView* m_view; | |
117 IntSize m_scrollOffset; | |
118 IntSize m_contentsSize; | |
119 bool m_hasStaticBackground; | |
120 bool m_scrollbarsSuppressed; | |
121 bool m_inUpdateScrollbars; | |
122 int m_scrollbarsAvoidingResizer; | |
123 ScrollbarMode m_vScrollbarMode; | |
124 ScrollbarMode m_hScrollbarMode; | |
125 RefPtr<PlatformScrollbar> m_vBar; | |
126 RefPtr<PlatformScrollbar> m_hBar; | |
127 HRGN m_dirtyRegion; | |
128 HashSet<Widget*> m_children; | |
129 bool m_visible; | |
130 bool m_attachedToWindow; | |
131 IntPoint m_panScrollIconPoint; | |
132 bool m_drawPanScrollIcon; | |
133 }; | |
134 | |
135 const int panIconSizeLength = 20; | |
136 | |
137 void ScrollView::ScrollViewPrivate::setHasHorizontalScrollbar(bool hasBar) | |
138 { | |
139 if (Scrollbar::hasPlatformScrollbars()) { | |
140 if (hasBar && !m_hBar) { | |
141 m_hBar = PlatformScrollbar::create(this, HorizontalScrollbar, Regula
rScrollbar); | |
142 m_view->addChild(m_hBar.get()); | |
143 } else if (!hasBar && m_hBar) { | |
144 m_view->removeChild(m_hBar.get()); | |
145 m_hBar = 0; | |
146 } | |
147 } | |
148 } | |
149 | |
150 void ScrollView::ScrollViewPrivate::setHasVerticalScrollbar(bool hasBar) | |
151 { | |
152 if (Scrollbar::hasPlatformScrollbars()) { | |
153 if (hasBar && !m_vBar) { | |
154 m_vBar = PlatformScrollbar::create(this, VerticalScrollbar, RegularS
crollbar); | |
155 m_view->addChild(m_vBar.get()); | |
156 } else if (!hasBar && m_vBar) { | |
157 m_view->removeChild(m_vBar.get()); | |
158 m_vBar = 0; | |
159 } | |
160 } | |
161 } | |
162 | |
163 void ScrollView::ScrollViewPrivate::valueChanged(Scrollbar* bar) | |
164 { | |
165 // Figure out if we really moved. | |
166 IntSize newOffset = m_scrollOffset; | |
167 if (bar) { | |
168 if (bar == m_hBar) | |
169 newOffset.setWidth(bar->value()); | |
170 else if (bar == m_vBar) | |
171 newOffset.setHeight(bar->value()); | |
172 } | |
173 IntSize scrollDelta = newOffset - m_scrollOffset; | |
174 if (scrollDelta == IntSize()) | |
175 return; | |
176 m_scrollOffset = newOffset; | |
177 | |
178 if (m_scrollbarsSuppressed) | |
179 return; | |
180 | |
181 scrollBackingStore(scrollDelta); | |
182 | |
183 if (Frame* frame = static_cast<FrameView*>(m_view)->frame()) { | |
184 frame->sendScrollEvent(); | |
185 | |
186 // Inform the delegate that the scroll position has changed. | |
187 WidgetClientWin* client = | |
188 static_cast<WidgetClientWin*>(m_view->client()); | |
189 if (client) | |
190 client->onScrollPositionChanged(m_view); | |
191 } | |
192 } | |
193 | |
194 void ScrollView::ScrollViewPrivate::scrollBackingStore(const IntSize& scrollDelt
a) | |
195 { | |
196 // Since scrolling is double buffered, we will be blitting the scroll view's
intersection | |
197 // with the clip rect every time to keep it smooth. | |
198 | |
199 IntRect clipRect = m_view->windowClipRect(); | |
200 IntRect scrollViewRect = m_view->convertToContainingWindow(IntRect(0, 0, m_v
iew->visibleWidth(), m_view->visibleHeight())); | |
201 | |
202 // Negative when our frame is smaller than the min scrollbar width. | |
203 if (scrollViewRect.width() < 0) | |
204 scrollViewRect.setWidth(0); | |
205 if (scrollViewRect.height() < 0) | |
206 scrollViewRect.setHeight(0); | |
207 | |
208 if (!m_hasStaticBackground) { // The main frame can just blit the WebView wi
ndow | |
209 // FIXME: Find a way to blit subframes without blitting overlapping cont
ent | |
210 m_view->scrollBackingStore(-scrollDelta.width(), -scrollDelta.height(),
scrollViewRect, clipRect); | |
211 } else { | |
212 IntRect updateRect = clipRect; | |
213 updateRect.intersect(scrollViewRect); | |
214 | |
215 // We need to go ahead and repaint the entire backing store. Do it now
before moving the | |
216 // plugins. | |
217 m_view->addToDirtyRegion(updateRect); | |
218 m_view->updateBackingStore(); | |
219 } | |
220 | |
221 // This call will move child HWNDs (plugins) and invalidate them as well. | |
222 m_view->geometryChanged(); | |
223 } | |
224 | |
225 void ScrollView::ScrollViewPrivate::setAllowsScrolling(bool flag) | |
226 { | |
227 if (flag && m_vScrollbarMode == ScrollbarAlwaysOff) | |
228 m_vScrollbarMode = ScrollbarAuto; | |
229 else if (!flag) | |
230 m_vScrollbarMode = ScrollbarAlwaysOff; | |
231 | |
232 if (flag && m_hScrollbarMode == ScrollbarAlwaysOff) | |
233 m_hScrollbarMode = ScrollbarAuto; | |
234 else if (!flag) | |
235 m_hScrollbarMode = ScrollbarAlwaysOff; | |
236 | |
237 m_view->updateScrollbars(m_scrollOffset); | |
238 } | |
239 | |
240 bool ScrollView::ScrollViewPrivate::allowsScrolling() const | |
241 { | |
242 // Return YES if either horizontal or vertical scrolling is allowed. | |
243 return m_hScrollbarMode != ScrollbarAlwaysOff || m_vScrollbarMode != Scrollb
arAlwaysOff; | |
244 } | |
245 | |
246 IntRect ScrollView::ScrollViewPrivate::windowClipRect() const | |
247 { | |
248 // FrameView::windowClipRect() will exclude the scrollbars, but here we | |
249 // want to include them, so we are forced to cast to FrameView in order to | |
250 // call the non-virtual version of windowClipRect :-( | |
251 // | |
252 // The non-frame case exists to support FramelessScrollView. | |
253 | |
254 const FrameView* frameView = static_cast<const FrameView*>(m_view); | |
255 if (frameView->frame()) | |
256 return frameView->windowClipRect(false); | |
257 | |
258 return m_view->windowClipRect(); | |
259 } | |
260 | |
261 bool ScrollView::ScrollViewPrivate::isActive() const | |
262 { | |
263 Page* page = static_cast<const FrameView*>(m_view)->frame()->page(); | |
264 return page && page->focusController()->isActive(); | |
265 } | |
266 | |
267 const Vector<RefPtr<Range> >* ScrollView::ScrollViewPrivate::getTickmarks() cons
t | |
268 { | |
269 FrameView* view = static_cast<FrameView*>(m_view); | |
270 ASSERT(view); | |
271 Frame* frame = view->frame(); | |
272 | |
273 if (!frame) | |
274 return NULL; // NOTE: Frame can be null for dropdown boxes. | |
275 | |
276 WidgetClientWin* c = static_cast<WidgetClientWin*>(m_view->client()); | |
277 ASSERT(c); | |
278 return c->getTickmarks(view->frame()); | |
279 } | |
280 | |
281 size_t ScrollView::ScrollViewPrivate::getActiveTickmarkIndex() const | |
282 { | |
283 FrameView* view = static_cast<FrameView*>(m_view); | |
284 ASSERT(view); | |
285 Frame* frame = view->frame(); | |
286 | |
287 // NOTE: Frame can be null for dropdown boxes. | |
288 if (!frame) | |
289 return WidgetClientWin::kNoTickmark; | |
290 | |
291 WidgetClientWin* c = static_cast<WidgetClientWin*>(m_view->client()); | |
292 ASSERT(c); | |
293 return c->getActiveTickmarkIndex(view->frame()); | |
294 } | |
295 | |
296 const SkBitmap* ScrollView::ScrollViewPrivate::GetPreloadedBitmapFromRenderer( | |
297 int resource_id) const | |
298 { | |
299 WidgetClientWin* c = static_cast<WidgetClientWin*>(m_view->client()); | |
300 if (!c) | |
301 return NULL; | |
302 | |
303 return c->getPreloadedResourceBitmap(resource_id); | |
304 } | |
305 | |
306 void ScrollView::ScrollViewPrivate::highlightMatches( | |
307 GraphicsContext* context) const | |
308 { | |
309 if (context->paintingDisabled()) | |
310 return; | |
311 | |
312 const Vector<RefPtr<Range> >* tickmarks = getTickmarks(); | |
313 if (!tickmarks || tickmarks->isEmpty()) | |
314 return; | |
315 | |
316 context->save(); | |
317 context->translate(m_view->x(), m_view->y()); | |
318 | |
319 // NOTE: We tolerate the platformContext() call here because the scrollbars | |
320 // will not be serialized, i.e. composition is done in the renderer and | |
321 // never in the browser. | |
322 // Prepare for drawing the arrows along the scroll bar. | |
323 gfx::PlatformCanvas* canvas = context->platformContext()->canvas(); | |
324 | |
325 int horz_start = 0; | |
326 int horz_end = m_view->width(); | |
327 int vert_start = 0; | |
328 int vert_end = m_view->height(); | |
329 | |
330 if (m_vBar) { | |
331 // Account for the amount of scrolling on the vertical scroll bar. | |
332 vert_start += m_scrollOffset.height(); | |
333 vert_end += m_scrollOffset.height(); | |
334 // Don't draw atop the vertical scrollbar. | |
335 horz_end -= PlatformScrollbar::verticalScrollbarWidth() + 1; | |
336 } | |
337 | |
338 if (m_hBar) { | |
339 // Account for the amount of scrolling on the horizontal scroll bar. | |
340 horz_start += m_scrollOffset.width(); | |
341 horz_end += m_scrollOffset.width(); | |
342 // Don't draw atop the horizontal scrollbar. | |
343 vert_end -= PlatformScrollbar::horizontalScrollbarHeight() + 1; | |
344 } | |
345 | |
346 IntRect view_rect(IntPoint(), m_view->size()); | |
347 HDC hdc = context->getWindowsContext(view_rect); | |
348 | |
349 // We create a memory DC, copy the bits we want to highlight to the DC and | |
350 // then MERGE_COPY pieces of it back with a yellow brush selected (which | |
351 // gives them yellow highlighting). | |
352 HDC mem_dc = CreateCompatibleDC(hdc); | |
353 HBITMAP mem_bmp = CreateCompatibleBitmap(hdc, m_view->width(), | |
354 m_view->height()); | |
355 HGDIOBJ old_bmp = SelectObject(mem_dc, mem_bmp); | |
356 | |
357 // Now create a brush for hit highlighting. This is needed for the MERGECOPY | |
358 // to paint a yellow highlight onto the matches found. For more details, see | |
359 // the documentation for BitBlt. | |
360 static const COLORREF kFillColor = RGB(255, 250, 150); // Light yellow. | |
361 HGDIOBJ inactive_brush = CreateSolidBrush(kFillColor); | |
362 static const COLORREF kFillColorActive = RGB(255, 150, 50); // Orange. | |
363 HGDIOBJ active_brush = CreateSolidBrush(kFillColorActive); | |
364 HGDIOBJ old_brush = SelectObject(hdc, inactive_brush); | |
365 | |
366 // Keep a copy of what's on screen, so we can MERGECOPY it back later for | |
367 // the purpose of highlighting the text. | |
368 BitBlt(mem_dc, 0, 0, m_view->width(), m_view->height(), | |
369 hdc, 0, 0, SRCCOPY); | |
370 | |
371 const size_t active_tickmark = getActiveTickmarkIndex(); | |
372 for (Vector<RefPtr<Range> >::const_iterator i = tickmarks->begin(); | |
373 i != tickmarks->end(); ++i) { | |
374 const RefPtr<Range> range = (*i); | |
375 const IntRect& bounds = range->boundingBox(); | |
376 // To highlight the word, we check if the rectangle boundary is within | |
377 // the bounds vertically as well as horizontally. | |
378 if (bounds.bottomRight().y() > vert_start && | |
379 bounds.topLeft().y() < vert_end && | |
380 bounds.bottomRight().x() > horz_start && | |
381 bounds.topLeft().x() < horz_end && | |
382 WebFrameImpl::RangeShouldBeHighlighted(range.get())) { | |
383 // We highlight the active tick-mark with a green color instead | |
384 // of the normal yellow color. | |
385 SelectObject(hdc, ((i - tickmarks->begin()) == active_tickmark) ? | |
386 active_brush : inactive_brush); | |
387 highlightRange(hdc, mem_dc, range); | |
388 } | |
389 } | |
390 | |
391 SelectObject(mem_dc, old_brush); | |
392 DeleteObject(active_brush); | |
393 DeleteObject(inactive_brush); | |
394 | |
395 SelectObject(mem_dc, old_bmp); | |
396 DeleteObject(mem_bmp); | |
397 | |
398 DeleteDC(mem_dc); | |
399 | |
400 context->releaseWindowsContext(hdc, view_rect); | |
401 context->restore(); | |
402 } | |
403 | |
404 // TODO(ojan): http://b/1143983 make this work for inline elements as they can | |
405 // wrap (use highlightRange instead?) | |
406 void ScrollView::ScrollViewPrivate::highlightInspectedNode( | |
407 GraphicsContext* context, Frame* frame) const | |
408 { | |
409 WebViewImpl* c = static_cast<WebViewImpl*>(m_view->client()); | |
410 const WebCore::Node* inspected_node = c->getInspectedNode(frame); | |
411 | |
412 if (!inspected_node) | |
413 return; | |
414 | |
415 SkPaint paint; | |
416 paint.setARGB(122, 255, 225, 0); // Yellow | |
417 | |
418 // TODO(ojan): http://b/1143991 Once we sync a Skia version that supports | |
419 // it, use SkPorterDuff::kScreenMode and remove the transparency. | |
420 // Then port highlightMatches/highlightRanges to use this as well. | |
421 // Although, perhaps the web inspector really should be using | |
422 // an alpha overlay? It's less pretty, but more clear what node | |
423 // is being overlayed. In this case, the TODO is to make | |
424 // highlightMatches/Ranges use Skia and to leave this as is. | |
425 // | |
426 // paint.setPorterDuffXfermode(SkPorterDuff::kScreenMode); | |
427 | |
428 // TODO(ojan): http://b/1143975 Draw the padding/border/margin boxes in | |
429 // different colors. | |
430 context->platformContext()->paintSkPaint(inspected_node->getRect(), paint); | |
431 } | |
432 | |
433 void ScrollView::ScrollViewPrivate::highlightRange(HDC hdc, HDC mem_dc, | |
434 RefPtr<Range> range) const { | |
435 // We need to figure out whether the match that we want to | |
436 // highlight is on a single line or on multiple lines. | |
437 IntRect start = VisiblePosition(range->startPosition()).caretRect(); | |
438 IntRect end = VisiblePosition(range->endPosition()).caretRect(); | |
439 IntRect bounds = range->boundingBox(); | |
440 | |
441 // Multi-line bounds have different y pos for start and end. | |
442 if (start.y() == end.y()) { | |
443 int x = bounds.topLeft().x() - m_scrollOffset.width(); | |
444 int y = bounds.topLeft().y() - m_scrollOffset.height(); | |
445 int w = bounds.bottomRight().x() - bounds.topLeft().x() + 1; | |
446 int h = bounds.bottomRight().y() - bounds.topLeft().y() + 1; | |
447 | |
448 // MERGECOPY the relevant bits back, creating a highlight. | |
449 BitBlt(hdc, x, y, w, h, mem_dc, x, y, MERGECOPY); | |
450 } else { | |
451 // Multi line bounds, for example, when we need to highlight | |
452 // all the numbers (and only the numbers) in this block of | |
453 // text: | |
454 // | |
455 // xxxxxxxxxxxxxxxx11111111 | |
456 // 222222222222222222222222 | |
457 // 222222222222222222222222 | |
458 // 333333333333333xxxxxxxxx | |
459 // | |
460 // In this case, the bounding box will contain all the text, | |
461 // (including the exes (x)). We highlight in three steps. | |
462 // First we highlight the segment containing the ones (1) | |
463 // above. Then the whole middle section is highlighted, or the | |
464 // twos (2), and finally the remaining segment consisting of | |
465 // the threes (3) is highlighted. | |
466 | |
467 const int row_height = start.height(); | |
468 int x = 0, y = 0, w = 0, h = 0; | |
469 | |
470 // The start and end caret can be outside the bounding box, for leading | |
471 // and trailing whitespace and we should not highlight those. | |
472 if (start.intersects(bounds)) { | |
473 // Highlight the first segment. | |
474 x = start.x() - m_scrollOffset.width(); | |
475 y = start.y() - m_scrollOffset.height(); | |
476 w = bounds.topRight().x() - start.x() + 1; | |
477 h = row_height; | |
478 | |
479 BitBlt(hdc, x, y, w, h, mem_dc, x, y, MERGECOPY); | |
480 } | |
481 | |
482 // Figure out how large the middle section is. | |
483 int rows_between = (end.y() - start.y()) / row_height - 1; | |
484 | |
485 if (rows_between > 0) { | |
486 // Highlight the middle segment. | |
487 x = bounds.x() - m_scrollOffset.width(); | |
488 y = bounds.y() - m_scrollOffset.height() + row_height; | |
489 w = bounds.width(); | |
490 h = rows_between * row_height; | |
491 | |
492 BitBlt(hdc, x, y, w, h, mem_dc, x, y, MERGECOPY); | |
493 } | |
494 | |
495 // The end caret might not intersect the bounding box, for example | |
496 // when highlighting the last letter of a line that wraps. In that | |
497 // case the end caret is set to the beginning of the next line, and | |
498 // since it doesn't intersect with the bounding box we don't need to | |
499 // highlight. | |
500 if (end.intersects(bounds)) { | |
501 // Highlight the remaining segment. | |
502 x = bounds.bottomLeft().x() - m_scrollOffset.width(); | |
503 y = bounds.bottomLeft().y() - m_scrollOffset.height() - | |
504 row_height + 1; | |
505 w = end.x() - bounds.bottomLeft().x(); | |
506 h = row_height; | |
507 | |
508 BitBlt(hdc, x, y, w, h, mem_dc, x, y, MERGECOPY); | |
509 } | |
510 } | |
511 } | |
512 | |
513 ScrollView::ScrollView() | |
514 { | |
515 m_data = new ScrollViewPrivate(this); | |
516 } | |
517 | |
518 ScrollView::~ScrollView() | |
519 { | |
520 delete m_data; | |
521 } | |
522 | |
523 void ScrollView::updateContents(const IntRect& rect, bool now) | |
524 { | |
525 if (rect.isEmpty()) | |
526 return; | |
527 | |
528 IntRect containingWindowRect = contentsToWindow(rect); | |
529 | |
530 if (containingWindowRect.x() < 0) | |
531 containingWindowRect.setX(0); | |
532 if (containingWindowRect.y() < 0) | |
533 containingWindowRect.setY(0); | |
534 | |
535 updateWindowRect(containingWindowRect, now); | |
536 } | |
537 | |
538 void ScrollView::updateWindowRect(const IntRect& rect, bool now) | |
539 { | |
540 // TODO(dglazkov): make sure this is actually the right way to do this | |
541 | |
542 // Cache the dirty spot. | |
543 addToDirtyRegion(rect); | |
544 | |
545 // since painting always happens asynchronously, we don't have a way to | |
546 // honor the "now" parameter. it is unclear if it matters. | |
547 if (now) { | |
548 // TODO(iyengar): Should we force a layout to occur here? | |
549 geometryChanged(); | |
550 } | |
551 } | |
552 | |
553 void ScrollView::update() | |
554 { | |
555 // TODO(iyengar): Should we force a layout to occur here? | |
556 geometryChanged(); | |
557 } | |
558 | |
559 int ScrollView::visibleWidth() const | |
560 { | |
561 return width() - (m_data->m_vBar ? m_data->m_vBar->width() : 0); | |
562 } | |
563 | |
564 int ScrollView::visibleHeight() const | |
565 { | |
566 return height() - (m_data->m_hBar ? m_data->m_hBar->height() : 0); | |
567 } | |
568 | |
569 FloatRect ScrollView::visibleContentRect() const | |
570 { | |
571 return FloatRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); | |
572 } | |
573 | |
574 FloatRect ScrollView::visibleContentRectConsideringExternalScrollers() const | |
575 { | |
576 // external scrollers not supported for now | |
577 return visibleContentRect(); | |
578 } | |
579 | |
580 void ScrollView::setContentsPos(int newX, int newY) | |
581 { | |
582 int dx = newX - contentsX(); | |
583 int dy = newY - contentsY(); | |
584 scrollBy(dx, dy); | |
585 } | |
586 | |
587 void ScrollView::resizeContents(int w, int h) | |
588 { | |
589 IntSize newContentsSize(w, h); | |
590 if (m_data->m_contentsSize != newContentsSize) { | |
591 m_data->m_contentsSize = newContentsSize; | |
592 updateScrollbars(m_data->m_scrollOffset); | |
593 } | |
594 } | |
595 | |
596 void ScrollView::setFrameGeometry(const IntRect& newGeometry) | |
597 { | |
598 IntRect normalizedNewGeometry = newGeometry; | |
599 | |
600 // Webkit sometimes attempts to set negative sizes due to | |
601 // sloppy calculations of width with margins and such. | |
602 // (RenderPart:updateWidgetPosition is one example.) | |
603 // Safeguard against this and prevent negative heights/widths. | |
604 if (normalizedNewGeometry.width() < 0) | |
605 normalizedNewGeometry.setWidth(0); | |
606 if (normalizedNewGeometry.height() < 0) | |
607 normalizedNewGeometry.setHeight(0); | |
608 | |
609 IntRect oldGeometry = frameGeometry(); | |
610 Widget::setFrameGeometry(normalizedNewGeometry); | |
611 | |
612 if (normalizedNewGeometry == oldGeometry) | |
613 return; | |
614 | |
615 if (normalizedNewGeometry.width() != oldGeometry.width() || | |
616 normalizedNewGeometry.height() != oldGeometry.height()) { | |
617 updateScrollbars(m_data->m_scrollOffset); | |
618 | |
619 // when used to display a popup menu, we do not have a frame | |
620 FrameView* frameView = static_cast<FrameView*>(this); | |
621 if (frameView->frame()) | |
622 frameView->setNeedsLayout(); | |
623 } | |
624 | |
625 geometryChanged(); | |
626 } | |
627 | |
628 int ScrollView::contentsX() const | |
629 { | |
630 return scrollOffset().width(); | |
631 } | |
632 | |
633 int ScrollView::contentsY() const | |
634 { | |
635 return scrollOffset().height(); | |
636 } | |
637 | |
638 int ScrollView::contentsWidth() const | |
639 { | |
640 return m_data->m_contentsSize.width(); | |
641 } | |
642 | |
643 int ScrollView::contentsHeight() const | |
644 { | |
645 return m_data->m_contentsSize.height(); | |
646 } | |
647 | |
648 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const | |
649 { | |
650 IntPoint viewPoint = convertFromContainingWindow(windowPoint); | |
651 return viewPoint + scrollOffset(); | |
652 } | |
653 | |
654 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const | |
655 { | |
656 IntPoint viewPoint = contentsPoint - scrollOffset(); | |
657 return convertToContainingWindow(viewPoint); | |
658 } | |
659 | |
660 IntPoint ScrollView::convertChildToSelf(const Widget* child, const IntPoint& poi
nt) const | |
661 { | |
662 IntPoint newPoint = point; | |
663 if (child != m_data->m_hBar && child != m_data->m_vBar) | |
664 newPoint = point - scrollOffset(); | |
665 return Widget::convertChildToSelf(child, newPoint); | |
666 } | |
667 | |
668 IntPoint ScrollView::convertSelfToChild(const Widget* child, const IntPoint& poi
nt) const | |
669 { | |
670 IntPoint newPoint = point; | |
671 if (child != m_data->m_hBar && child != m_data->m_vBar) | |
672 newPoint = point + scrollOffset(); | |
673 return Widget::convertSelfToChild(child, newPoint); | |
674 } | |
675 | |
676 IntSize ScrollView::scrollOffset() const | |
677 { | |
678 return m_data->m_scrollOffset; | |
679 } | |
680 | |
681 IntSize ScrollView::maximumScroll() const | |
682 { | |
683 // We should not check whether scrolling is allowed for this view before cal
culating | |
684 // the maximumScroll. Please refer to http://b/issue?id=1164704, where in sc
rolling | |
685 // would not work on a scrollview created with scrollbars disabled. The curr
ent | |
686 // behavior mirrors Safari's webkit implementation. Firefox also behaves sim
ilarly. | |
687 IntSize delta = (m_data->m_contentsSize - IntSize(visibleWidth(), visibleHei
ght())) - scrollOffset(); | |
688 delta.clampNegativeToZero(); | |
689 return delta; | |
690 } | |
691 | |
692 void ScrollView::scrollBy(int dx, int dy) | |
693 { | |
694 IntSize scrollOffset = m_data->m_scrollOffset; | |
695 IntSize newScrollOffset = scrollOffset + IntSize(dx, dy).shrunkTo(maximumScr
oll()); | |
696 newScrollOffset.clampNegativeToZero(); | |
697 | |
698 if (newScrollOffset == scrollOffset) | |
699 return; | |
700 | |
701 updateScrollbars(newScrollOffset); | |
702 } | |
703 | |
704 void ScrollView::scrollRectIntoViewRecursively(const IntRect& r) | |
705 { | |
706 IntPoint p(max(0, r.x()), max(0, r.y())); | |
707 ScrollView* view = this; | |
708 while (view) { | |
709 view->setContentsPos(p.x(), p.y()); | |
710 p.move(view->x() - view->scrollOffset().width(), view->y() - view->scrol
lOffset().height()); | |
711 view = static_cast<ScrollView*>(view->parent()); | |
712 } | |
713 } | |
714 | |
715 WebCore::ScrollbarMode ScrollView::hScrollbarMode() const | |
716 { | |
717 return m_data->m_hScrollbarMode; | |
718 } | |
719 | |
720 WebCore::ScrollbarMode ScrollView::vScrollbarMode() const | |
721 { | |
722 return m_data->m_vScrollbarMode; | |
723 } | |
724 | |
725 void ScrollView::suppressScrollbars(bool suppressed, bool repaintOnSuppress) | |
726 { | |
727 m_data->m_scrollbarsSuppressed = suppressed; | |
728 if (repaintOnSuppress && !suppressed) { | |
729 if (m_data->m_hBar) | |
730 m_data->m_hBar->invalidate(); | |
731 if (m_data->m_vBar) | |
732 m_data->m_vBar->invalidate(); | |
733 | |
734 // Invalidate the scroll corner too on unsuppress. | |
735 IntRect hCorner; | |
736 if (m_data->m_hBar && width() - m_data->m_hBar->width() > 0) { | |
737 hCorner = IntRect(m_data->m_hBar->width(), | |
738 height() - m_data->m_hBar->height(), | |
739 width() - m_data->m_hBar->width(), | |
740 m_data->m_hBar->height()); | |
741 invalidateRect(hCorner); | |
742 } | |
743 | |
744 if (m_data->m_vBar && height() - m_data->m_vBar->height() > 0) { | |
745 IntRect vCorner(width() - m_data->m_vBar->width(), | |
746 m_data->m_vBar->height(), | |
747 m_data->m_vBar->width(), | |
748 height() - m_data->m_vBar->height()); | |
749 if (vCorner != hCorner) | |
750 invalidateRect(vCorner); | |
751 } | |
752 } | |
753 } | |
754 | |
755 void ScrollView::setHScrollbarMode(ScrollbarMode newMode) | |
756 { | |
757 if (m_data->m_hScrollbarMode != newMode) { | |
758 m_data->m_hScrollbarMode = newMode; | |
759 updateScrollbars(m_data->m_scrollOffset); | |
760 } | |
761 } | |
762 | |
763 void ScrollView::setVScrollbarMode(ScrollbarMode newMode) | |
764 { | |
765 if (m_data->m_vScrollbarMode != newMode) { | |
766 m_data->m_vScrollbarMode = newMode; | |
767 updateScrollbars(m_data->m_scrollOffset); | |
768 } | |
769 } | |
770 | |
771 void ScrollView::setScrollbarsMode(ScrollbarMode newMode) | |
772 { | |
773 if (m_data->m_hScrollbarMode != newMode || | |
774 m_data->m_vScrollbarMode != newMode) { | |
775 m_data->m_hScrollbarMode = m_data->m_vScrollbarMode = newMode; | |
776 updateScrollbars(m_data->m_scrollOffset); | |
777 } | |
778 } | |
779 | |
780 void ScrollView::setStaticBackground(bool flag) | |
781 { | |
782 m_data->m_hasStaticBackground = flag; | |
783 } | |
784 | |
785 void ScrollView::updateScrollbars(const IntSize& desiredOffset) | |
786 { | |
787 // Don't allow re-entrancy into this function. | |
788 if (m_data->m_inUpdateScrollbars) | |
789 return; | |
790 | |
791 m_data->m_inUpdateScrollbars = true; | |
792 | |
793 bool hasVerticalScrollbar = m_data->m_vBar; | |
794 bool hasHorizontalScrollbar = m_data->m_hBar; | |
795 bool oldHasVertical = hasVerticalScrollbar; | |
796 bool oldHasHorizontal = hasHorizontalScrollbar; | |
797 ScrollbarMode hScroll = m_data->m_hScrollbarMode; | |
798 ScrollbarMode vScroll = m_data->m_vScrollbarMode; | |
799 | |
800 const int cVerticalWidth = PlatformScrollbar::verticalScrollbarWidth(); | |
801 const int cHorizontalHeight = PlatformScrollbar::horizontalScrollbarHeight()
; | |
802 | |
803 // we may not be able to support scrollbars due to our frame geometry | |
804 if (width() < cVerticalWidth) | |
805 vScroll = ScrollbarAlwaysOff; | |
806 if (height() < cHorizontalHeight) | |
807 hScroll = ScrollbarAlwaysOff; | |
808 | |
809 for (int pass = 0; pass < 2; pass++) { | |
810 bool scrollsVertically; | |
811 bool scrollsHorizontally; | |
812 | |
813 if (!m_data->m_scrollbarsSuppressed && (hScroll == ScrollbarAuto || vScr
oll == ScrollbarAuto)) { | |
814 // Do a layout if pending before checking if scrollbars are needed. | |
815 if (hasVerticalScrollbar != oldHasVertical || hasHorizontalScrollbar
!= oldHasHorizontal) | |
816 static_cast<FrameView*>(this)->layout(); | |
817 | |
818 scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScroll == Sc
rollbarAuto && contentsHeight() > height()); | |
819 if (scrollsVertically) | |
820 scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll
== ScrollbarAuto && contentsWidth() + cVerticalWidth > width()); | |
821 else { | |
822 scrollsHorizontally = (hScroll == ScrollbarAlwaysOn) || (hScroll
== ScrollbarAuto && contentsWidth() > width()); | |
823 if (scrollsHorizontally) | |
824 scrollsVertically = (vScroll == ScrollbarAlwaysOn) || (vScro
ll == ScrollbarAuto && contentsHeight() + cHorizontalHeight > height()); | |
825 } | |
826 } | |
827 else { | |
828 scrollsHorizontally = (hScroll == ScrollbarAuto) ? hasHorizontalScro
llbar : (hScroll == ScrollbarAlwaysOn); | |
829 scrollsVertically = (vScroll == ScrollbarAuto) ? hasVerticalScrollba
r : (vScroll == ScrollbarAlwaysOn); | |
830 } | |
831 | |
832 if (hasVerticalScrollbar != scrollsVertically) { | |
833 m_data->setHasVerticalScrollbar(scrollsVertically); | |
834 hasVerticalScrollbar = scrollsVertically; | |
835 } | |
836 | |
837 if (hasHorizontalScrollbar != scrollsHorizontally) { | |
838 m_data->setHasHorizontalScrollbar(scrollsHorizontally); | |
839 hasHorizontalScrollbar = scrollsHorizontally; | |
840 } | |
841 } | |
842 | |
843 // Set up the range (and page step/line step). | |
844 IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight()
- visibleHeight()); | |
845 IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition); | |
846 scroll.clampNegativeToZero(); | |
847 | |
848 if (m_data->m_hBar) { | |
849 int clientWidth = visibleWidth(); | |
850 m_data->m_hBar->setEnabled(contentsWidth() > clientWidth); | |
851 int pageStep = (clientWidth - PAGE_KEEP); | |
852 if (pageStep < 0) pageStep = clientWidth; | |
853 IntRect oldRect(m_data->m_hBar->frameGeometry()); | |
854 IntRect hBarRect = IntRect(0, | |
855 height() - m_data->m_hBar->height(), | |
856 width() - (m_data->m_vBar ? m_data->m_vBar->w
idth() : 0), | |
857 m_data->m_hBar->height()); | |
858 m_data->m_hBar->setRect(hBarRect); | |
859 if (!m_data->m_scrollbarsSuppressed && oldRect != m_data->m_hBar->frameG
eometry()) | |
860 m_data->m_hBar->invalidate(); | |
861 | |
862 if (m_data->m_scrollbarsSuppressed) | |
863 m_data->m_hBar->setSuppressInvalidation(true); | |
864 m_data->m_hBar->setSteps(LINE_STEP, pageStep); | |
865 m_data->m_hBar->setProportion(clientWidth, contentsWidth()); | |
866 m_data->m_hBar->setValue(scroll.width()); | |
867 if (m_data->m_scrollbarsSuppressed) | |
868 m_data->m_hBar->setSuppressInvalidation(false); | |
869 } | |
870 | |
871 if (m_data->m_vBar) { | |
872 int clientHeight = visibleHeight(); | |
873 m_data->m_vBar->setEnabled(contentsHeight() > clientHeight); | |
874 int pageStep = (clientHeight - PAGE_KEEP); | |
875 if (pageStep < 0) pageStep = clientHeight; | |
876 IntRect oldRect(m_data->m_vBar->frameGeometry()); | |
877 IntRect vBarRect = IntRect(width() - m_data->m_vBar->width(), | |
878 0, | |
879 m_data->m_vBar->width(), | |
880 height() - (m_data->m_hBar ? m_data->m_hBar->
height() : 0)); | |
881 m_data->m_vBar->setRect(vBarRect); | |
882 if (!m_data->m_scrollbarsSuppressed && oldRect != m_data->m_vBar->frameG
eometry()) | |
883 m_data->m_vBar->invalidate(); | |
884 | |
885 if (m_data->m_scrollbarsSuppressed) | |
886 m_data->m_vBar->setSuppressInvalidation(true); | |
887 m_data->m_vBar->setSteps(LINE_STEP, pageStep); | |
888 m_data->m_vBar->setProportion(clientHeight, contentsHeight()); | |
889 m_data->m_vBar->setValue(scroll.height()); | |
890 if (m_data->m_scrollbarsSuppressed) | |
891 m_data->m_vBar->setSuppressInvalidation(false); | |
892 } | |
893 | |
894 if (oldHasVertical != (m_data->m_vBar != 0) || oldHasHorizontal != (m_data->
m_hBar != 0)) | |
895 geometryChanged(); | |
896 | |
897 // See if our offset has changed in a situation where we might not have scro
llbars. | |
898 // This can happen when editing a body with overflow:hidden and scrolling to
reveal selection. | |
899 // It can also happen when maximizing a window that has scrollbars (but the
new maximized result | |
900 // does not). | |
901 IntSize scrollDelta = scroll - m_data->m_scrollOffset; | |
902 if (scrollDelta != IntSize()) { | |
903 m_data->m_scrollOffset = scroll; | |
904 m_data->scrollBackingStore(scrollDelta); | |
905 | |
906 // Inform the delegate that the scroll position has changed. | |
907 WidgetClientWin* c = static_cast<WidgetClientWin*>(client()); | |
908 if (c) | |
909 c->onScrollPositionChanged(this); | |
910 } | |
911 | |
912 m_data->m_inUpdateScrollbars = false; | |
913 | |
914 ASSERT(visibleWidth() >= 0); | |
915 ASSERT(visibleHeight() >= 0); | |
916 } | |
917 | |
918 PlatformScrollbar* ScrollView::scrollbarUnderMouse(const PlatformMouseEvent& mou
seEvent) | |
919 { | |
920 IntPoint viewPoint = convertFromContainingWindow(mouseEvent.pos()); | |
921 if (m_data->m_hBar && m_data->m_hBar->frameGeometry().contains(viewPoint)) | |
922 return m_data->m_hBar.get(); | |
923 if (m_data->m_vBar && m_data->m_vBar->frameGeometry().contains(viewPoint)) | |
924 return m_data->m_vBar.get(); | |
925 return 0; | |
926 } | |
927 | |
928 void ScrollView::addChild(Widget* child) | |
929 { | |
930 child->setParent(this); | |
931 | |
932 // There is only one global widget client (which should be the WebViewImpl). | |
933 // It is responsible for things like capturing the mouse. | |
934 child->setClient(client()); | |
935 | |
936 m_data->m_children.add(child); | |
937 } | |
938 | |
939 void ScrollView::removeChild(Widget* child) | |
940 { | |
941 child->setParent(0); | |
942 m_data->m_children.remove(child); | |
943 } | |
944 | |
945 void ScrollView::paint(GraphicsContext* context, const IntRect& rect) | |
946 { | |
947 // FIXME: This code is here so we don't have to fork FrameView.h/.cpp. | |
948 // In the end, FrameView should just merge with ScrollView. | |
949 ASSERT(isFrameView()); | |
950 | |
951 if (context->paintingDisabled()) | |
952 return; | |
953 | |
954 if (Frame* frame = static_cast<FrameView*>(this)->frame()) { | |
955 IntRect documentDirtyRect = rect; | |
956 documentDirtyRect.intersect(frameGeometry()); | |
957 | |
958 context->save(); | |
959 | |
960 context->translate(x(), y()); | |
961 documentDirtyRect.move(-x(), -y()); | |
962 | |
963 context->translate(-contentsX(), -contentsY()); | |
964 documentDirtyRect.move(contentsX(), contentsY()); | |
965 | |
966 // do not allow painting outside of the dirty rect | |
967 context->clip(documentDirtyRect); | |
968 | |
969 frame->paint(context, documentDirtyRect); | |
970 | |
971 // Highlights the node selected in the DOM inspector. | |
972 m_data->highlightInspectedNode(context, frame); | |
973 | |
974 context->restore(); | |
975 } | |
976 | |
977 // Highlight the matches found on the page, during a FindInPage operation. | |
978 m_data->highlightMatches(context); | |
979 | |
980 // Now paint the scrollbars. | |
981 if (!m_data->m_scrollbarsSuppressed && (m_data->m_hBar || m_data->m_vBar)) { | |
982 context->save(); | |
983 IntRect scrollViewDirtyRect = rect; | |
984 scrollViewDirtyRect.intersect(frameGeometry()); | |
985 context->translate(x(), y()); | |
986 scrollViewDirtyRect.move(-x(), -y()); | |
987 if (m_data->m_hBar) | |
988 m_data->m_hBar->paint(context, scrollViewDirtyRect); | |
989 if (m_data->m_vBar) | |
990 m_data->m_vBar->paint(context, scrollViewDirtyRect); | |
991 | |
992 // Fill the scroll corner with white. | |
993 IntRect hCorner; | |
994 if (m_data->m_hBar && width() - m_data->m_hBar->width() > 0) { | |
995 hCorner = IntRect(m_data->m_hBar->width(), | |
996 height() - m_data->m_hBar->height(), | |
997 width() - m_data->m_hBar->width(), | |
998 m_data->m_hBar->height()); | |
999 if (hCorner.intersects(scrollViewDirtyRect)) | |
1000 context->fillRect(hCorner, Color::white); | |
1001 } | |
1002 | |
1003 if (m_data->m_vBar && height() - m_data->m_vBar->height() > 0) { | |
1004 IntRect vCorner(width() - m_data->m_vBar->width(), | |
1005 m_data->m_vBar->height(), | |
1006 m_data->m_vBar->width(), | |
1007 height() - m_data->m_vBar->height()); | |
1008 if (vCorner != hCorner && vCorner.intersects(scrollViewDirtyRect)) | |
1009 context->fillRect(vCorner, Color::white); | |
1010 } | |
1011 | |
1012 context->restore(); | |
1013 } | |
1014 } | |
1015 | |
1016 void ScrollView::themeChanged() | |
1017 { | |
1018 PlatformScrollbar::themeChanged(); | |
1019 theme()->themeChanged(); | |
1020 invalidate(); | |
1021 } | |
1022 | |
1023 void ScrollView::wheelEvent(PlatformWheelEvent& e) | |
1024 { | |
1025 if (!m_data->allowsScrolling()) | |
1026 return; | |
1027 | |
1028 // Determine how much we want to scroll. If we can move at all, we will acc
ept the event. | |
1029 IntSize maxScrollDelta = maximumScroll(); | |
1030 if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) || | |
1031 (e.deltaX() > 0 && scrollOffset().width() > 0) || | |
1032 (e.deltaY() < 0 && maxScrollDelta.height() > 0) || | |
1033 (e.deltaY() > 0 && scrollOffset().height() > 0)) { | |
1034 e.accept(); | |
1035 scrollBy(-e.deltaX() * LINE_STEP, -e.deltaY() * LINE_STEP); | |
1036 } | |
1037 } | |
1038 | |
1039 HashSet<Widget*>* ScrollView::children() | |
1040 { | |
1041 return &(m_data->m_children); | |
1042 } | |
1043 | |
1044 void ScrollView::geometryChanged() const | |
1045 { | |
1046 HashSet<Widget*>::const_iterator end = m_data->m_children.end(); | |
1047 for (HashSet<Widget*>::const_iterator current = m_data->m_children.begin();
current != end; ++current) | |
1048 (*current)->geometryChanged(); | |
1049 } | |
1050 | |
1051 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity
) | |
1052 { | |
1053 if (direction == ScrollUp || direction == ScrollDown) { | |
1054 if (m_data->m_vBar) | |
1055 return m_data->m_vBar->scroll(direction, granularity); | |
1056 } else { | |
1057 if (m_data->m_hBar) | |
1058 return m_data->m_hBar->scroll(direction, granularity); | |
1059 } | |
1060 return false; | |
1061 } | |
1062 | |
1063 IntRect ScrollView::windowResizerRect() | |
1064 { | |
1065 return IntRect(); | |
1066 } | |
1067 | |
1068 bool ScrollView::resizerOverlapsContent() const | |
1069 { | |
1070 return !m_data->m_scrollbarsAvoidingResizer; | |
1071 } | |
1072 | |
1073 void ScrollView::adjustOverlappingScrollbarCount(int overlapDelta) | |
1074 { | |
1075 int oldCount = m_data->m_scrollbarsAvoidingResizer; | |
1076 m_data->m_scrollbarsAvoidingResizer += overlapDelta; | |
1077 if (parent() && parent()->isFrameView()) | |
1078 static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(overl
apDelta); | |
1079 else if (!m_data->m_scrollbarsSuppressed) { | |
1080 // If we went from n to 0 or from 0 to n and we're the outermost view, | |
1081 // we need to invalidate the windowResizerRect(), since it will now need
to paint | |
1082 // differently. | |
1083 if (oldCount > 0 && m_data->m_scrollbarsAvoidingResizer == 0 || | |
1084 oldCount == 0 && m_data->m_scrollbarsAvoidingResizer > 0) | |
1085 invalidateRect(windowResizerRect()); | |
1086 } | |
1087 } | |
1088 | |
1089 void ScrollView::setParent(ScrollView* parentView) | |
1090 { | |
1091 if (!parentView && m_data->m_scrollbarsAvoidingResizer && parent() && parent
()->isFrameView()) | |
1092 static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(false
); | |
1093 Widget::setParent(parentView); | |
1094 } | |
1095 | |
1096 void ScrollView::addToDirtyRegion(const IntRect& containingWindowRect) | |
1097 { | |
1098 WidgetClientWin* c = static_cast<WidgetClientWin*>(client()); | |
1099 if (c) | |
1100 c->invalidateRect(containingWindowRect); | |
1101 } | |
1102 | |
1103 void ScrollView::scrollBackingStore(int dx, int dy, const IntRect& scrollViewRec
t, const IntRect& clipRect) | |
1104 { | |
1105 // We don't know how to scroll in two directions at once. | |
1106 if (dx && dy) { | |
1107 IntRect updateRect = clipRect; | |
1108 updateRect.intersect(scrollViewRect); | |
1109 addToDirtyRegion(updateRect); | |
1110 return; | |
1111 } | |
1112 | |
1113 WidgetClientWin* c = static_cast<WidgetClientWin*>(client()); | |
1114 if (c) { | |
1115 // TODO(ericroman): would be better to pass both the scroll rect | |
1116 // and clip rect up to the client and let them decide how best to | |
1117 // scroll the backing store. | |
1118 IntRect clippedScrollRect = scrollViewRect; | |
1119 clippedScrollRect.intersect(clipRect); | |
1120 c->scrollRect(dx, dy, clippedScrollRect); | |
1121 } | |
1122 } | |
1123 | |
1124 void ScrollView::updateBackingStore() | |
1125 { | |
1126 // nothing to do. painting happens asynchronously. | |
1127 } | |
1128 | |
1129 bool ScrollView::inWindow() const | |
1130 { | |
1131 WidgetClientWin* c = static_cast<WidgetClientWin*>(client()); | |
1132 if (!c) | |
1133 return false; | |
1134 | |
1135 return !c->isHidden(); | |
1136 } | |
1137 | |
1138 void ScrollView::attachToWindow() | |
1139 { | |
1140 if (m_data->m_attachedToWindow) | |
1141 return; | |
1142 | |
1143 m_data->m_attachedToWindow = true; | |
1144 | |
1145 if (m_data->m_visible) { | |
1146 HashSet<Widget*>::iterator end = m_data->m_children.end(); | |
1147 for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it != e
nd; ++it) | |
1148 (*it)->attachToWindow(); | |
1149 } | |
1150 } | |
1151 | |
1152 void ScrollView::detachFromWindow() | |
1153 { | |
1154 if (!m_data->m_attachedToWindow) | |
1155 return; | |
1156 | |
1157 if (m_data->m_visible) { | |
1158 HashSet<Widget*>::iterator end = m_data->m_children.end(); | |
1159 for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it != e
nd; ++it) | |
1160 (*it)->detachFromWindow(); | |
1161 } | |
1162 | |
1163 m_data->m_attachedToWindow = false; | |
1164 } | |
1165 | |
1166 void ScrollView::show() | |
1167 { | |
1168 if (!m_data->m_visible) { | |
1169 m_data->m_visible = true; | |
1170 if (isAttachedToWindow()) { | |
1171 HashSet<Widget*>::iterator end = m_data->m_children.end(); | |
1172 for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it
!= end; ++it) | |
1173 (*it)->attachToWindow(); | |
1174 } | |
1175 } | |
1176 | |
1177 Widget::show(); | |
1178 } | |
1179 | |
1180 void ScrollView::hide() | |
1181 { | |
1182 if (m_data->m_visible) { | |
1183 if (isAttachedToWindow()) { | |
1184 HashSet<Widget*>::iterator end = m_data->m_children.end(); | |
1185 for (HashSet<Widget*>::iterator it = m_data->m_children.begin(); it
!= end; ++it) | |
1186 (*it)->detachFromWindow(); | |
1187 } | |
1188 m_data->m_visible = false; | |
1189 } | |
1190 | |
1191 Widget::hide(); | |
1192 } | |
1193 | |
1194 bool ScrollView::isAttachedToWindow() const | |
1195 { | |
1196 return m_data->m_attachedToWindow; | |
1197 } | |
1198 | |
1199 void ScrollView::setAllowsScrolling(bool flag) | |
1200 { | |
1201 m_data->setAllowsScrolling(flag); | |
1202 } | |
1203 | |
1204 bool ScrollView::allowsScrolling() const | |
1205 { | |
1206 return m_data->allowsScrolling(); | |
1207 } | |
1208 | |
1209 void ScrollView::printPanScrollIcon(const IntPoint& iconPosition) | |
1210 { | |
1211 m_data->m_drawPanScrollIcon = true; | |
1212 m_data->m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength
/ 2 , iconPosition.y() - panIconSizeLength / 2) ; | |
1213 | |
1214 updateWindowRect(IntRect(m_data->m_panScrollIconPoint, IntSize(panIconSizeLe
ngth,panIconSizeLength)), true); | |
1215 } | |
1216 | |
1217 void ScrollView::removePanScrollIcon() | |
1218 { | |
1219 m_data->m_drawPanScrollIcon = false; | |
1220 | |
1221 updateWindowRect(IntRect(m_data->m_panScrollIconPoint, IntSize(panIconSizeLe
ngth, panIconSizeLength)), true); | |
1222 } | |
1223 | |
1224 bool ScrollView::isScrollable() | |
1225 { | |
1226 return m_data->m_vBar != 0 || m_data->m_hBar != 0; | |
1227 } | |
1228 | |
1229 } // namespace WebCore | |
OLD | NEW |