WebCore/rendering/RenderScrollbar.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/rendering/RenderScrollbar.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2008, 2009 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 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 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 "RenderScrollbar.h"
+
+#include "RenderScrollbarPart.h"
+#include "RenderScrollbarTheme.h"
+
+namespace WebCore {
+
+PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderBox* renderer)
+{
+    return adoptRef(new RenderScrollbar(client, orientation, renderer));
+}
+
+RenderScrollbar::RenderScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderBox* renderer)
+    : Scrollbar(client, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme())
+    , m_owner(renderer)
+{
+    // FIXME: We need to do this because RenderScrollbar::styleChanged is called as soon as the scrollbar is created.
+    
+    // Update the scrollbar size.
+    updateScrollbarPart(ScrollbarBGPart);
+    RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
+    if (!part)
+        return;
+
+    part->layout();
+    setFrameRect(IntRect(0, 0, part->width(), part->height()));
+}
+
+RenderScrollbar::~RenderScrollbar()
+{
+    ASSERT(m_parts.isEmpty());
+}
+
+void RenderScrollbar::setParent(ScrollView* parent)
+{
+    Scrollbar::setParent(parent);
+    if (!parent) {
+        // Destroy all of the scrollbar's RenderBoxes.
+        updateScrollbarParts(true);
+    }
+}
+
+void RenderScrollbar::setEnabled(bool e)
+{
+    bool wasEnabled = enabled();
+    Scrollbar::setEnabled(e);
+    if (wasEnabled != e)
+        updateScrollbarParts();
+}
+
+void RenderScrollbar::styleChanged()
+{
+    updateScrollbarParts();
+}
+
+void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect)
+{
+    if (context->updatingControlTints()) {
+        updateScrollbarParts();
+        return;
+    }
+    Scrollbar::paint(context, damageRect);
+}
+
+void RenderScrollbar::setHoveredPart(ScrollbarPart part)
+{
+    if (part == m_hoveredPart)
+        return;
+
+    ScrollbarPart oldPart = m_hoveredPart;
+    m_hoveredPart = part;
+
+    updateScrollbarPart(oldPart);
+    updateScrollbarPart(m_hoveredPart);
+
+    updateScrollbarPart(ScrollbarBGPart);
+    updateScrollbarPart(TrackBGPart);
+}
+
+void RenderScrollbar::setPressedPart(ScrollbarPart part)
+{
+    ScrollbarPart oldPart = m_pressedPart;
+    Scrollbar::setPressedPart(part);
+    
+    updateScrollbarPart(oldPart);
+    updateScrollbarPart(part);
+    
+    updateScrollbarPart(ScrollbarBGPart);
+    updateScrollbarPart(TrackBGPart);
+}
+
+static ScrollbarPart s_styleResolvePart;
+static RenderScrollbar* s_styleResolveScrollbar;
+
+RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve()
+{
+    return s_styleResolveScrollbar;
+}
+
+ScrollbarPart RenderScrollbar::partForStyleResolve()
+{
+    return s_styleResolvePart;
+}
+
+PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, PseudoId pseudoId)
+{
+    s_styleResolvePart = partType;
+    s_styleResolveScrollbar = this;
+    RefPtr<RenderStyle> result = m_owner->getUncachedPseudoStyle(pseudoId, m_owner->style());
+    s_styleResolvePart = NoPart;
+    s_styleResolveScrollbar = 0;
+    return result;
+}
+
+void RenderScrollbar::updateScrollbarParts(bool destroy)
+{
+    updateScrollbarPart(ScrollbarBGPart, destroy);
+    updateScrollbarPart(BackButtonStartPart, destroy);
+    updateScrollbarPart(ForwardButtonStartPart, destroy);
+    updateScrollbarPart(BackTrackPart, destroy);
+    updateScrollbarPart(ThumbPart, destroy);
+    updateScrollbarPart(ForwardTrackPart, destroy);
+    updateScrollbarPart(BackButtonEndPart, destroy);
+    updateScrollbarPart(ForwardButtonEndPart, destroy);
+    updateScrollbarPart(TrackBGPart, destroy);
+    
+    if (destroy)
+        return;
+
+    // See if the scrollbar's thickness changed.  If so, we need to mark our owning object as needing a layout.
+    bool isHorizontal = orientation() == HorizontalScrollbar;    
+    int oldThickness = isHorizontal ? height() : width();
+    int newThickness = 0;
+    RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart);
+    if (part) {
+        part->layout();
+        newThickness = isHorizontal ? part->height() : part->width();
+    }
+    
+    if (newThickness != oldThickness) {
+        setFrameRect(IntRect(x(), y(), isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height()));
+        m_owner->setChildNeedsLayout(true);
+    }
+}
+
+static PseudoId pseudoForScrollbarPart(ScrollbarPart part)
+{
+    switch (part) {
+        case BackButtonStartPart:
+        case ForwardButtonStartPart:
+        case BackButtonEndPart:
+        case ForwardButtonEndPart:
+            return SCROLLBAR_BUTTON;
+        case BackTrackPart:
+        case ForwardTrackPart:
+            return SCROLLBAR_TRACK_PIECE;
+        case ThumbPart:
+            return SCROLLBAR_THUMB;
+        case TrackBGPart:
+            return SCROLLBAR_TRACK;
+        case ScrollbarBGPart:
+            return SCROLLBAR;
+        case NoPart:
+        case AllParts:
+            break;
+    }
+    ASSERT_NOT_REACHED();
+    return SCROLLBAR;
+}
+
+void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy)
+{
+    if (partType == NoPart)
+        return;
+
+    RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType,  pseudoForScrollbarPart(partType)) : 0;
+    
+    bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE;
+    
+    if (needRenderer && partStyle->display() != BLOCK) {
+        // See if we are a button that should not be visible according to OS settings.
+        ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement();
+        switch (partType) {
+            case BackButtonStartPart:
+                needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart ||
+                                buttonsPlacement == ScrollbarButtonsDoubleBoth);
+                break;
+            case ForwardButtonStartPart:
+                needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth);
+                break;
+            case BackButtonEndPart:
+                needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth);
+                break;
+            case ForwardButtonEndPart:
+                needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd ||
+                                buttonsPlacement == ScrollbarButtonsDoubleBoth);
+                break;
+            default:
+                break;
+        }
+    }
+    
+    RenderScrollbarPart* partRenderer = m_parts.get(partType);
+    if (!partRenderer && needRenderer) {
+        partRenderer = new (m_owner->renderArena()) RenderScrollbarPart(m_owner->document(), this, partType);
+        m_parts.set(partType, partRenderer);
+    } else if (partRenderer && !needRenderer) {
+        m_parts.remove(partType);
+        partRenderer->destroy();
+        partRenderer = 0;
+    }
+    
+    if (partRenderer)
+        partRenderer->setStyle(partStyle.release());
+}
+
+void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect)
+{
+    RenderScrollbarPart* partRenderer = m_parts.get(partType);
+    if (!partRenderer)
+        return;
+    partRenderer->paintIntoRect(graphicsContext, x(), y(), rect);
+}
+
+IntRect RenderScrollbar::buttonRect(ScrollbarPart partType)
+{
+    RenderScrollbarPart* partRenderer = m_parts.get(partType);
+    if (!partRenderer)
+        return IntRect();
+        
+    partRenderer->layout();
+    
+    bool isHorizontal = orientation() == HorizontalScrollbar;
+    if (partType == BackButtonStartPart)
+        return IntRect(x(), y(), isHorizontal ? partRenderer->width() : width(), isHorizontal ? height() : partRenderer->height());
+    if (partType == ForwardButtonEndPart)
+        return IntRect(isHorizontal ? x() + width() - partRenderer->width() : x(),
+        
+                       isHorizontal ? y() : y() + height() - partRenderer->height(),
+                       isHorizontal ? partRenderer->width() : width(),
+                       isHorizontal ? height() : partRenderer->height());
+    
+    if (partType == ForwardButtonStartPart) {
+        IntRect previousButton = buttonRect(BackButtonStartPart);
+        return IntRect(isHorizontal ? x() + previousButton.width() : x(),
+                       isHorizontal ? y() : y() + previousButton.height(),
+                       isHorizontal ? partRenderer->width() : width(),
+                       isHorizontal ? height() : partRenderer->height());
+    }
+    
+    IntRect followingButton = buttonRect(ForwardButtonEndPart);
+    return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->width() : x(),
+                   isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->height(),
+                   isHorizontal ? partRenderer->width() : width(),
+                   isHorizontal ? height() : partRenderer->height());
+}
+
+IntRect RenderScrollbar::trackRect(int startLength, int endLength)
+{
+    RenderScrollbarPart* part = m_parts.get(TrackBGPart);
+    if (part)
+        part->layout();
+
+    if (orientation() == HorizontalScrollbar) {
+        int marginLeft = part ? part->marginLeft() : 0;
+        int marginRight = part ? part->marginRight() : 0;
+        startLength += marginLeft;
+        endLength += marginRight;
+        int totalLength = startLength + endLength;
+        return IntRect(x() + startLength, y(), width() - totalLength, height());
+    }
+    
+    int marginTop = part ? part->marginTop() : 0;
+    int marginBottom = part ? part->marginBottom() : 0;
+    startLength += marginTop;
+    endLength += marginBottom;
+    int totalLength = startLength + endLength;
+
+    return IntRect(x(), y() + startLength, width(), height() - totalLength);
+}
+
+IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect)
+{
+    RenderScrollbarPart* partRenderer = m_parts.get(partType);
+    if (!partRenderer)
+        return oldRect;
+    
+    partRenderer->layout();
+    
+    IntRect rect = oldRect;
+    if (orientation() == HorizontalScrollbar) {
+        rect.setX(rect.x() + partRenderer->marginLeft());
+        rect.setWidth(rect.width() - (partRenderer->marginLeft() + partRenderer->marginRight()));
+    } else {
+        rect.setY(rect.y() + partRenderer->marginTop());
+        rect.setHeight(rect.height() - (partRenderer->marginTop() + partRenderer->marginBottom()));
+    }
+    return rect;
+}
+
+int RenderScrollbar::minimumThumbLength()
+{
+    RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart);
+    if (!partRenderer)
+        return 0;    
+    partRenderer->layout();
+    return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height();
+}
+
+}