1/*
2 * Copyright (C) 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 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 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 "ScrollbarThemeGtk.h"
28
29#ifndef GTK_API_VERSION_2
30
31#include "PlatformContextCairo.h"
32#include "PlatformMouseEvent.h"
33#include "RenderThemeGtk.h"
34#include "ScrollView.h"
35#include "Scrollbar.h"
36#include <gtk/gtk.h>
37
38namespace WebCore {
39
40static void gtkStyleChangedCallback(GtkWidget*, ScrollbarThemeGtk* scrollbarTheme)
41{
42    scrollbarTheme->updateThemeProperties();
43}
44
45ScrollbarThemeGtk::ScrollbarThemeGtk()
46    : m_context(static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->gtkScrollbarStyle())
47{
48    updateThemeProperties();
49    g_signal_connect(m_context, "changed", G_CALLBACK(gtkStyleChangedCallback), this);
50}
51
52void ScrollbarThemeGtk::updateThemeProperties()
53{
54    gtk_style_context_get_style(m_context,
55                                "min-slider-length", &m_minThumbLength,
56                                "slider-width", &m_thumbFatness,
57                                "trough-border", &m_troughBorderWidth,
58                                "stepper-size", &m_stepperSize,
59                                "stepper-spacing", &m_stepperSpacing,
60                                "trough-under-steppers", &m_troughUnderSteppers,
61                                "has-backward-stepper", &m_hasBackButtonStartPart,
62                                "has-forward-stepper", &m_hasForwardButtonEndPart,
63                                "has-secondary-backward-stepper", &m_hasBackButtonEndPart,
64                                "has-secondary-forward-stepper", &m_hasForwardButtonStartPart,
65                                NULL);
66    updateScrollbarsFrameThickness();
67}
68
69static void applyScrollbarStyleContextClasses(GtkStyleContext* context, ScrollbarOrientation orientation)
70{
71    gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCROLLBAR);
72    gtk_style_context_add_class(context, orientation == VerticalScrollbar ?  GTK_STYLE_CLASS_VERTICAL : GTK_STYLE_CLASS_HORIZONTAL);
73}
74
75void ScrollbarThemeGtk::paintTrackBackground(GraphicsContext* context, ScrollbarThemeClient* scrollbar, const IntRect& rect)
76{
77    // Paint the track background. If the trough-under-steppers property is true, this
78    // should be the full size of the scrollbar, but if is false, it should only be the
79    // track rect.
80    IntRect fullScrollbarRect(rect);
81    if (m_troughUnderSteppers)
82        fullScrollbarRect = IntRect(scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height());
83
84    gtk_style_context_save(m_context);
85
86    applyScrollbarStyleContextClasses(m_context, scrollbar->orientation());
87    gtk_style_context_add_class(m_context, GTK_STYLE_CLASS_TROUGH);
88
89    gtk_render_background(m_context, context->platformContext()->cr(),
90                          fullScrollbarRect.x(), fullScrollbarRect.y(), fullScrollbarRect.width(), fullScrollbarRect.height());
91    gtk_render_frame(m_context, context->platformContext()->cr(),
92                     fullScrollbarRect.x(), fullScrollbarRect.y(), fullScrollbarRect.width(), fullScrollbarRect.height());
93
94    gtk_style_context_restore(m_context);
95}
96
97void ScrollbarThemeGtk::paintScrollbarBackground(GraphicsContext* context, ScrollbarThemeClient* scrollbar)
98{
99    gtk_style_context_save(m_context);
100
101    applyScrollbarStyleContextClasses(m_context, scrollbar->orientation());
102    gtk_style_context_add_class(m_context, "scrolled-window");
103    gtk_render_frame(m_context, context->platformContext()->cr(), scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height());
104
105    gtk_style_context_restore(m_context);
106}
107
108void ScrollbarThemeGtk::paintThumb(GraphicsContext* context, ScrollbarThemeClient* scrollbar, const IntRect& rect)
109{
110    gtk_style_context_save(m_context);
111
112    ScrollbarOrientation orientation = scrollbar->orientation();
113    applyScrollbarStyleContextClasses(m_context, orientation);
114    gtk_style_context_add_class(m_context, GTK_STYLE_CLASS_SLIDER);
115
116    guint flags = 0;
117    if (scrollbar->pressedPart() == ThumbPart)
118        flags |= GTK_STATE_FLAG_ACTIVE;
119    if (scrollbar->hoveredPart() == ThumbPart)
120        flags |= GTK_STATE_FLAG_PRELIGHT;
121    gtk_style_context_set_state(m_context, static_cast<GtkStateFlags>(flags));
122
123    gtk_render_slider(m_context, context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
124                      orientation == VerticalScrollbar ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL);
125
126    gtk_style_context_restore(m_context);
127}
128
129void ScrollbarThemeGtk::paintButton(GraphicsContext* context, ScrollbarThemeClient* scrollbar, const IntRect& rect, ScrollbarPart part)
130{
131    gtk_style_context_save(m_context);
132
133    ScrollbarOrientation orientation = scrollbar->orientation();
134    applyScrollbarStyleContextClasses(m_context, orientation);
135
136    guint flags = 0;
137    if ((BackButtonStartPart == part && scrollbar->currentPos())
138        || (BackButtonEndPart == part && scrollbar->currentPos())
139        || (ForwardButtonEndPart == part && scrollbar->currentPos() != scrollbar->maximum())
140        || (ForwardButtonStartPart == part && scrollbar->currentPos() != scrollbar->maximum())) {
141        if (part == scrollbar->pressedPart())
142            flags |= GTK_STATE_FLAG_ACTIVE;
143        if (part == scrollbar->hoveredPart())
144            flags |= GTK_STATE_FLAG_PRELIGHT;
145    } else
146        flags |= GTK_STATE_FLAG_INSENSITIVE;
147    gtk_style_context_set_state(m_context, static_cast<GtkStateFlags>(flags));
148
149    gtk_style_context_add_class(m_context, GTK_STYLE_CLASS_BUTTON);
150    gtk_render_background(m_context, context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
151    gtk_render_frame(m_context, context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
152
153    gfloat arrowScaling;
154    gtk_style_context_get_style(m_context, "arrow-scaling", &arrowScaling, NULL);
155
156    double arrowSize = std::min(rect.width(), rect.height()) * arrowScaling;
157    FloatPoint arrowPoint(rect.x() + (rect.width() - arrowSize) / 2,
158                          rect.y() + (rect.height() - arrowSize) / 2);
159
160    if (flags & GTK_STATE_FLAG_ACTIVE) {
161        gint arrowDisplacementX, arrowDisplacementY;
162        gtk_style_context_get_style(m_context,
163                                    "arrow-displacement-x", &arrowDisplacementX,
164                                    "arrow-displacement-y", &arrowDisplacementY,
165                                    NULL);
166        arrowPoint.move(arrowDisplacementX, arrowDisplacementY);
167    }
168
169    gdouble angle;
170    if (orientation == VerticalScrollbar) {
171        angle = (part == ForwardButtonEndPart || part == ForwardButtonStartPart) ? G_PI : 0;
172    } else {
173        angle = (part == ForwardButtonEndPart || part == ForwardButtonStartPart) ? G_PI / 2 : 3 * (G_PI / 2);
174    }
175
176    gtk_render_arrow(m_context, context->platformContext()->cr(), angle, arrowPoint.x(), arrowPoint.y(), arrowSize);
177
178    gtk_style_context_restore(m_context);
179}
180
181} // namespace WebCore
182
183#endif // !GTK_API_VERSION_2
184