--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/platform/ScrollView.cpp Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,1052 @@
+/*
+ * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ScrollView.h"
+
+#include "GraphicsContext.h"
+#include "HostWindow.h"
+#include "PlatformMouseEvent.h"
+#include "PlatformWheelEvent.h"
+#include "Scrollbar.h"
+#include "ScrollbarTheme.h"
+#include <wtf/StdLibExtras.h>
+
+
+using std::max;
+
+namespace WebCore {
+
+ScrollView::ScrollView()
+ : m_horizontalScrollbarMode(ScrollbarAuto)
+ , m_verticalScrollbarMode(ScrollbarAuto)
+ , m_horizontalScrollbarLock(false)
+ , m_verticalScrollbarLock(false)
+ , m_prohibitsScrolling(false)
+ , m_canBlitOnScroll(true)
+ , m_scrollbarsAvoidingResizer(0)
+ , m_scrollbarsSuppressed(false)
+ , m_inUpdateScrollbars(false)
+ , m_updateScrollbarsPass(0)
+ , m_drawPanScrollIcon(false)
+ , m_useFixedLayout(false)
+ , m_paintsEntireContents(false)
+{
+ platformInit();
+}
+
+ScrollView::~ScrollView()
+{
+ platformDestroy();
+}
+
+void ScrollView::addChild(PassRefPtr<Widget> prpChild)
+{
+ Widget* child = prpChild.get();
+ ASSERT(child != this && !child->parent());
+ child->setParent(this);
+ m_children.add(prpChild);
+ if (child->platformWidget())
+ platformAddChild(child);
+}
+
+void ScrollView::removeChild(Widget* child)
+{
+ ASSERT(child->parent() == this);
+ child->setParent(0);
+ m_children.remove(child);
+ if (child->platformWidget())
+ platformRemoveChild(child);
+}
+
+void ScrollView::setHasHorizontalScrollbar(bool hasBar)
+{
+ if (hasBar && avoidScrollbarCreation())
+ return;
+
+ if (hasBar && !m_horizontalScrollbar) {
+ m_horizontalScrollbar = createScrollbar(HorizontalScrollbar);
+ addChild(m_horizontalScrollbar.get());
+ m_horizontalScrollbar->styleChanged();
+ } else if (!hasBar && m_horizontalScrollbar) {
+ removeChild(m_horizontalScrollbar.get());
+ m_horizontalScrollbar = 0;
+ }
+}
+
+void ScrollView::setHasVerticalScrollbar(bool hasBar)
+{
+ if (hasBar && avoidScrollbarCreation())
+ return;
+
+ if (hasBar && !m_verticalScrollbar) {
+ m_verticalScrollbar = createScrollbar(VerticalScrollbar);
+ addChild(m_verticalScrollbar.get());
+ m_verticalScrollbar->styleChanged();
+ } else if (!hasBar && m_verticalScrollbar) {
+ removeChild(m_verticalScrollbar.get());
+ m_verticalScrollbar = 0;
+ }
+}
+
+#if !PLATFORM(GTK)
+PassRefPtr<Scrollbar> ScrollView::createScrollbar(ScrollbarOrientation orientation)
+{
+ return Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar);
+}
+
+void ScrollView::setScrollbarModes(ScrollbarMode horizontalMode, ScrollbarMode verticalMode,
+ bool horizontalLock, bool verticalLock)
+{
+ bool needsUpdate = false;
+
+ if (horizontalMode != horizontalScrollbarMode() && !m_horizontalScrollbarLock) {
+ m_horizontalScrollbarMode = horizontalMode;
+ needsUpdate = true;
+ }
+
+ if (verticalMode != verticalScrollbarMode() && !m_verticalScrollbarLock) {
+ m_verticalScrollbarMode = verticalMode;
+ needsUpdate = true;
+ }
+
+ if (horizontalLock)
+ setHorizontalScrollbarLock();
+
+ if (verticalLock)
+ setVerticalScrollbarLock();
+
+ if (!needsUpdate)
+ return;
+
+ if (platformWidget())
+ platformSetScrollbarModes();
+ else
+ updateScrollbars(scrollOffset());
+}
+#endif
+
+void ScrollView::scrollbarModes(ScrollbarMode& horizontalMode, ScrollbarMode& verticalMode) const
+{
+ if (platformWidget()) {
+ platformScrollbarModes(horizontalMode, verticalMode);
+ return;
+ }
+ horizontalMode = m_horizontalScrollbarMode;
+ verticalMode = m_verticalScrollbarMode;
+}
+
+void ScrollView::setCanHaveScrollbars(bool canScroll)
+{
+ ScrollbarMode newHorizontalMode;
+ ScrollbarMode newVerticalMode;
+
+ scrollbarModes(newHorizontalMode, newVerticalMode);
+
+ if (canScroll && newVerticalMode == ScrollbarAlwaysOff)
+ newVerticalMode = ScrollbarAuto;
+ else if (!canScroll)
+ newVerticalMode = ScrollbarAlwaysOff;
+
+ if (canScroll && newHorizontalMode == ScrollbarAlwaysOff)
+ newHorizontalMode = ScrollbarAuto;
+ else if (!canScroll)
+ newHorizontalMode = ScrollbarAlwaysOff;
+
+ setScrollbarModes(newHorizontalMode, newVerticalMode);
+}
+
+void ScrollView::setCanBlitOnScroll(bool b)
+{
+ if (platformWidget()) {
+ platformSetCanBlitOnScroll(b);
+ return;
+ }
+
+ m_canBlitOnScroll = b;
+}
+
+bool ScrollView::canBlitOnScroll() const
+{
+ if (platformWidget())
+ return platformCanBlitOnScroll();
+
+ return m_canBlitOnScroll;
+}
+
+void ScrollView::setPaintsEntireContents(bool paintsEntireContents)
+{
+ m_paintsEntireContents = paintsEntireContents;
+}
+
+#if !PLATFORM(GTK)
+IntRect ScrollView::visibleContentRect(bool includeScrollbars) const
+{
+ if (platformWidget())
+ return platformVisibleContentRect(includeScrollbars);
+ return IntRect(IntPoint(m_scrollOffset.width(), m_scrollOffset.height()),
+ IntSize(max(0, width() - (verticalScrollbar() && !includeScrollbars ? verticalScrollbar()->width() : 0)),
+ max(0, height() - (horizontalScrollbar() && !includeScrollbars ? horizontalScrollbar()->height() : 0))));
+}
+#endif
+
+int ScrollView::layoutWidth() const
+{
+ return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleWidth() : m_fixedLayoutSize.width();
+}
+
+int ScrollView::layoutHeight() const
+{
+ return m_fixedLayoutSize.isEmpty() || !m_useFixedLayout ? visibleHeight() : m_fixedLayoutSize.height();
+}
+
+IntSize ScrollView::fixedLayoutSize() const
+{
+ return m_fixedLayoutSize;
+}
+
+void ScrollView::setFixedLayoutSize(const IntSize& newSize)
+{
+ if (fixedLayoutSize() == newSize)
+ return;
+ m_fixedLayoutSize = newSize;
+ updateScrollbars(scrollOffset());
+}
+
+bool ScrollView::useFixedLayout() const
+{
+ return m_useFixedLayout;
+}
+
+void ScrollView::setUseFixedLayout(bool enable)
+{
+ if (useFixedLayout() == enable)
+ return;
+ m_useFixedLayout = enable;
+ updateScrollbars(scrollOffset());
+}
+
+IntSize ScrollView::contentsSize() const
+{
+ if (platformWidget())
+ return platformContentsSize();
+ return m_contentsSize;
+}
+
+void ScrollView::setContentsSize(const IntSize& newSize)
+{
+ if (contentsSize() == newSize)
+ return;
+ m_contentsSize = newSize;
+ if (platformWidget())
+ platformSetContentsSize();
+ else
+ updateScrollbars(scrollOffset());
+}
+
+IntPoint ScrollView::maximumScrollPosition() const
+{
+ IntSize maximumOffset = contentsSize() - visibleContentRect().size();
+ maximumOffset.clampNegativeToZero();
+ return IntPoint(maximumOffset.width(), maximumOffset.height());
+}
+
+void ScrollView::valueChanged(Scrollbar* scrollbar)
+{
+ // Figure out if we really moved.
+ IntSize newOffset = m_scrollOffset;
+ if (scrollbar) {
+ if (scrollbar->orientation() == HorizontalScrollbar)
+ newOffset.setWidth(scrollbar->value());
+ else if (scrollbar->orientation() == VerticalScrollbar)
+ newOffset.setHeight(scrollbar->value());
+ }
+
+ IntSize scrollDelta = newOffset - m_scrollOffset;
+ if (scrollDelta == IntSize())
+ return;
+ m_scrollOffset = newOffset;
+
+ if (scrollbarsSuppressed())
+ return;
+
+ repaintFixedElementsAfterScrolling();
+ scrollContents(scrollDelta);
+}
+
+void ScrollView::setScrollPosition(const IntPoint& scrollPoint)
+{
+ if (prohibitsScrolling())
+ return;
+
+ if (platformWidget()) {
+ platformSetScrollPosition(scrollPoint);
+ return;
+ }
+
+ IntPoint newScrollPosition = scrollPoint.shrunkTo(maximumScrollPosition());
+ newScrollPosition.clampNegativeToZero();
+
+ if (newScrollPosition == scrollPosition())
+ return;
+
+ updateScrollbars(IntSize(newScrollPosition.x(), newScrollPosition.y()));
+}
+
+bool ScrollView::scroll(ScrollDirection direction, ScrollGranularity granularity)
+{
+ if (platformWidget())
+ return platformScroll(direction, granularity);
+
+ if (direction == ScrollUp || direction == ScrollDown) {
+ if (m_verticalScrollbar)
+ return m_verticalScrollbar->scroll(direction, granularity);
+ } else {
+ if (m_horizontalScrollbar)
+ return m_horizontalScrollbar->scroll(direction, granularity);
+ }
+ return false;
+}
+
+static const unsigned cMaxUpdateScrollbarsPass = 2;
+
+void ScrollView::updateScrollbars(const IntSize& desiredOffset)
+{
+ if (m_inUpdateScrollbars || prohibitsScrolling() || platformWidget())
+ return;
+
+ // If we came in here with the view already needing a layout, then go ahead and do that
+ // first. (This will be the common case, e.g., when the page changes due to window resizing for example).
+ // This layout will not re-enter updateScrollbars and does not count towards our max layout pass total.
+ if (!m_scrollbarsSuppressed) {
+ m_inUpdateScrollbars = true;
+ visibleContentsResized();
+ m_inUpdateScrollbars = false;
+ }
+
+ bool hasHorizontalScrollbar = m_horizontalScrollbar;
+ bool hasVerticalScrollbar = m_verticalScrollbar;
+
+ bool newHasHorizontalScrollbar = hasHorizontalScrollbar;
+ bool newHasVerticalScrollbar = hasVerticalScrollbar;
+
+ ScrollbarMode hScroll = m_horizontalScrollbarMode;
+ ScrollbarMode vScroll = m_verticalScrollbarMode;
+
+ if (hScroll != ScrollbarAuto)
+ newHasHorizontalScrollbar = (hScroll == ScrollbarAlwaysOn);
+ if (vScroll != ScrollbarAuto)
+ newHasVerticalScrollbar = (vScroll == ScrollbarAlwaysOn);
+
+ if (m_scrollbarsSuppressed || (hScroll != ScrollbarAuto && vScroll != ScrollbarAuto)) {
+ if (hasHorizontalScrollbar != newHasHorizontalScrollbar)
+ setHasHorizontalScrollbar(newHasHorizontalScrollbar);
+ if (hasVerticalScrollbar != newHasVerticalScrollbar)
+ setHasVerticalScrollbar(newHasVerticalScrollbar);
+ } else {
+ bool sendContentResizedNotification = false;
+
+ IntSize docSize = contentsSize();
+ IntSize frameSize = frameRect().size();
+
+ if (hScroll == ScrollbarAuto) {
+ newHasHorizontalScrollbar = docSize.width() > visibleWidth();
+ if (newHasHorizontalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
+ newHasHorizontalScrollbar = false;
+ }
+ if (vScroll == ScrollbarAuto) {
+ newHasVerticalScrollbar = docSize.height() > visibleHeight();
+ if (newHasVerticalScrollbar && !m_updateScrollbarsPass && docSize.width() <= frameSize.width() && docSize.height() <= frameSize.height())
+ newHasVerticalScrollbar = false;
+ }
+
+ // If we ever turn one scrollbar off, always turn the other one off too. Never ever
+ // try to both gain/lose a scrollbar in the same pass.
+ if (!newHasHorizontalScrollbar && hasHorizontalScrollbar && vScroll != ScrollbarAlwaysOn)
+ newHasVerticalScrollbar = false;
+ if (!newHasVerticalScrollbar && hasVerticalScrollbar && hScroll != ScrollbarAlwaysOn)
+ newHasHorizontalScrollbar = false;
+
+ if (hasHorizontalScrollbar != newHasHorizontalScrollbar) {
+ setHasHorizontalScrollbar(newHasHorizontalScrollbar);
+ sendContentResizedNotification = true;
+ }
+
+ if (hasVerticalScrollbar != newHasVerticalScrollbar) {
+ setHasVerticalScrollbar(newHasVerticalScrollbar);
+ sendContentResizedNotification = true;
+ }
+
+ if (sendContentResizedNotification && m_updateScrollbarsPass < cMaxUpdateScrollbarsPass) {
+ m_updateScrollbarsPass++;
+ contentsResized();
+ visibleContentsResized();
+ IntSize newDocSize = contentsSize();
+ if (newDocSize == docSize) {
+ // The layout with the new scroll state had no impact on
+ // the document's overall size, so updateScrollbars didn't get called.
+ // Recur manually.
+ updateScrollbars(desiredOffset);
+ }
+ m_updateScrollbarsPass--;
+ }
+ }
+
+ // Set up the range (and page step/line step), but only do this if we're not in a nested call (to avoid
+ // doing it multiple times).
+ if (m_updateScrollbarsPass)
+ return;
+
+ m_inUpdateScrollbars = true;
+ IntSize maxScrollPosition(contentsWidth() - visibleWidth(), contentsHeight() - visibleHeight());
+ IntSize scroll = desiredOffset.shrunkTo(maxScrollPosition);
+ scroll.clampNegativeToZero();
+
+ if (m_horizontalScrollbar) {
+ int clientWidth = visibleWidth();
+ m_horizontalScrollbar->setEnabled(contentsWidth() > clientWidth);
+ int pageStep = max(max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1);
+ IntRect oldRect(m_horizontalScrollbar->frameRect());
+ IntRect hBarRect = IntRect(0,
+ height() - m_horizontalScrollbar->height(),
+ width() - (m_verticalScrollbar ? m_verticalScrollbar->width() : 0),
+ m_horizontalScrollbar->height());
+ m_horizontalScrollbar->setFrameRect(hBarRect);
+ if (!m_scrollbarsSuppressed && oldRect != m_horizontalScrollbar->frameRect())
+ m_horizontalScrollbar->invalidate();
+
+ if (m_scrollbarsSuppressed)
+ m_horizontalScrollbar->setSuppressInvalidation(true);
+ m_horizontalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
+ m_horizontalScrollbar->setProportion(clientWidth, contentsWidth());
+ m_horizontalScrollbar->setValue(scroll.width());
+ if (m_scrollbarsSuppressed)
+ m_horizontalScrollbar->setSuppressInvalidation(false);
+ }
+
+ if (m_verticalScrollbar) {
+ int clientHeight = visibleHeight();
+ m_verticalScrollbar->setEnabled(contentsHeight() > clientHeight);
+ int pageStep = max(max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1);
+ IntRect oldRect(m_verticalScrollbar->frameRect());
+ IntRect vBarRect = IntRect(width() - m_verticalScrollbar->width(),
+ 0,
+ m_verticalScrollbar->width(),
+ height() - (m_horizontalScrollbar ? m_horizontalScrollbar->height() : 0));
+ m_verticalScrollbar->setFrameRect(vBarRect);
+ if (!m_scrollbarsSuppressed && oldRect != m_verticalScrollbar->frameRect())
+ m_verticalScrollbar->invalidate();
+
+ if (m_scrollbarsSuppressed)
+ m_verticalScrollbar->setSuppressInvalidation(true);
+ m_verticalScrollbar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
+ m_verticalScrollbar->setProportion(clientHeight, contentsHeight());
+ m_verticalScrollbar->setValue(scroll.height());
+ if (m_scrollbarsSuppressed)
+ m_verticalScrollbar->setSuppressInvalidation(false);
+ }
+
+ if (hasHorizontalScrollbar != (m_horizontalScrollbar != 0) || hasVerticalScrollbar != (m_verticalScrollbar != 0)) {
+ frameRectsChanged();
+ updateScrollCorner();
+ }
+
+ // See if our offset has changed in a situation where we might not have scrollbars.
+ // This can happen when editing a body with overflow:hidden and scrolling to reveal selection.
+ // It can also happen when maximizing a window that has scrollbars (but the new maximized result
+ // does not).
+ IntSize scrollDelta = scroll - m_scrollOffset;
+ if (scrollDelta != IntSize()) {
+ m_scrollOffset = scroll;
+ scrollContents(scrollDelta);
+ }
+
+ m_inUpdateScrollbars = false;
+}
+
+const int panIconSizeLength = 16;
+
+void ScrollView::scrollContents(const IntSize& scrollDelta)
+{
+ if (!hostWindow())
+ return;
+
+ // Since scrolling is double buffered, we will be blitting the scroll view's intersection
+ // with the clip rect every time to keep it smooth.
+ IntRect clipRect = windowClipRect();
+ IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight()));
+ IntRect updateRect = clipRect;
+ updateRect.intersect(scrollViewRect);
+
+ // Invalidate the window (not the backing store).
+ hostWindow()->invalidateWindow(updateRect, false /*immediate*/);
+
+ if (m_drawPanScrollIcon) {
+ int panIconDirtySquareSizeLength = 2 * (panIconSizeLength + max(abs(scrollDelta.width()), abs(scrollDelta.height()))); // We only want to repaint what's necessary
+ IntPoint panIconDirtySquareLocation = IntPoint(m_panScrollIconPoint.x() - (panIconDirtySquareSizeLength / 2), m_panScrollIconPoint.y() - (panIconDirtySquareSizeLength / 2));
+ IntRect panScrollIconDirtyRect = IntRect(panIconDirtySquareLocation , IntSize(panIconDirtySquareSizeLength, panIconDirtySquareSizeLength));
+ panScrollIconDirtyRect.intersect(clipRect);
+ hostWindow()->invalidateContentsAndWindow(panScrollIconDirtyRect, false /*immediate*/);
+ }
+
+ if (canBlitOnScroll()) { // The main frame can just blit the WebView window
+ // FIXME: Find a way to scroll subframes with this faster path
+ if (!scrollContentsFastPath(-scrollDelta, scrollViewRect, clipRect))
+ hostWindow()->invalidateContentsForSlowScroll(updateRect, false);
+ } else {
+ // We need to go ahead and repaint the entire backing store. Do it now before moving the
+ // windowed plugins.
+ hostWindow()->invalidateContentsForSlowScroll(updateRect, false);
+ }
+
+ // This call will move children with native widgets (plugins) and invalidate them as well.
+ frameRectsChanged();
+
+ // Now blit the backingstore into the window which should be very fast.
+ hostWindow()->invalidateWindow(IntRect(), true);
+}
+
+bool ScrollView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
+{
+ hostWindow()->scroll(scrollDelta, rectToScroll, clipRect);
+ return true;
+}
+
+IntPoint ScrollView::windowToContents(const IntPoint& windowPoint) const
+{
+ IntPoint viewPoint = convertFromContainingWindow(windowPoint);
+ return viewPoint + scrollOffset();
+}
+
+IntPoint ScrollView::contentsToWindow(const IntPoint& contentsPoint) const
+{
+ IntPoint viewPoint = contentsPoint - scrollOffset();
+ return convertToContainingWindow(viewPoint);
+}
+
+IntRect ScrollView::windowToContents(const IntRect& windowRect) const
+{
+ IntRect viewRect = convertFromContainingWindow(windowRect);
+ viewRect.move(scrollOffset());
+ return viewRect;
+}
+
+IntRect ScrollView::contentsToWindow(const IntRect& contentsRect) const
+{
+ IntRect viewRect = contentsRect;
+ viewRect.move(-scrollOffset());
+ return convertToContainingWindow(viewRect);
+}
+
+IntRect ScrollView::contentsToScreen(const IntRect& rect) const
+{
+ if (platformWidget())
+ return platformContentsToScreen(rect);
+ if (!hostWindow())
+ return IntRect();
+ return hostWindow()->windowToScreen(contentsToWindow(rect));
+}
+
+IntPoint ScrollView::screenToContents(const IntPoint& point) const
+{
+ if (platformWidget())
+ return platformScreenToContents(point);
+ if (!hostWindow())
+ return IntPoint();
+ return windowToContents(hostWindow()->screenToWindow(point));
+}
+
+bool ScrollView::containsScrollbarsAvoidingResizer() const
+{
+ return !m_scrollbarsAvoidingResizer;
+}
+
+void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta)
+{
+ int oldCount = m_scrollbarsAvoidingResizer;
+ m_scrollbarsAvoidingResizer += overlapDelta;
+ if (parent())
+ parent()->adjustScrollbarsAvoidingResizerCount(overlapDelta);
+ else if (!scrollbarsSuppressed()) {
+ // If we went from n to 0 or from 0 to n and we're the outermost view,
+ // we need to invalidate the windowResizerRect(), since it will now need to paint
+ // differently.
+ if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) ||
+ (oldCount == 0 && m_scrollbarsAvoidingResizer > 0))
+ invalidateRect(windowResizerRect());
+ }
+}
+
+void ScrollView::setParent(ScrollView* parentView)
+{
+ if (parentView == parent())
+ return;
+
+ if (m_scrollbarsAvoidingResizer && parent())
+ parent()->adjustScrollbarsAvoidingResizerCount(-m_scrollbarsAvoidingResizer);
+
+ Widget::setParent(parentView);
+
+ if (m_scrollbarsAvoidingResizer && parent())
+ parent()->adjustScrollbarsAvoidingResizerCount(m_scrollbarsAvoidingResizer);
+}
+
+void ScrollView::setScrollbarsSuppressed(bool suppressed, bool repaintOnUnsuppress)
+{
+ if (suppressed == m_scrollbarsSuppressed)
+ return;
+
+ m_scrollbarsSuppressed = suppressed;
+
+ if (platformWidget())
+ platformSetScrollbarsSuppressed(repaintOnUnsuppress);
+ else if (repaintOnUnsuppress && !suppressed) {
+ if (m_horizontalScrollbar)
+ m_horizontalScrollbar->invalidate();
+ if (m_verticalScrollbar)
+ m_verticalScrollbar->invalidate();
+
+ // Invalidate the scroll corner too on unsuppress.
+ invalidateRect(scrollCornerRect());
+ }
+}
+
+Scrollbar* ScrollView::scrollbarAtPoint(const IntPoint& windowPoint)
+{
+ if (platformWidget())
+ return 0;
+
+ IntPoint viewPoint = convertFromContainingWindow(windowPoint);
+ if (m_horizontalScrollbar && m_horizontalScrollbar->frameRect().contains(viewPoint))
+ return m_horizontalScrollbar.get();
+ if (m_verticalScrollbar && m_verticalScrollbar->frameRect().contains(viewPoint))
+ return m_verticalScrollbar.get();
+ return 0;
+}
+
+void ScrollView::wheelEvent(PlatformWheelEvent& e)
+{
+ // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
+#if PLATFORM(WX)
+ if (!canHaveScrollbars()) {
+#else
+ if (!canHaveScrollbars() || platformWidget()) {
+#endif
+ return;
+ }
+
+ // Determine how much we want to scroll. If we can move at all, we will accept the event.
+ IntSize maxScrollDelta = maximumScrollPosition() - scrollPosition();
+ if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) ||
+ (e.deltaX() > 0 && scrollOffset().width() > 0) ||
+ (e.deltaY() < 0 && maxScrollDelta.height() > 0) ||
+ (e.deltaY() > 0 && scrollOffset().height() > 0)) {
+ e.accept();
+ float deltaX = e.deltaX();
+ float deltaY = e.deltaY();
+ if (e.granularity() == ScrollByPageWheelEvent) {
+ ASSERT(deltaX == 0);
+ bool negative = deltaY < 0;
+ deltaY = max(max<int>(visibleHeight() * Scrollbar::minFractionToStepWhenPaging(), visibleHeight() - Scrollbar::maxOverlapBetweenPages()), 1);
+ if (negative)
+ deltaY = -deltaY;
+ }
+ scrollBy(IntSize(-deltaX, -deltaY));
+ }
+}
+
+void ScrollView::setFrameRect(const IntRect& newRect)
+{
+ IntRect oldRect = frameRect();
+
+ if (newRect == oldRect)
+ return;
+
+ Widget::setFrameRect(newRect);
+
+ if (platformWidget())
+ return;
+
+ if (newRect.width() != oldRect.width() || newRect.height() != oldRect.height()) {
+ updateScrollbars(m_scrollOffset);
+ if (!m_useFixedLayout)
+ contentsResized();
+ }
+
+ frameRectsChanged();
+}
+
+void ScrollView::frameRectsChanged()
+{
+ if (platformWidget())
+ return;
+
+ HashSet<RefPtr<Widget> >::const_iterator end = m_children.end();
+ for (HashSet<RefPtr<Widget> >::const_iterator current = m_children.begin(); current != end; ++current)
+ (*current)->frameRectsChanged();
+}
+
+void ScrollView::repaintContentRectangle(const IntRect& rect, bool now)
+{
+ IntRect paintRect = rect;
+ if (!paintsEntireContents())
+ paintRect.intersect(visibleContentRect());
+ if (paintRect.isEmpty())
+ return;
+
+ if (platformWidget()) {
+ platformRepaintContentRectangle(paintRect, now);
+ return;
+ }
+
+ if (hostWindow())
+ hostWindow()->invalidateContentsAndWindow(contentsToWindow(paintRect), now /*immediate*/);
+}
+
+IntRect ScrollView::scrollCornerRect() const
+{
+ IntRect cornerRect;
+
+ if (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) {
+ cornerRect.unite(IntRect(m_horizontalScrollbar->width(),
+ height() - m_horizontalScrollbar->height(),
+ width() - m_horizontalScrollbar->width(),
+ m_horizontalScrollbar->height()));
+ }
+
+ if (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0) {
+ cornerRect.unite(IntRect(width() - m_verticalScrollbar->width(),
+ m_verticalScrollbar->height(),
+ m_verticalScrollbar->width(),
+ height() - m_verticalScrollbar->height()));
+ }
+
+ return cornerRect;
+}
+
+void ScrollView::updateScrollCorner()
+{
+}
+
+void ScrollView::paintScrollCorner(GraphicsContext* context, const IntRect& cornerRect)
+{
+ ScrollbarTheme::nativeTheme()->paintScrollCorner(this, context, cornerRect);
+}
+
+void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect)
+{
+ if (m_horizontalScrollbar)
+ m_horizontalScrollbar->paint(context, rect);
+ if (m_verticalScrollbar)
+ m_verticalScrollbar->paint(context, rect);
+
+ paintScrollCorner(context, scrollCornerRect());
+}
+
+void ScrollView::paintPanScrollIcon(GraphicsContext* context)
+{
+ static Image* panScrollIcon = Image::loadPlatformResource("panIcon").releaseRef();
+ context->drawImage(panScrollIcon, DeviceColorSpace, m_panScrollIconPoint);
+}
+
+void ScrollView::paint(GraphicsContext* context, const IntRect& rect)
+{
+ if (platformWidget()) {
+ Widget::paint(context, rect);
+ return;
+ }
+
+ if (context->paintingDisabled() && !context->updatingControlTints())
+ return;
+
+ IntRect documentDirtyRect = rect;
+ documentDirtyRect.intersect(frameRect());
+
+ context->save();
+
+ context->translate(x(), y());
+ documentDirtyRect.move(-x(), -y());
+
+ context->translate(-scrollX(), -scrollY());
+ documentDirtyRect.move(scrollX(), scrollY());
+
+ context->clip(visibleContentRect());
+
+ paintContents(context, documentDirtyRect);
+
+ context->restore();
+
+ // Now paint the scrollbars.
+ if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) {
+ context->save();
+ IntRect scrollViewDirtyRect = rect;
+ scrollViewDirtyRect.intersect(frameRect());
+ context->translate(x(), y());
+ scrollViewDirtyRect.move(-x(), -y());
+
+ paintScrollbars(context, scrollViewDirtyRect);
+
+ context->restore();
+ }
+
+ // Paint the panScroll Icon
+ if (m_drawPanScrollIcon)
+ paintPanScrollIcon(context);
+}
+
+bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint)
+{
+ if (!scrollbarCornerPresent())
+ return false;
+
+ IntPoint viewPoint = convertFromContainingWindow(windowPoint);
+
+ if (m_horizontalScrollbar) {
+ int horizontalScrollbarYMin = m_horizontalScrollbar->frameRect().y();
+ int horizontalScrollbarYMax = m_horizontalScrollbar->frameRect().y() + m_horizontalScrollbar->frameRect().height();
+ int horizontalScrollbarXMin = m_horizontalScrollbar->frameRect().x() + m_horizontalScrollbar->frameRect().width();
+
+ return viewPoint.y() > horizontalScrollbarYMin && viewPoint.y() < horizontalScrollbarYMax && viewPoint.x() > horizontalScrollbarXMin;
+ }
+
+ int verticalScrollbarXMin = m_verticalScrollbar->frameRect().x();
+ int verticalScrollbarXMax = m_verticalScrollbar->frameRect().x() + m_verticalScrollbar->frameRect().width();
+ int verticalScrollbarYMin = m_verticalScrollbar->frameRect().y() + m_verticalScrollbar->frameRect().height();
+
+ return viewPoint.x() > verticalScrollbarXMin && viewPoint.x() < verticalScrollbarXMax && viewPoint.y() > verticalScrollbarYMin;
+}
+
+bool ScrollView::scrollbarCornerPresent() const
+{
+ return (m_horizontalScrollbar && width() - m_horizontalScrollbar->width() > 0) ||
+ (m_verticalScrollbar && height() - m_verticalScrollbar->height() > 0);
+}
+
+IntRect ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& localRect) const
+{
+ // Scrollbars won't be transformed within us
+ IntRect newRect = localRect;
+ newRect.move(scrollbar->x(), scrollbar->y());
+ return newRect;
+}
+
+IntRect ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
+{
+ IntRect newRect = parentRect;
+ // Scrollbars won't be transformed within us
+ newRect.move(-scrollbar->x(), -scrollbar->y());
+ return newRect;
+}
+
+// FIXME: test these on windows
+IntPoint ScrollView::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& localPoint) const
+{
+ // Scrollbars won't be transformed within us
+ IntPoint newPoint = localPoint;
+ newPoint.move(scrollbar->x(), scrollbar->y());
+ return newPoint;
+}
+
+IntPoint ScrollView::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
+{
+ IntPoint newPoint = parentPoint;
+ // Scrollbars won't be transformed within us
+ newPoint.move(-scrollbar->x(), -scrollbar->y());
+ return newPoint;
+}
+
+void ScrollView::setParentVisible(bool visible)
+{
+ if (isParentVisible() == visible)
+ return;
+
+ Widget::setParentVisible(visible);
+
+ if (!isSelfVisible())
+ return;
+
+ HashSet<RefPtr<Widget> >::iterator end = m_children.end();
+ for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
+ (*it)->setParentVisible(visible);
+}
+
+void ScrollView::show()
+{
+ if (!isSelfVisible()) {
+ setSelfVisible(true);
+ if (isParentVisible()) {
+ HashSet<RefPtr<Widget> >::iterator end = m_children.end();
+ for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
+ (*it)->setParentVisible(true);
+ }
+ }
+
+ Widget::show();
+}
+
+void ScrollView::hide()
+{
+ if (isSelfVisible()) {
+ if (isParentVisible()) {
+ HashSet<RefPtr<Widget> >::iterator end = m_children.end();
+ for (HashSet<RefPtr<Widget> >::iterator it = m_children.begin(); it != end; ++it)
+ (*it)->setParentVisible(false);
+ }
+ setSelfVisible(false);
+ }
+
+ Widget::hide();
+}
+
+bool ScrollView::isOffscreen() const
+{
+ if (platformWidget())
+ return platformIsOffscreen();
+
+ if (!isVisible())
+ return true;
+
+ // FIXME: Add a HostWindow::isOffscreen method here. Since only Mac implements this method
+ // currently, we can add the method when the other platforms decide to implement this concept.
+ return false;
+}
+
+
+void ScrollView::addPanScrollIcon(const IntPoint& iconPosition)
+{
+ if (!hostWindow())
+ return;
+ m_drawPanScrollIcon = true;
+ m_panScrollIconPoint = IntPoint(iconPosition.x() - panIconSizeLength / 2 , iconPosition.y() - panIconSizeLength / 2) ;
+ hostWindow()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/);
+}
+
+void ScrollView::removePanScrollIcon()
+{
+ if (!hostWindow())
+ return;
+ m_drawPanScrollIcon = false;
+ hostWindow()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/);
+}
+
+#if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(EFL)
+
+void ScrollView::platformInit()
+{
+}
+
+void ScrollView::platformDestroy()
+{
+}
+
+#endif
+
+#if !PLATFORM(WX) && !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(MAC)
+
+void ScrollView::platformAddChild(Widget*)
+{
+}
+
+void ScrollView::platformRemoveChild(Widget*)
+{
+}
+
+#endif
+
+#if !PLATFORM(MAC)
+
+void ScrollView::platformSetScrollbarsSuppressed(bool)
+{
+}
+
+#endif
+
+#if !PLATFORM(MAC) && !PLATFORM(WX)
+
+void ScrollView::platformSetScrollbarModes()
+{
+}
+
+void ScrollView::platformScrollbarModes(ScrollbarMode& horizontal, ScrollbarMode& vertical) const
+{
+ horizontal = ScrollbarAuto;
+ vertical = ScrollbarAuto;
+}
+
+void ScrollView::platformSetCanBlitOnScroll(bool)
+{
+}
+
+bool ScrollView::platformCanBlitOnScroll() const
+{
+ return false;
+}
+
+IntRect ScrollView::platformVisibleContentRect(bool) const
+{
+ return IntRect();
+}
+
+IntSize ScrollView::platformContentsSize() const
+{
+ return IntSize();
+}
+
+void ScrollView::platformSetContentsSize()
+{
+}
+
+IntRect ScrollView::platformContentsToScreen(const IntRect& rect) const
+{
+ return rect;
+}
+
+IntPoint ScrollView::platformScreenToContents(const IntPoint& point) const
+{
+ return point;
+}
+
+void ScrollView::platformSetScrollPosition(const IntPoint&)
+{
+}
+
+bool ScrollView::platformScroll(ScrollDirection, ScrollGranularity)
+{
+ return true;
+}
+
+void ScrollView::platformRepaintContentRectangle(const IntRect&, bool /*now*/)
+{
+}
+
+bool ScrollView::platformIsOffscreen() const
+{
+ return false;
+}
+
+#endif
+
+}
+