1/*
2 * Copyright (C) 2007, 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2007 Staikos Computing Services Inc. <info@staikos.net>
4 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "ScrollbarThemeQt.h"
30
31#include "GraphicsContext.h"
32#include "PlatformMouseEvent.h"
33#include "RenderThemeQt.h"
34#include "Scrollbar.h"
35#include "ScrollView.h"
36
37#include <QApplication>
38#include <QDebug>
39#include <QPainter>
40#include <QStyle>
41#include <QStyleOptionSlider>
42#include <QMenu>
43
44namespace WebCore {
45
46ScrollbarTheme* ScrollbarTheme::nativeTheme()
47{
48    static ScrollbarThemeQt theme;
49    return &theme;
50}
51
52ScrollbarThemeQt::~ScrollbarThemeQt()
53{
54}
55
56static QStyle::SubControl scPart(const ScrollbarPart& part)
57{
58    switch (part) {
59        case NoPart:
60            return QStyle::SC_None;
61        case BackButtonStartPart:
62        case BackButtonEndPart:
63            return QStyle::SC_ScrollBarSubLine;
64        case BackTrackPart:
65            return QStyle::SC_ScrollBarSubPage;
66        case ThumbPart:
67            return QStyle::SC_ScrollBarSlider;
68        case ForwardTrackPart:
69            return QStyle::SC_ScrollBarAddPage;
70        case ForwardButtonStartPart:
71        case ForwardButtonEndPart:
72            return QStyle::SC_ScrollBarAddLine;
73    }
74
75    return QStyle::SC_None;
76}
77
78static ScrollbarPart scrollbarPart(const QStyle::SubControl& sc)
79{
80    switch (sc) {
81        case QStyle::SC_None:
82            return NoPart;
83        case QStyle::SC_ScrollBarSubLine:
84            return BackButtonStartPart;
85        case QStyle::SC_ScrollBarSubPage:
86            return BackTrackPart;
87        case QStyle::SC_ScrollBarSlider:
88            return ThumbPart;
89        case QStyle::SC_ScrollBarAddPage:
90            return ForwardTrackPart;
91        case QStyle::SC_ScrollBarAddLine:
92            return ForwardButtonStartPart;
93    }
94    return NoPart;
95}
96
97static QStyleOptionSlider* styleOptionSlider(Scrollbar* scrollbar, QWidget* widget = 0)
98{
99    static QStyleOptionSlider opt;
100    if (widget)
101        opt.initFrom(widget);
102    else
103        opt.state |= QStyle::State_Active;
104
105    opt.state &= ~QStyle::State_HasFocus;
106
107    opt.rect = scrollbar->frameRect();
108    if (scrollbar->enabled())
109        opt.state |= QStyle::State_Enabled;
110    if (scrollbar->controlSize() != RegularScrollbar)
111        opt.state |= QStyle::State_Mini;
112    opt.orientation = (scrollbar->orientation() == VerticalScrollbar) ? Qt::Vertical : Qt::Horizontal;
113    if (scrollbar->orientation() == HorizontalScrollbar)
114        opt.state |= QStyle::State_Horizontal;
115    opt.sliderValue = scrollbar->value();
116    opt.sliderPosition = opt.sliderValue;
117    opt.pageStep = scrollbar->visibleSize();
118    opt.singleStep = scrollbar->lineStep();
119    opt.minimum = 0;
120    opt.maximum = qMax(0, scrollbar->maximum());
121    ScrollbarPart pressedPart = scrollbar->pressedPart();
122    ScrollbarPart hoveredPart = scrollbar->hoveredPart();
123    if (pressedPart != NoPart) {
124        opt.activeSubControls = scPart(scrollbar->pressedPart());
125        if (pressedPart == BackButtonStartPart || pressedPart == ForwardButtonStartPart ||
126            pressedPart == BackButtonEndPart || pressedPart == ForwardButtonEndPart ||
127            pressedPart == ThumbPart)
128            opt.state |= QStyle::State_Sunken;
129    } else
130        opt.activeSubControls = scPart(hoveredPart);
131    if (hoveredPart != NoPart)
132        opt.state |= QStyle::State_MouseOver;
133    return &opt;
134}
135
136bool ScrollbarThemeQt::paint(Scrollbar* scrollbar, GraphicsContext* graphicsContext, const IntRect& damageRect)
137{
138    if (graphicsContext->updatingControlTints()) {
139       scrollbar->invalidateRect(damageRect);
140       return false;
141    }
142
143    StylePainter p(this, graphicsContext);
144    if (!p.isValid())
145      return true;
146
147    p.painter->save();
148    QStyleOptionSlider* opt = styleOptionSlider(scrollbar, p.widget);
149
150    p.painter->setClipRect(opt->rect.intersected(damageRect), Qt::IntersectClip);
151
152#ifdef Q_WS_MAC
153    p.drawComplexControl(QStyle::CC_ScrollBar, *opt);
154#else
155    const QPoint topLeft = opt->rect.topLeft();
156    p.painter->translate(topLeft);
157    opt->rect.moveTo(QPoint(0, 0));
158
159    // The QStyle expects the background to be already filled
160    p.painter->fillRect(opt->rect, opt->palette.background());
161
162    p.drawComplexControl(QStyle::CC_ScrollBar, *opt);
163    opt->rect.moveTo(topLeft);
164#endif
165    p.painter->restore();
166
167    return true;
168}
169
170ScrollbarPart ScrollbarThemeQt::hitTest(Scrollbar* scrollbar, const PlatformMouseEvent& evt)
171{
172    QStyleOptionSlider* opt = styleOptionSlider(scrollbar);
173    const QPoint pos = scrollbar->convertFromContainingWindow(evt.pos());
174    opt->rect.moveTo(QPoint(0, 0));
175    QStyle::SubControl sc = style()->hitTestComplexControl(QStyle::CC_ScrollBar, opt, pos, 0);
176    return scrollbarPart(sc);
177}
178
179bool ScrollbarThemeQt::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
180{
181    // Middle click centers slider thumb (if supported)
182    return style()->styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition) && evt.button() == MiddleButton;
183}
184
185void ScrollbarThemeQt::invalidatePart(Scrollbar* scrollbar, ScrollbarPart)
186{
187    // FIXME: Do more precise invalidation.
188    scrollbar->invalidate();
189}
190
191int ScrollbarThemeQt::scrollbarThickness(ScrollbarControlSize controlSize)
192{
193    QStyleOptionSlider o;
194    o.orientation = Qt::Vertical;
195    o.state &= ~QStyle::State_Horizontal;
196    if (controlSize != RegularScrollbar)
197        o.state |= QStyle::State_Mini;
198    return style()->pixelMetric(QStyle::PM_ScrollBarExtent, &o, 0);
199}
200
201int ScrollbarThemeQt::thumbPosition(Scrollbar* scrollbar)
202{
203    if (scrollbar->enabled())
204        return (int)((float)scrollbar->currentPos() * (trackLength(scrollbar) - thumbLength(scrollbar)) / scrollbar->maximum());
205    return 0;
206}
207
208int ScrollbarThemeQt::thumbLength(Scrollbar* scrollbar)
209{
210    QStyleOptionSlider* opt = styleOptionSlider(scrollbar);
211    IntRect thumb = style()->subControlRect(QStyle::CC_ScrollBar, opt, QStyle::SC_ScrollBarSlider, 0);
212    return scrollbar->orientation() == HorizontalScrollbar ? thumb.width() : thumb.height();
213}
214
215int ScrollbarThemeQt::trackPosition(Scrollbar* scrollbar)
216{
217    QStyleOptionSlider* opt = styleOptionSlider(scrollbar);
218    IntRect track = style()->subControlRect(QStyle::CC_ScrollBar, opt, QStyle::SC_ScrollBarGroove, 0);
219    return scrollbar->orientation() == HorizontalScrollbar ? track.x() - scrollbar->x() : track.y() - scrollbar->y();
220}
221
222int ScrollbarThemeQt::trackLength(Scrollbar* scrollbar)
223{
224    QStyleOptionSlider* opt = styleOptionSlider(scrollbar);
225    IntRect track = style()->subControlRect(QStyle::CC_ScrollBar, opt, QStyle::SC_ScrollBarGroove, 0);
226    return scrollbar->orientation() == HorizontalScrollbar ? track.width() : track.height();
227}
228
229void ScrollbarThemeQt::paintScrollCorner(ScrollView* scrollView, GraphicsContext* context, const IntRect& rect)
230{
231    if (context->updatingControlTints()) {
232       scrollView->invalidateRect(rect);
233       return;
234    }
235
236#if QT_VERSION < 0x040500
237    context->fillRect(rect, QApplication::palette().color(QPalette::Normal, QPalette::Window), DeviceColorSpace);
238#else
239    StylePainter p(this, context);
240    if (!p.isValid())
241        return;
242
243    QStyleOption option;
244    option.rect = rect;
245    p.drawPrimitive(QStyle::PE_PanelScrollAreaCorner, option);
246#endif
247}
248
249QStyle* ScrollbarThemeQt::style() const
250{
251    return QApplication::style();
252}
253
254}
255
256