1 /*
2 * Copyright (C) 2006, 2007, 2008 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 "GraphicsContext.h"
30 #include "HostWindow.h"
31 #include "PlatformMouseEvent.h"
32 #include "PlatformWheelEvent.h"
33 #include "Scrollbar.h"
34 #include "ScrollbarTheme.h"
35 #include <wtf/StdLibExtras.h>
36
37 using std::max;
38
39 namespace WebCore {
40
ScrollView()41 ScrollView::ScrollView()
42 : m_horizontalScrollbarMode(ScrollbarAuto)
43 , m_verticalScrollbarMode(ScrollbarAuto)
44 , m_prohibitsScrolling(false)
45 , m_canBlitOnScroll(true)
46 , m_scrollbarsAvoidingResizer(0)
47 , m_scrollbarsSuppressed(false)
48 , m_inUpdateScrollbars(false)
49 , m_updateScrollbarsPass(0)
50 , m_drawPanScrollIcon(false)
51 , m_useFixedLayout(false)
52 , m_paintsEntireContents(false)
53 {
54 platformInit();
55 }
56
~ScrollView()57 ScrollView::~ScrollView()
58 {
59 platformDestroy();
60 }
61
addChild(PassRefPtr<Widget> prpChild)62 void ScrollView::addChild(PassRefPtr<Widget> prpChild)
63 {
64 Widget* child = prpChild.get();
65 ASSERT(child != this && !child->parent());
66 child->setParent(this);
67 m_children.add(prpChild);
68 if (child->platformWidget())
69 platformAddChild(child);
70 }
71
removeChild(Widget * child)72 void ScrollView::removeChild(Widget* child)
73 {
74 ASSERT(child->parent() == this);
75 child->setParent(0);
76 m_children.remove(child);
77 if (child->platformWidget())
78 platformRemoveChild(child);
79 }
80
setHasHorizontalScrollbar(bool hasBar)81 void ScrollView::setHasHorizontalScrollbar(bool hasBar)
82 {
83 if (hasBar && !m_horizontalScrollbar) {
84 m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
85 addChild(m_horizontalScrollbar.get());
86 m_horizontalScrollbar->styleChanged();
87 } else if (!hasBar && m_horizontalScrollbar) {
88 removeChild(m_horizontalScrollbar.get());
89 m_horizontalScrollbar = 0;
90 }
91 }
92
setHasVerticalScrollbar(bool hasBar)93 void ScrollView::setHasVerticalScrollbar(bool hasBar)
94 {
95 if (hasBar && !m_verticalScrollbar) {
96 m_verticalScrollbar = createScrollbar(VerticalScrollbar);
97 addChild(m_verticalScrollbar.get());
98 m_verticalScrollbar->styleChanged();
99 } else if (!hasBar && m_verticalScrollbar) {
100 removeChild(m_verticalScrollbar.get());
101 m_verticalScrollbar = 0;
102 }
103 }
104
105 #if !PLATFORM(GTK)
createScrollbar(ScrollbarOrientation orientation)106 PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
107 {
108 return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
109 }
110
setScrollbarModes(ScrollbarMode horizontalMode,ScrollbarMode verticalMode)111 void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode)
112 {
113 if (horizontalMode == horizontalScrollbarMode() && verticalMode == verticalScrollbarMode())
114 return;
115 m_horizontalScrollbarMode = horizontalMode;
116 m_verticalScrollbarMode = verticalMode;
117 if (platformWidget())
118 platformSetScrollbarModes();
119 else
120 updateScrollbars(scrollOffset());
121 }
122 #endif
123
scrollbarModes(ScrollbarMode & horizontalMode,ScrollbarMode & verticalMode) const124 void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
125 {
126 if (platformWidget()) {
127 platformScrollbarModes(horizontalMode, verticalMode);
128 return;
129 }
130 horizontalMode = m_horizontalScrollbarMode;
131 verticalMode = m_verticalScrollbarMode;
132 }
133
setCanHaveScrollbars(bool canScroll)134 void ScrollView::setCanHaveScrollbars(bool canScroll)
135 {
136 ScrollbarMode newHorizontalMode;
137 ScrollbarMode newVerticalMode;
138
139 scrollbarModes(newHorizontalMode, newVerticalMode);
140
141 if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
142 newVerticalMode = ScrollbarAuto;
143 else if (!canScroll)
144 newVerticalMode = ScrollbarAlwaysOff;
145
146 if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
147 newHorizontalMode = ScrollbarAuto;
148 else if (!canScroll)
149 newHorizontalMode = ScrollbarAlwaysOff;
150
151 setScrollbarModes(newHorizontalMode, newVerticalMode);
152 }
153
setCanBlitOnScroll(bool b)154 void ScrollView::setCanBlitOnScroll(bool b)
155 {
156 if (platformWidget()) {
157 platformSetCanBlitOnScroll(b);
158 return;
159 }
160
161 m_canBlitOnScroll = b;
162 }
163
canBlitOnScroll() const164 bool ScrollView::canBlitOnScroll() const
165 {
166 if (platformWidget())
167 return platformCanBlitOnScroll();
168
169 return m_canBlitOnScroll;
170 }
171
setPaintsEntireContents(bool paintsEntireContents)172 void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
173 {
174 m_paintsEntireContents = paintsEntireContents;
175 }
176
177 #if !PLATFORM(GTK)
visibleContentRect(bool includeScrollbars) const178 IntRect ScrollView::visibleContentRect(bool includeScrollbars) const
179 {
180 if (platformWidget())
181 return platformVisibleContentRect(includeScrollbars);
182 return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()),
183 IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)),
184 max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0))));
185 }
186 #endif
187
layoutWidth() const188 int ScrollView::layoutWidth() const
189 {
190 return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width();
191 }
192
layoutHeight() const193 int ScrollView::layoutHeight() const
194 {
195 return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height();
196 }
197
fixedLayoutSize() const198 IntSize ScrollView::fixedLayoutSize() const
199 {
200 return m_fixedLayoutSize;
201 }
202
setFixedLayoutSize(const IntSize & newSize)203 void ScrollView::setFixedLayoutSize(const IntSize& newSize)
204 {
205 if (fixedLayoutSize() == newSize)
206 return;
207 m_fixedLayoutSize = newSize;
208 updateScrollbars(scrollOffset());
209 }
210
useFixedLayout() const211 bool ScrollView::useFixedLayout() const
212 {
213 return m_useFixedLayout;
214 }
215
setUseFixedLayout(bool enable)216 void ScrollView::setUseFixedLayout(bool enable)
217 {
218 if (useFixedLayout() == enable)
219 return;
220 m_useFixedLayout = enable;
221 updateScrollbars(scrollOffset());
222 }
223
contentsSize() const224 IntSize ScrollView::contentsSize() const
225 {
226 if (platformWidget())
227 return platformContentsSize();
228 return m_contentsSize;
229 }
230
setContentsSize(const IntSize & newSize)231 void ScrollView::setContentsSize(const IntSize& newSize)
232 {
233 if (contentsSize() == newSize)
234 return;
235 m_contentsSize = newSize;
236 if (platformWidget())
237 platformSetContentsSize();
238 else
239 updateScrollbars(scrollOffset());
240 }
241
maximumScrollPosition() const242 IntPoint ScrollView::maximumScrollPosition() const
243 {
244 IntSize maximumOffset = contentsSize() - visibleContentRect().size();
245 maximumOffset.clampNegativeToZero();
246 return IntPoint(maximumOffset.width(), maximumOffset.height());
247 }
248
valueChanged(Scrollbar * scrollbar)249 void ScrollView::valueChanged(Scrollbar* scrollbar)
250 {
251 // Figure out if we really moved.
252 IntSize newOffset = m_scrollOffset;
253 if (scrollbar) {
254 if (scrollbar->orientation() == HorizontalScrollbar)
255 newOffset.setWidth(scrollbar->value());
256 else if (scrollbar->orientation() == VerticalScrollbar)
257 newOffset.setHeight(scrollbar->value());
258 }
259
260 IntSize scrollDelta = newOffset - m_scrollOffset;
261 if (scrollDelta == IntSize())
262 return;
263 m_scrollOffset = newOffset;
264
265 if (scrollbarsSuppressed())
266 return;
267
268 scrollContents(scrollDelta);
269 }
270
scrollRectIntoViewRecursively(const IntRect & r)271 void ScrollView::scrollRectIntoViewRecursively(const IntRect& r)
272 {
273 #if PLATFORM(ANDROID)
274 if (platformProhibitsScrolling())
275 return;
276 #endif
277 // FIXME: This method is not transform-aware. It should just be moved to FrameView so that an accurate
278 // position for the child view can be determined.
279 IntRect rect = r;
280 ScrollView* view = this;
281 while (view) {
282 if (view->prohibitsScrolling()) // Allow the views to scroll into view recursively until we hit one that prohibits scrolling.
283 return;
284 view->setScrollPosition(rect.location());
285 rect.move(view->x() - view->scrollOffset().width(), view->y() - view->scrollOffset().height());
286 if (view->parent())
287 rect.intersect(view->frameRect());
288 view = view->parent();
289 }
290
291 // We may be embedded inside some containing platform scroll view that we don't manage. This is the case
292 // in Mail.app on OS X, for example, where the WebKit view for message bodies is inside a Cocoa NSScrollView
293 // that contains both it and message headers. Let the HostWindow know about this scroll so that it can pass the message
294 // on up the view chain.
295 // This rect is not clamped, since Mail actually relies on receiving an unclamped rect with negative coordinates in order to
296 // expose the headers.
297 if (hostWindow())
298 hostWindow()->scrollRectIntoView(rect, this);
299 }
300
setScrollPosition(const IntPoint & scrollPoint)301 void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
302 {
303 if (prohibitsScrolling())
304 return;
305
306 if (platformWidget()) {
307 platformSetScrollPosition(scrollPoint);
308 return;
309 }
310
311 IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
312 newScrollPosition.clampNegativeToZero();
313
314 if (newScrollPosition == scrollPosition())
315 return;
316
317 updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
318 }
319
scroll(ScrollDirection direction,ScrollGranularity granularity)320 bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
321 {
322 if (platformWidget())
323 return platformScroll(direction, granularity);
324
325 if (direction == ScrollUp || direction == ScrollDown) {
326 if (m_verticalScrollbar)
327 return m_verticalScrollbar->scroll(direction, granularity);
328 } else {
329 if (m_horizontalScrollbar)
330 return m_horizontalScrollbar->scroll(direction, granularity);
331 }
332 return false;
333 }
334
335 static const unsigned cMaxUpdateScrollbarsPass = 2;
336
updateScrollbars(const IntSize & desiredOffset)337 void ScrollView::updateScrollbars(const IntSize& desiredOffset)
338 {
339 if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget())
340 return;
341
342 // If we came in here with the view already needing a layout, then go ahead and do that
343 // first. (This will be the common case, e.g., when the page changes due to window resizing for example).
344 // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
345 if (!m_scrollbarsSuppressed) {
346 m_inUpdateScrollbars = true;
347 visibleContentsResized();
348 m_inUpdateScrollbars = false;
349 }
350
351 bool hasHorizontalScrollbar = m_horizontalScrollbar;
352 bool hasVerticalScrollbar = m_verticalScrollbar;
353
354 bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
355 bool newHasVerticalScrollbar = hasVerticalScrollbar;
356
357 ScrollbarMode hScroll = m_horizontalScrollbarMode;
358 ScrollbarMode vScroll = m_verticalScrollbarMode;
359
360 if (hScroll != ScrollbarAuto)
361 newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
362 if (vScroll != ScrollbarAuto)
363 newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
364
365 if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
366 if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
367 setHasHorizontalScrollbar(newHasHorizontalScrollbar);
368 if (hasVerticalScrollbar != newHasVerticalScrollbar)
369 setHasVerticalScrollbar(newHasVerticalScrollbar);
370 } else {
371 bool sendContentResizedNotification = false;
372
373 IntSize docSize = contentsSize();
374 IntSize frameSize = frameRect().size();
375
376 if (hScroll == ScrollbarAuto) {
377 newHasHorizontalScrollbar = docSize.width() > visibleWidth();
378 if (newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
379 newHasHorizontalScrollbar = false;
380 }
381 if (vScroll == ScrollbarAuto) {
382 newHasVerticalScrollbar = docSize.height() > visibleHeight();
383 if (newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
384 newHasVerticalScrollbar = false;
385 }
386
387 // If we ever turn one scrollbar off, always turn the other one off too. Never ever
388 // try to both gain/lose a scrollbar in the same pass.
389 if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
390 newHasVerticalScrollbar = false;
391 if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
392 newHasHorizontalScrollbar = false;
393
394 if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
395 setHasHorizontalScrollbar(newHasHorizontalScrollbar);
396 sendContentResizedNotification = true;
397 }
398
399 if (hasVerticalScrollbar != newHasVerticalScrollbar) {
400 setHasVerticalScrollbar(newHasVerticalScrollbar);
401 sendContentResizedNotification = true;
402 }
403
404 if (sendContentResizedNotification && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
405 m_updateScrollbarsPass++;
406 contentsResized();
407 visibleContentsResized();
408 IntSize newDocSize = contentsSize();
409 if (newDocSize == docSize) {
410 // The layout with the new scroll state had no impact on
411 // the document's overall size, so updateScrollbars didn't get called.
412 // Recur manually.
413 updateScrollbars(desiredOffset);
414 }
415 m_updateScrollbarsPass--;
416 }
417 }
418
419 // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid
420 // doing it multiple times).
421 if (m_updateScrollbarsPass)
422 return;
423
424 m_inUpdateScrollbars = true;
425 IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight());
426 IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition);
427 scroll.clampNegativeToZero();
428
429 if (m_horizontalScrollbar) {
430 int clientWidth = visibleWidth();
431 m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
432 int pageStep = max(max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1);
433 IntRect oldRect(m_horizontalScrollbar->frameRect());
434 IntRect hBarRect = IntRect(0,
435 height() - m_horizontalScrollbar->height(),
436 width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
437 m_horizontalScrollbar->height());
438 m_horizontalScrollbar->setFrameRect(hBarRect);
439 if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
440 m_horizontalScrollbar->invalidate();
441
442 if (m_scrollbarsSuppressed)
443 m_horizontalScrollbar->setSuppressInvalidation(true);
444 m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
445 m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
446 m_horizontalScrollbar->setValue(scroll.width());
447 if (m_scrollbarsSuppressed)
448 m_horizontalScrollbar->setSuppressInvalidation(false);
449 }
450
451 if (m_verticalScrollbar) {
452 int clientHeight = visibleHeight();
453 m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
454 int pageStep = max(max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1);
455 if (pageStep < 0)
456 pageStep = clientHeight;
457 IntRect oldRect(m_verticalScrollbar->frameRect());
458 IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(),
459 0,
460 m_verticalScrollbar->width(),
461 height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
462 m_verticalScrollbar->setFrameRect(vBarRect);
463 if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
464 m_verticalScrollbar->invalidate();
465
466 if (m_scrollbarsSuppressed)
467 m_verticalScrollbar->setSuppressInvalidation(true);
468 m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
469 m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
470 m_verticalScrollbar->setValue(scroll.height());
471 if (m_scrollbarsSuppressed)
472 m_verticalScrollbar->setSuppressInvalidation(false);
473 }
474
475 if (hasHorizontalScrollbar != (m_horizontalScrollbar != 0) || hasVerticalScrollbar != (m_verticalScrollbar != 0)) {
476 frameRectsChanged();
477 updateScrollCorner();
478 }
479
480 // See if our offset has changed in a situation where we might not have scrollbars.
481 // This can happen when editing a body with overflow:hidden and scrolling to reveal selection.
482 // It can also happen when maximizing a window that has scrollbars (but the new maximized result
483 // does not).
484 IntSize scrollDelta = scroll - m_scrollOffset;
485 if (scrollDelta != IntSize()) {
486 m_scrollOffset = scroll;
487 scrollContents(scrollDelta);
488 }
489
490 m_inUpdateScrollbars = false;
491 }
492
493 const int panIconSizeLength = 16;
494
scrollContents(const IntSize & scrollDelta)495 void ScrollView::scrollContents(const IntSize& scrollDelta)
496 {
497 if (!hostWindow())
498 return;
499
500 // Since scrolling is double buffered, we will be blitting the scroll view's intersection
501 // with the clip rect every time to keep it smooth.
502 IntRect clipRect = windowClipRect();
503 IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight()));
504 IntRect updateRect = clipRect;
505 updateRect.intersect(scrollViewRect);
506
507 // Invalidate the window (not the backing store).
508 hostWindow()->repaint(updateRect, false);
509
510 if (m_drawPanScrollIcon) {
511 int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
512 IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
513 IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
514 panScrollIconDirtyRect.intersect(clipRect);
515 hostWindow()->repaint(panScrollIconDirtyRect, true);
516 }
517
518 if (canBlitOnScroll()) { // The main frame can just blit the WebView window
519 // FIXME: Find a way to blit subframes without blitting overlapping content
520 hostWindow()->scroll(-scrollDelta, scrollViewRect, clipRect);
521 } else {
522 // We need to go ahead and repaint the entire backing store. Do it now before moving the
523 // windowed plugins.
524 hostWindow()->repaint(updateRect, true, false, true); // Invalidate the backing store and repaint it synchronously
525 }
526
527 // This call will move children with native widgets (plugins) and invalidate them as well.
528 frameRectsChanged();
529
530 // Now update the window (which should do nothing but a blit of the backing store's updateRect and so should
531 // be very fast).
532 hostWindow()->paint();
533 }
534
windowToContents(const IntPoint & windowPoint) const535 IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
536 {
537 IntPoint viewPoint = convertFromContainingWindow(windowPoint);
538 return viewPoint + scrollOffset();
539 }
540
contentsToWindow(const IntPoint & contentsPoint) const541 IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
542 {
543 IntPoint viewPoint = contentsPoint - scrollOffset();
544 return convertToContainingWindow(viewPoint);
545 }
546
windowToContents(const IntRect & windowRect) const547 IntRect ScrollView::windowToContents(const IntRect& windowRect) const
548 {
549 IntRect viewRect = convertFromContainingWindow(windowRect);
550 viewRect.move(scrollOffset());
551 return viewRect;
552 }
553
contentsToWindow(const IntRect & contentsRect) const554 IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
555 {
556 IntRect viewRect = contentsRect;
557 viewRect.move(-scrollOffset());
558 return convertToContainingWindow(viewRect);
559 }
560
contentsToScreen(const IntRect & rect) const561 IntRect ScrollView::contentsToScreen(const IntRect& rect) const
562 {
563 if (platformWidget())
564 return platformContentsToScreen(rect);
565 if (!hostWindow())
566 return IntRect();
567 return hostWindow()->windowToScreen(contentsToWindow(rect));
568 }
569
screenToContents(const IntPoint & point) const570 IntPoint ScrollView::screenToContents(const IntPoint& point) const
571 {
572 if (platformWidget())
573 return platformScreenToContents(point);
574 if (!hostWindow())
575 return IntPoint();
576 return windowToContents(hostWindow()->screenToWindow(point));
577 }
578
containsScrollbarsAvoidingResizer() const579 bool ScrollView::containsScrollbarsAvoidingResizer() const
580 {
581 return !m_scrollbarsAvoidingResizer;
582 }
583
adjustScrollbarsAvoidingResizerCount(int overlapDelta)584 void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
585 {
586 int oldCount = m_scrollbarsAvoidingResizer;
587 m_scrollbarsAvoidingResizer += overlapDelta;
588 if (parent())
589 parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
590 else if (!scrollbarsSuppressed()) {
591 // If we went from n to 0 or from 0 to n and we're the outermost view,
592 // we need to invalidate the windowResizerRect(), since it will now need to paint
593 // differently.
594 if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
595 (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
596 invalidateRect(windowResizerRect());
597 }
598 }
599
setParent(ScrollView * parentView)600 void ScrollView::setParent(ScrollView* parentView)
601 {
602 if (parentView == parent())
603 return;
604
605 if (m_scrollbarsAvoidingResizer && parent())
606 parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
607
608 Widget::setParent(parentView);
609
610 if (m_scrollbarsAvoidingResizer && parent())
611 parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
612 }
613
setScrollbarsSuppressed(bool suppressed,bool repaintOnUnsuppress)614 void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
615 {
616 if (suppressed == m_scrollbarsSuppressed)
617 return;
618
619 m_scrollbarsSuppressed = suppressed;
620
621 if (platformWidget())
622 platformSetScrollbarsSuppressed(repaintOnUnsuppress);
623 else if (repaintOnUnsuppress && !suppressed) {
624 if (m_horizontalScrollbar)
625 m_horizontalScrollbar->invalidate();
626 if (m_verticalScrollbar)
627 m_verticalScrollbar->invalidate();
628
629 // Invalidate the scroll corner too on unsuppress.
630 invalidateRect(scrollCornerRect());
631 }
632 }
633
scrollbarAtPoint(const IntPoint & windowPoint)634 Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
635 {
636 if (platformWidget())
637 return 0;
638
639 IntPoint viewPoint = convertFromContainingWindow(windowPoint);
640 if (m_horizontalScrollbar && m_horizontalScrollbar->frameRect().contains(viewPoint))
641 return m_horizontalScrollbar.get();
642 if (m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(viewPoint))
643 return m_verticalScrollbar.get();
644 return 0;
645 }
646
wheelEvent(PlatformWheelEvent & e)647 void ScrollView::wheelEvent(PlatformWheelEvent& e)
648 {
649 // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
650 #if PLATFORM(WX)
651 if (!canHaveScrollbars()) {
652 #else
653 if (!canHaveScrollbars() || platformWidget()) {
654 #endif
655 return;
656 }
657
658 // Determine how much we want to scroll. If we can move at all, we will accept the event.
659 IntSize maxScrollDelta = maximumScrollPosition() - scrollPosition();
660 if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) ||
661 (e.deltaX() > 0 && scrollOffset().width() > 0) ||
662 (e.deltaY() < 0 && maxScrollDelta.height() > 0) ||
663 (e.deltaY() > 0 && scrollOffset().height() > 0)) {
664 e.accept();
665 float deltaX = e.deltaX();
666 float deltaY = e.deltaY();
667 if (e.granularity() == ScrollByPageWheelEvent) {
668 ASSERT(deltaX == 0);
669 bool negative = deltaY < 0;
670 deltaY = max(max<int>(visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), visibleHeight() - Scrollbar::maxOverlapBetweenPages()), 1);
671 if (negative)
672 deltaY = -deltaY;
673 }
674 scrollBy(IntSize(-deltaX, -deltaY));
675 }
676 }
677
678 void ScrollView::setFrameRect(const IntRect& newRect)
679 {
680 IntRect oldRect = frameRect();
681
682 if (newRect == oldRect)
683 return;
684
685 Widget::setFrameRect(newRect);
686
687 if (platformWidget())
688 return;
689
690 if (newRect.width() != oldRect.width() || newRect.height() != oldRect.height()) {
691 updateScrollbars(m_scrollOffset);
692 contentsResized();
693 }
694
695 frameRectsChanged();
696 }
697
698 void ScrollView::frameRectsChanged()
699 {
700 if (platformWidget())
701 return;
702
703 HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
704 for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
705 (*current)->frameRectsChanged();
706 }
707
708 void ScrollView::repaintContentRectangle(const IntRect& rect, bool now)
709 {
710 IntRect paintRect = rect;
711 if (!paintsEntireContents())
712 paintRect.intersect(visibleContentRect());
713 #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS
714 if (rect != paintRect)
715 platformOffscreenContentRectangle(visibleContentRect(), rect);
716 #endif
717 if (paintRect.isEmpty())
718 return;
719 if (platformWidget()) {
720 platformRepaintContentRectangle(paintRect, now);
721 return;
722 }
723
724 if (hostWindow())
725 hostWindow()->repaint(contentsToWindow(paintRect), true, now);
726 }
727
728 IntRect ScrollView::scrollCornerRect() const
729 {
730 IntRect cornerRect;
731
732 if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
733 cornerRect.unite(IntRect(m_horizontalScrollbar->width(),
734 height() - m_horizontalScrollbar->height(),
735 width() - m_horizontalScrollbar->width(),
736 m_horizontalScrollbar->height()));
737 }
738
739 if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
740 cornerRect.unite(IntRect(width() - m_verticalScrollbar->width(),
741 m_verticalScrollbar->height(),
742 m_verticalScrollbar->width(),
743 height() - m_verticalScrollbar->height()));
744 }
745
746 return cornerRect;
747 }
748
749 void ScrollView::updateScrollCorner()
750 {
751 }
752
753 void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
754 {
755 ScrollbarTheme::nativeTheme()->paintScrollCorner(this, context, cornerRect);
756 }
757
758 void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
759 {
760 if (m_horizontalScrollbar)
761 m_horizontalScrollbar->paint(context, rect);
762 if (m_verticalScrollbar)
763 m_verticalScrollbar->paint(context, rect);
764
765 paintScrollCorner(context, scrollCornerRect());
766 }
767
768 void ScrollView::paintPanScrollIcon(GraphicsContext* context)
769 {
770 DEFINE_STATIC_LOCAL(Image*, panScrollIcon, (Image::loadPlatformResource("panIcon").releaseRef()));
771 context->drawImage(panScrollIcon, DeviceColorSpace, m_panScrollIconPoint);
772 }
773
774 void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
775 {
776 if (platformWidget()) {
777 Widget::paint(context, rect);
778 return;
779 }
780
781 if (context->paintingDisabled() && !context->updatingControlTints())
782 return;
783
784 IntRect documentDirtyRect = rect;
785 documentDirtyRect.intersect(frameRect());
786
787 context->save();
788
789 context->translate(x(), y());
790 documentDirtyRect.move(-x(), -y());
791
792 context->translate(-scrollX(), -scrollY());
793 documentDirtyRect.move(scrollX(), scrollY());
794
795 context->clip(visibleContentRect());
796
797 paintContents(context, documentDirtyRect);
798
799 context->restore();
800
801 // Now paint the scrollbars.
802 if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
803 context->save();
804 IntRect scrollViewDirtyRect = rect;
805 scrollViewDirtyRect.intersect(frameRect());
806 context->translate(x(), y());
807 scrollViewDirtyRect.move(-x(), -y());
808
809 paintScrollbars(context, scrollViewDirtyRect);
810
811 context->restore();
812 }
813
814 // Paint the panScroll Icon
815 if (m_drawPanScrollIcon)
816 paintPanScrollIcon(context);
817 }
818
819 bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
820 {
821 if (!scrollbarCornerPresent())
822 return false;
823
824 IntPoint viewPoint = convertFromContainingWindow(windowPoint);
825
826 if (m_horizontalScrollbar) {
827 int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
828 int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
829 int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
830
831 return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
832 }
833
834 int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
835 int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
836 int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
837
838 return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
839 }
840
841 bool ScrollView::scrollbarCornerPresent() const
842 {
843 return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) ||
844 (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
845 }
846
847 IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
848 {
849 // Scrollbars won't be transformed within us
850 IntRect newRect = localRect;
851 newRect.move(scrollbar->x(), scrollbar->y());
852 return newRect;
853 }
854
855 IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
856 {
857 IntRect newRect = parentRect;
858 // Scrollbars won't be transformed within us
859 newRect.move(-scrollbar->x(), -scrollbar->y());
860 return newRect;
861 }
862
863 // FIXME: test these on windows
864 IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
865 {
866 // Scrollbars won't be transformed within us
867 IntPoint newPoint = localPoint;
868 newPoint.move(scrollbar->x(), scrollbar->y());
869 return newPoint;
870 }
871
872 IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
873 {
874 IntPoint newPoint = parentPoint;
875 // Scrollbars won't be transformed within us
876 newPoint.move(-scrollbar->x(), -scrollbar->y());
877 return newPoint;
878 }
879
880 void ScrollView::setParentVisible(bool visible)
881 {
882 if (isParentVisible() == visible)
883 return;
884
885 Widget::setParentVisible(visible);
886
887 if (!isSelfVisible())
888 return;
889
890 HashSet<RefPtr<Widget> >::iterator end = m_children.end();
891 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
892 (*it)->setParentVisible(visible);
893 }
894
895 void ScrollView::show()
896 {
897 if (!isSelfVisible()) {
898 setSelfVisible(true);
899 if (isParentVisible()) {
900 HashSet<RefPtr<Widget> >::iterator end = m_children.end();
901 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
902 (*it)->setParentVisible(true);
903 }
904 }
905
906 Widget::show();
907 }
908
909 void ScrollView::hide()
910 {
911 if (isSelfVisible()) {
912 if (isParentVisible()) {
913 HashSet<RefPtr<Widget> >::iterator end = m_children.end();
914 for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
915 (*it)->setParentVisible(false);
916 }
917 setSelfVisible(false);
918 }
919
920 Widget::hide();
921 }
922
923 bool ScrollView::isOffscreen() const
924 {
925 if (platformWidget())
926 return platformIsOffscreen();
927
928 if (!isVisible())
929 return true;
930
931 // FIXME: Add a HostWindow::isOffscreen method here. Since only Mac implements this method
932 // currently, we can add the method when the other platforms decide to implement this concept.
933 return false;
934 }
935
936
937 void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
938 {
939 if (!hostWindow())
940 return;
941 m_drawPanScrollIcon = true;
942 m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
943 hostWindow()->repaint(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength,panIconSizeLength)), true, true);
944 }
945
946 void ScrollView::removePanScrollIcon()
947 {
948 if (!hostWindow())
949 return;
950 m_drawPanScrollIcon = false;
951 hostWindow()->repaint(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true, true);
952 }
953
954 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT)
955
956 void ScrollView::platformInit()
957 {
958 }
959
960 void ScrollView::platformDestroy()
961 {
962 }
963
964 #endif
965
966 #if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(MAC)
967
968 void ScrollView::platformAddChild(Widget*)
969 {
970 }
971
972 void ScrollView::platformRemoveChild(Widget*)
973 {
974 }
975
976 #endif
977
978 #if !PLATFORM(MAC)
979
980 void ScrollView::platformSetScrollbarsSuppressed(bool)
981 {
982 }
983
984 #endif
985
986 #if !PLATFORM(MAC) && !PLATFORM(WX)
987
988 #if !PLATFORM(ANDROID)
989 void ScrollView::platformSetScrollbarModes()
990 {
991 }
992
993 void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
994 {
995 horizontal = ScrollbarAuto;
996 vertical = ScrollbarAuto;
997 }
998 #endif
999
1000 void ScrollView::platformSetCanBlitOnScroll(bool)
1001 {
1002 }
1003
1004 bool ScrollView::platformCanBlitOnScroll() const
1005 {
1006 return false;
1007 }
1008
1009 #if !PLATFORM(ANDROID)
1010 IntRect ScrollView::platformVisibleContentRect(bool) const
1011 {
1012 return IntRect();
1013 }
1014 #endif
1015
1016 #if !PLATFORM(ANDROID)
1017 IntSize ScrollView::platformContentsSize() const
1018 {
1019 return IntSize();
1020 }
1021 #endif
1022
1023 void ScrollView::platformSetContentsSize()
1024 {
1025 }
1026
1027 IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
1028 {
1029 return rect;
1030 }
1031
1032 IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
1033 {
1034 return point;
1035 }
1036
1037 #if !PLATFORM(ANDROID)
1038 void ScrollView::platformSetScrollPosition(const IntPoint&)
1039 {
1040 }
1041 #endif
1042
1043 bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
1044 {
1045 return true;
1046 }
1047
1048 #if !PLATFORM(ANDROID)
1049 void ScrollView::platformRepaintContentRectangle(const IntRect&, bool /*now*/)
1050 {
1051 }
1052
1053 #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS
1054 void ScrollView::platformOffscreenContentRectangle(const IntRect& )
1055 {
1056 }
1057 #endif
1058 #endif
1059
1060 bool ScrollView::platformIsOffscreen() const
1061 {
1062 return false;
1063 }
1064
1065 #endif
1066
1067 }
1068
1069