| /* | 
 |  * Copyright (C) 2007 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" | 
 |  | 
 | #ifdef USE_SAFARI_THEME  | 
 |  | 
 | #include "PlatformScrollBar.h" | 
 |  | 
 | #include "EventHandler.h" | 
 | #include "FrameView.h" | 
 | #include "Frame.h" | 
 | #include "GraphicsContext.h" | 
 | #include "IntRect.h" | 
 | #include "PlatformMouseEvent.h" | 
 |  | 
 | #include <CoreGraphics/CoreGraphics.h> | 
 | #include <SafariTheme/SafariTheme.h> | 
 |  | 
 | // FIXME: There are repainting problems due to Aqua scroll bar buttons' visual overflow. | 
 |  | 
 | using namespace std; | 
 |  | 
 | namespace WebCore { | 
 |  | 
 | using namespace SafariTheme; | 
 |  | 
 | // FIXME: We should get these numbers from SafariTheme | 
 | static int cHorizontalWidth[] = { 15, 11 }; | 
 | static int cHorizontalHeight[] = { 15, 11 }; | 
 | static int cVerticalWidth[] = { 15, 11 }; | 
 | static int cVerticalHeight[] = { 15, 11 }; | 
 | static int cRealButtonLength[] = { 28, 21 }; | 
 | static int cButtonInset[] = { 14, 11 }; | 
 | // cRealButtonLength - cButtonInset | 
 | static int cButtonLength[] = { 14, 10 }; | 
 | static int cThumbWidth[] = { 15, 11 }; | 
 | static int cThumbHeight[] = { 15, 11 }; | 
 | static int cThumbMinLength[] = { 26, 20 }; | 
 |  | 
 | static paintThemePartPtr paintThemePart; | 
 |  | 
 | static HMODULE themeDLL; | 
 |  | 
 | const double cInitialTimerDelay = 0.25; | 
 | const double cNormalTimerDelay = 0.05; | 
 |  | 
 | PlatformScrollbar::PlatformScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize size) | 
 |     : Scrollbar(client, orientation, size), m_hoveredPart(NoPart), m_pressedPart(NoPart), m_pressedPos(0), | 
 |       m_scrollTimer(this, &PlatformScrollbar::autoscrollTimerFired), | 
 |       m_overlapsResizer(false) | 
 | { | 
 |     // Obtain the correct scrollbar sizes from the system. | 
 |     if (!cHorizontalWidth) { | 
 |         // FIXME: Get metics from SafariTheme | 
 |     } | 
 |  | 
 |     if (!themeDLL) | 
 |         themeDLL = ::LoadLibrary(SAFARITHEMEDLL); | 
 |  | 
 |     if (themeDLL) | 
 |         paintThemePart = (paintThemePartPtr)GetProcAddress(themeDLL, "paintThemePart"); | 
 |  | 
 |     if (orientation == VerticalScrollbar) | 
 |         setFrameGeometry(IntRect(0, 0, cVerticalWidth[controlSize()], cVerticalHeight[controlSize()])); | 
 |     else | 
 |         setFrameGeometry(IntRect(0, 0, cHorizontalWidth[controlSize()], cHorizontalHeight[controlSize()])); | 
 | } | 
 |  | 
 | PlatformScrollbar::~PlatformScrollbar() | 
 | { | 
 |     stopTimerIfNeeded(); | 
 | } | 
 |  | 
 | void PlatformScrollbar::updateThumbPosition() | 
 | { | 
 |     invalidateTrack(); | 
 | } | 
 |  | 
 | void PlatformScrollbar::updateThumbProportion() | 
 | { | 
 |     invalidateTrack(); | 
 | } | 
 |  | 
 | static IntRect trackRepaintRect(const IntRect& trackRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) | 
 | { | 
 |     IntRect paintRect(trackRect); | 
 |     if (orientation == HorizontalScrollbar) | 
 |         paintRect.inflateX(cButtonLength[controlSize]); | 
 |     else | 
 |         paintRect.inflateY(cButtonLength[controlSize]); | 
 |  | 
 |     return paintRect; | 
 | } | 
 |  | 
 | static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start) | 
 | { | 
 |     IntRect paintRect(buttonRect); | 
 |     if (orientation == HorizontalScrollbar) { | 
 |         paintRect.setWidth(cRealButtonLength[controlSize]); | 
 |         if (!start) | 
 |             paintRect.setX(buttonRect.x() - (cRealButtonLength[controlSize] - buttonRect.width())); | 
 |     } else { | 
 |         paintRect.setHeight(cRealButtonLength[controlSize]); | 
 |         if (!start) | 
 |             paintRect.setY(buttonRect.y() - (cRealButtonLength[controlSize] - buttonRect.height())); | 
 |     } | 
 |  | 
 |     return paintRect; | 
 | } | 
 |  | 
 | void PlatformScrollbar::invalidateTrack() | 
 | { | 
 |     IntRect rect = trackRepaintRect(trackRect(), m_orientation, controlSize()); | 
 |     rect.move(-x(), -y()); | 
 |     invalidateRect(rect); | 
 | } | 
 |  | 
 | void PlatformScrollbar::invalidatePart(ScrollbarPart part) | 
 | { | 
 |     if (part == NoPart) | 
 |         return; | 
 |  | 
 |     IntRect result;     | 
 |     switch (part) { | 
 |         case BackButtonPart: | 
 |             result = buttonRepaintRect(backButtonRect(), m_orientation, controlSize(), true); | 
 |             break; | 
 |         case ForwardButtonPart: | 
 |             result = buttonRepaintRect(forwardButtonRect(), m_orientation, controlSize(), false); | 
 |             break; | 
 |         default: { | 
 |             IntRect beforeThumbRect, thumbRect, afterThumbRect; | 
 |             splitTrack(trackRect(), beforeThumbRect, thumbRect, afterThumbRect); | 
 |             if (part == BackTrackPart) | 
 |                 result = beforeThumbRect; | 
 |             else if (part == ForwardTrackPart) | 
 |                 result = afterThumbRect; | 
 |             else | 
 |                 result = thumbRect; | 
 |         } | 
 |     } | 
 |     result.move(-x(), -y()); | 
 |     invalidateRect(result); | 
 | } | 
 |  | 
 | int PlatformScrollbar::width() const | 
 | { | 
 |     return Widget::width(); | 
 | } | 
 |  | 
 | int PlatformScrollbar::height() const | 
 | { | 
 |     return Widget::height(); | 
 | } | 
 |  | 
 | void PlatformScrollbar::setRect(const IntRect& rect) | 
 | { | 
 |     // Get our window resizer rect and see if we overlap.  Adjust to avoid the overlap | 
 |     // if necessary. | 
 |     IntRect adjustedRect(rect); | 
 |     if (parent() && parent()->isFrameView()) { | 
 |         bool overlapsResizer = false; | 
 |         FrameView* view = static_cast<FrameView*>(parent()); | 
 |         IntRect resizerRect = view->windowResizerRect(); | 
 |         resizerRect.setLocation(view->convertFromContainingWindow(resizerRect.location())); | 
 |         if (rect.intersects(resizerRect)) { | 
 |             if (orientation() == HorizontalScrollbar) { | 
 |                 int overlap = rect.right() - resizerRect.x(); | 
 |                 if (overlap > 0 && resizerRect.right() >= rect.right()) { | 
 |                     adjustedRect.setWidth(rect.width() - overlap); | 
 |                     overlapsResizer = true; | 
 |                 } | 
 |             } else { | 
 |                 int overlap = rect.bottom() - resizerRect.y(); | 
 |                 if (overlap > 0 && resizerRect.bottom() >= rect.bottom()) { | 
 |                     adjustedRect.setHeight(rect.height() - overlap); | 
 |                     overlapsResizer = true; | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |         if (overlapsResizer != m_overlapsResizer) { | 
 |             m_overlapsResizer = overlapsResizer; | 
 |             view->adjustOverlappingScrollbarCount(m_overlapsResizer ? 1 : -1); | 
 |         } | 
 |     } | 
 |  | 
 |     setFrameGeometry(adjustedRect); | 
 | } | 
 |  | 
 | void PlatformScrollbar::setParent(ScrollView* parentView) | 
 | { | 
 |     if (!parentView && m_overlapsResizer && parent() && parent()->isFrameView()) | 
 |         static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(-1); | 
 |     Widget::setParent(parentView); | 
 | } | 
 |  | 
 | void PlatformScrollbar::setEnabled(bool enabled) | 
 | { | 
 |     if (enabled != isEnabled()) { | 
 |         Widget::setEnabled(enabled); | 
 |         invalidate(); | 
 |     } | 
 | } | 
 |  | 
 | void PlatformScrollbar::paint(GraphicsContext* graphicsContext, const IntRect& damageRect) | 
 | { | 
 |     if (graphicsContext->paintingDisabled()) | 
 |         return; | 
 |  | 
 |     // Don't paint anything if the scrollbar doesn't intersect the damage rect. | 
 |     if (!frameGeometry().intersects(damageRect)) | 
 |         return; | 
 |  | 
 |     IntRect track = trackRect(); | 
 |     paintTrack(graphicsContext, track, true, damageRect); | 
 |  | 
 |     if (isEnabled()) { | 
 |         paintButton(graphicsContext, backButtonRect(), true, damageRect); | 
 |         paintButton(graphicsContext, forwardButtonRect(), false, damageRect); | 
 |     } | 
 |  | 
 |     if (damageRect.intersects(track) && isEnabled()) { | 
 |         IntRect startTrackRect, thumbRect, endTrackRect; | 
 |         splitTrack(track, startTrackRect, thumbRect, endTrackRect); | 
 |         paintThumb(graphicsContext, thumbRect, damageRect); | 
 |     } | 
 | } | 
 |  | 
 | IntRect PlatformScrollbar::backButtonRect() const | 
 | { | 
 |     // Our desired rect is essentially 17x17. | 
 |      | 
 |     // Our actual rect will shrink to half the available space when | 
 |     // we have < 34 pixels left.  This allows the scrollbar | 
 |     // to scale down and function even at tiny sizes. | 
 |     if (m_orientation == HorizontalScrollbar) | 
 |         return IntRect(x(), y(), cButtonLength[controlSize()], cHorizontalHeight[controlSize()]); | 
 |     return IntRect(x(), y(), cVerticalWidth[controlSize()], cButtonLength[controlSize()]); | 
 | } | 
 |  | 
 | IntRect PlatformScrollbar::forwardButtonRect() const | 
 | { | 
 |     // Our desired rect is essentially 17x17. | 
 |      | 
 |     // Our actual rect will shrink to half the available space when | 
 |     // we have < 34 pixels left.  This allows the scrollbar | 
 |     // to scale down and function even at tiny sizes. | 
 |  | 
 |     if (m_orientation == HorizontalScrollbar) | 
 |         return IntRect(x() + width() - cButtonLength[controlSize()], y(), cButtonLength[controlSize()], cHorizontalHeight[controlSize()]); | 
 |      | 
 |     return IntRect(x(), y() + height() - cButtonLength[controlSize()], cVerticalWidth[controlSize()], cButtonLength[controlSize()]); | 
 | } | 
 |  | 
 | IntRect PlatformScrollbar::trackRect() const | 
 | { | 
 |     if (m_orientation == HorizontalScrollbar) { | 
 |         if (width() < 2 * cHorizontalWidth[controlSize()]) | 
 |             return IntRect(); | 
 |         return IntRect(x() + cButtonLength[controlSize()], y(), width() - 2 * cButtonLength[controlSize()], cHorizontalHeight[controlSize()]); | 
 |     } | 
 |  | 
 |     if (height() < 2 * cVerticalHeight[controlSize()]) | 
 |         return IntRect(); | 
 |     return IntRect(x(), y() + cButtonLength[controlSize()], cVerticalWidth[controlSize()], height() - 2 * cButtonLength[controlSize()]); | 
 | } | 
 |  | 
 | IntRect PlatformScrollbar::thumbRect() const | 
 | { | 
 |     IntRect beforeThumbRect, thumbRect, afterThumbRect; | 
 |     splitTrack(trackRect(), beforeThumbRect, thumbRect, afterThumbRect); | 
 |     return thumbRect; | 
 | } | 
 |  | 
 | void PlatformScrollbar::splitTrack(const IntRect& trackRect, IntRect& beforeThumbRect, IntRect& thumbRect, IntRect& afterThumbRect) const | 
 | { | 
 |     // This function won't even get called unless we're big enough to have some combination of these three rects where at least | 
 |     // one of them is non-empty. | 
 |     int thumbPos = thumbPosition(); | 
 |     if (m_orientation == HorizontalScrollbar) { | 
 |         thumbRect = IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - cThumbHeight[controlSize()]) / 2, thumbLength(), cThumbHeight[controlSize()]); | 
 |         beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), thumbPos, trackRect.height()); | 
 |         afterThumbRect = IntRect(thumbRect.x() + thumbRect.width(), trackRect.y(), trackRect.right() - thumbRect.right(), trackRect.height()); | 
 |     } else { | 
 |         thumbRect = IntRect(trackRect.x() + (trackRect.width() - cThumbWidth[controlSize()]) / 2, trackRect.y() + thumbPos, cThumbWidth[controlSize()], thumbLength()); | 
 |         beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), trackRect.width(), thumbPos); | 
 |         afterThumbRect = IntRect(trackRect.x(), thumbRect.y() + thumbRect.height(), trackRect.width(), trackRect.bottom() - thumbRect.bottom()); | 
 |     } | 
 | } | 
 |  | 
 | int PlatformScrollbar::thumbPosition() const | 
 | { | 
 |     if (isEnabled()) | 
 |         return (float)m_currentPos * (trackLength() - thumbLength()) / (m_totalSize - m_visibleSize); | 
 |     return 0; | 
 | } | 
 |  | 
 | int PlatformScrollbar::thumbLength() const | 
 | { | 
 |     if (!isEnabled()) | 
 |         return 0; | 
 |  | 
 |     float proportion = (float)(m_visibleSize) / m_totalSize; | 
 |     int trackLen = trackLength(); | 
 |     int length = proportion * trackLen; | 
 |     int minLength = cThumbMinLength[controlSize()]; | 
 |     length = max(length, minLength); | 
 |     if (length > trackLen) | 
 |         length = 0; // Once the thumb is below the track length, it just goes away (to make more room for the track). | 
 |     return length; | 
 | } | 
 |  | 
 | int PlatformScrollbar::trackLength() const | 
 | { | 
 |     return (m_orientation == HorizontalScrollbar) ? trackRect().width() : trackRect().height(); | 
 | } | 
 |  | 
 | void PlatformScrollbar::paintButton(GraphicsContext* context, const IntRect& rect, bool start, const IntRect& damageRect) const | 
 | { | 
 |     IntRect paintRect = buttonRepaintRect(rect, m_orientation, controlSize(), start); | 
 |      | 
 |     if (!damageRect.intersects(paintRect)) | 
 |         return; | 
 |  | 
 |     ThemePart part; | 
 |     ThemeControlState state = 0; | 
 |     if (m_orientation == HorizontalScrollbar) | 
 |         part = start ? ScrollLeftArrowPart : ScrollRightArrowPart; | 
 |     else | 
 |         part = start ? ScrollUpArrowPart : ScrollDownArrowPart; | 
 |  | 
 |     if (isEnabled()) | 
 |         state |= EnabledState; | 
 |     if ((m_pressedPart == BackButtonPart && start) | 
 |         || (m_pressedPart == ForwardButtonPart && !start)) | 
 |         state |= PressedState; | 
 |  | 
 |     paintThemePart(part, context->platformContext(), paintRect, controlSize() == SmallScrollbar ? NSSmallControlSize : NSRegularControlSize, state); | 
 | } | 
 |  | 
 | void PlatformScrollbar::paintTrack(GraphicsContext* context, const IntRect& rect, bool start, const IntRect& damageRect) const | 
 | { | 
 |     IntRect paintRect = trackRepaintRect(rect, m_orientation, controlSize()); | 
 |      | 
 |     if (!damageRect.intersects(paintRect)) | 
 |         return; | 
 |  | 
 |     ThemePart part = m_orientation == HorizontalScrollbar ? HScrollTrackPart : VScrollTrackPart; | 
 |     ThemeControlState state = 0; | 
 |     if (isEnabled()) | 
 |         state |= EnabledState; | 
 |  | 
 |     paintThemePart(part, context->platformContext(), paintRect, controlSize() == SmallScrollbar ? NSSmallControlSize : NSRegularControlSize, state); | 
 | } | 
 |  | 
 | void PlatformScrollbar::paintThumb(GraphicsContext* context, const IntRect& rect, const IntRect& damageRect) const | 
 | { | 
 |     if (!damageRect.intersects(rect)) | 
 |         return; | 
 |  | 
 |     ThemePart part = m_orientation == HorizontalScrollbar ? HScrollThumbPart : VScrollThumbPart; | 
 |     ThemeControlState state = 0; | 
 |     if (isEnabled()) | 
 |         state |= EnabledState; | 
 |  | 
 |     paintThemePart(part, context->platformContext(), rect, controlSize() == SmallScrollbar ? NSSmallControlSize : NSRegularControlSize, state); | 
 | } | 
 |  | 
 | ScrollbarPart PlatformScrollbar::hitTest(const PlatformMouseEvent& evt) | 
 | { | 
 |     ScrollbarPart result = NoPart; | 
 |     if (!isEnabled()) | 
 |         return result; | 
 |  | 
 |     IntPoint mousePosition = convertFromContainingWindow(evt.pos()); | 
 |     mousePosition.move(x(), y()); | 
 |     if (backButtonRect().contains(mousePosition)) | 
 |         result = BackButtonPart; | 
 |     else if (forwardButtonRect().contains(mousePosition)) | 
 |         result = ForwardButtonPart; | 
 |     else { | 
 |         IntRect track = trackRect(); | 
 |         if (track.contains(mousePosition)) { | 
 |             IntRect beforeThumbRect, thumbRect, afterThumbRect; | 
 |             splitTrack(track, beforeThumbRect, thumbRect, afterThumbRect); | 
 |             if (beforeThumbRect.contains(mousePosition)) | 
 |                 result = BackTrackPart; | 
 |             else if (thumbRect.contains(mousePosition)) | 
 |                 result = ThumbPart; | 
 |             else | 
 |                 result = ForwardTrackPart; | 
 |         } | 
 |     } | 
 |     return result; | 
 | } | 
 |  | 
 | bool PlatformScrollbar::handleMouseMoveEvent(const PlatformMouseEvent& evt) | 
 | { | 
 |     if (m_pressedPart == ThumbPart) { | 
 |         // Drag the thumb. | 
 |         int thumbPos = thumbPosition(); | 
 |         int thumbLen = thumbLength(); | 
 |         int trackLen = trackLength(); | 
 |         int maxPos = trackLen - thumbLen; | 
 |         int delta = 0; | 
 |         if (m_orientation == HorizontalScrollbar) | 
 |             delta = convertFromContainingWindow(evt.pos()).x() - m_pressedPos; | 
 |         else | 
 |             delta = convertFromContainingWindow(evt.pos()).y() - m_pressedPos; | 
 |  | 
 |         if (delta > 0) | 
 |             // The mouse moved down/right. | 
 |             delta = min(maxPos - thumbPos, delta); | 
 |         else if (delta < 0) | 
 |             // The mouse moved up/left. | 
 |             delta = max(-thumbPos, delta); | 
 |  | 
 |         if (delta != 0) { | 
 |             setValue((float)(thumbPos + delta) * (m_totalSize - m_visibleSize) / (trackLen - thumbLen)); | 
 |             m_pressedPos += thumbPosition() - thumbPos; | 
 |         } | 
 |          | 
 |         return true; | 
 |     } | 
 |  | 
 |     if (m_pressedPart != NoPart) | 
 |         m_pressedPos = (m_orientation == HorizontalScrollbar ? convertFromContainingWindow(evt.pos()).x() : convertFromContainingWindow(evt.pos()).y()); | 
 |  | 
 |     ScrollbarPart part = hitTest(evt);     | 
 |     if (part != m_hoveredPart) { | 
 |         if (m_pressedPart != NoPart) { | 
 |             if (part == m_pressedPart) { | 
 |                 // The mouse is moving back over the pressed part.  We | 
 |                 // need to start up the timer action again. | 
 |                 startTimerIfNeeded(cNormalTimerDelay); | 
 |                 invalidatePart(m_pressedPart); | 
 |             } else if (m_hoveredPart == m_pressedPart) { | 
 |                 // The mouse is leaving the pressed part.  Kill our timer | 
 |                 // if needed. | 
 |                 stopTimerIfNeeded(); | 
 |                 invalidatePart(m_pressedPart); | 
 |             } | 
 |         } else { | 
 |             invalidatePart(part); | 
 |             invalidatePart(m_hoveredPart); | 
 |         } | 
 |         m_hoveredPart = part; | 
 |     }  | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | bool PlatformScrollbar::handleMouseOutEvent(const PlatformMouseEvent& evt) | 
 | { | 
 |     invalidatePart(m_hoveredPart); | 
 |     m_hoveredPart = NoPart; | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | bool PlatformScrollbar::handleMousePressEvent(const PlatformMouseEvent& evt) | 
 | { | 
 |     m_pressedPart = hitTest(evt); | 
 |     m_pressedPos = (m_orientation == HorizontalScrollbar ? convertFromContainingWindow(evt.pos()).x() : convertFromContainingWindow(evt.pos()).y()); | 
 |     invalidatePart(m_pressedPart); | 
 |     autoscrollPressedPart(cInitialTimerDelay); | 
 |     return true; | 
 | } | 
 |  | 
 | bool PlatformScrollbar::handleMouseReleaseEvent(const PlatformMouseEvent& evt) | 
 | { | 
 |     invalidatePart(m_pressedPart); | 
 |     m_pressedPart = NoPart; | 
 |     m_pressedPos = 0; | 
 |     stopTimerIfNeeded(); | 
 |  | 
 |     if (parent() && parent()->isFrameView()) | 
 |         static_cast<FrameView*>(parent())->frame()->eventHandler()->setMousePressed(false); | 
 |  | 
 |     return true; | 
 | } | 
 |  | 
 | void PlatformScrollbar::startTimerIfNeeded(double delay) | 
 | { | 
 |     // Don't do anything for the thumb. | 
 |     if (m_pressedPart == ThumbPart) | 
 |         return; | 
 |  | 
 |     // Handle the track.  We halt track scrolling once the thumb is level | 
 |     // with us. | 
 |     if ((m_pressedPart == BackTrackPart || m_pressedPart == ForwardTrackPart) && thumbUnderMouse()) { | 
 |         invalidatePart(m_pressedPart); | 
 |         m_hoveredPart = ThumbPart; | 
 |         return; | 
 |     } | 
 |  | 
 |     // We can't scroll if we've hit the beginning or end. | 
 |     ScrollDirection dir = pressedPartScrollDirection(); | 
 |     if (dir == ScrollUp || dir == ScrollLeft) { | 
 |         if (m_currentPos == 0) | 
 |             return; | 
 |     } else { | 
 |         if (m_currentPos == m_totalSize - m_visibleSize) | 
 |             return; | 
 |     } | 
 |  | 
 |     m_scrollTimer.startOneShot(delay); | 
 | } | 
 |  | 
 | void PlatformScrollbar::stopTimerIfNeeded() | 
 | { | 
 |     if (m_scrollTimer.isActive()) | 
 |         m_scrollTimer.stop(); | 
 | } | 
 |  | 
 | void PlatformScrollbar::autoscrollPressedPart(double delay) | 
 | { | 
 |     // Don't do anything for the thumb or if nothing was pressed. | 
 |     if (m_pressedPart == ThumbPart || m_pressedPart == NoPart) | 
 |         return; | 
 |  | 
 |     // Handle the track. | 
 |     if ((m_pressedPart == BackTrackPart || m_pressedPart == ForwardTrackPart) && thumbUnderMouse()) { | 
 |         invalidatePart(m_pressedPart); | 
 |         m_hoveredPart = ThumbPart; | 
 |         return; | 
 |     } | 
 |  | 
 |     // Handle the arrows and track. | 
 |     if (scroll(pressedPartScrollDirection(), pressedPartScrollGranularity())) | 
 |         startTimerIfNeeded(delay); | 
 | } | 
 |  | 
 | void PlatformScrollbar::autoscrollTimerFired(Timer<PlatformScrollbar>*) | 
 | { | 
 |     autoscrollPressedPart(cNormalTimerDelay); | 
 | } | 
 |  | 
 | ScrollDirection PlatformScrollbar::pressedPartScrollDirection() | 
 | { | 
 |     if (m_orientation == HorizontalScrollbar) { | 
 |         if (m_pressedPart == BackButtonPart || m_pressedPart == BackTrackPart) | 
 |             return ScrollLeft; | 
 |         return ScrollRight; | 
 |     } else { | 
 |         if (m_pressedPart == BackButtonPart || m_pressedPart == BackTrackPart) | 
 |             return ScrollUp; | 
 |         return ScrollDown; | 
 |     } | 
 | } | 
 |  | 
 | ScrollGranularity PlatformScrollbar::pressedPartScrollGranularity() | 
 | { | 
 |     if (m_pressedPart == BackButtonPart || m_pressedPart == ForwardButtonPart) | 
 |         return ScrollByLine; | 
 |     return ScrollByPage; | 
 | } | 
 |  | 
 | bool PlatformScrollbar::thumbUnderMouse() | 
 | { | 
 |     // Construct a rect. | 
 |     IntRect thumb = thumbRect(); | 
 |     thumb.move(-x(), -y()); | 
 |     int begin = (m_orientation == HorizontalScrollbar) ? thumb.x() : thumb.y(); | 
 |     int end = (m_orientation == HorizontalScrollbar) ? thumb.right() : thumb.bottom(); | 
 |     return (begin <= m_pressedPos && m_pressedPos < end); | 
 | } | 
 |  | 
 | int PlatformScrollbar::horizontalScrollbarHeight(ScrollbarControlSize controlSize) | 
 | { | 
 |     return cHorizontalWidth[controlSize]; | 
 | } | 
 |  | 
 | int PlatformScrollbar::verticalScrollbarWidth(ScrollbarControlSize controlSize) | 
 | { | 
 |     return cVerticalHeight[controlSize]; | 
 | } | 
 |  | 
 | IntRect PlatformScrollbar::windowClipRect() const | 
 | { | 
 |     IntRect clipRect(0, 0, width(), height()); | 
 |     clipRect = convertToContainingWindow(clipRect); | 
 |     if (m_client) | 
 |         clipRect.intersect(m_client->windowClipRect()); | 
 |     return clipRect; | 
 | } | 
 |  | 
 | void PlatformScrollbar::paintGripper(HDC hdc, const IntRect& rect) const | 
 | { | 
 | } | 
 |  | 
 | IntRect PlatformScrollbar::gripperRect(const IntRect& thumbRect) const | 
 | { | 
 |     return IntRect(); | 
 | } | 
 |  | 
 | void PlatformScrollbar::themeChanged() | 
 | { | 
 | } | 
 |  | 
 | } | 
 |  | 
 | #endif // defined(USE_SAFARI_THEME) |