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#ifdef GTK_API_VERSION_2
30
31#include "GtkVersioning.h"
32#include "PlatformMouseEvent.h"
33#include "RenderThemeGtk.h"
34#include "ScrollView.h"
35#include "Scrollbar.h"
36#include "WidgetRenderingContext.h"
37#include <gtk/gtk.h>
38
39namespace WebCore {
40
41static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, ScrollbarThemeGtk* scrollbarTheme)
42{
43    scrollbarTheme->updateThemeProperties();
44}
45
46ScrollbarThemeGtk::ScrollbarThemeGtk()
47{
48    updateThemeProperties();
49    g_signal_connect(static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->gtkHScrollbar(),
50         "style-set", G_CALLBACK(gtkStyleSetCallback), this);
51}
52
53void ScrollbarThemeGtk::updateThemeProperties()
54{
55    GtkWidget* scrollbar = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->gtkHScrollbar();
56    gtk_widget_style_get(scrollbar,
57                         "slider_width", &m_thumbFatness,
58                         "trough_border", &m_troughBorderWidth,
59                         "stepper-size", &m_stepperSize,
60                         "trough-under-steppers", &m_troughUnderSteppers,
61                         "has-backward-stepper", &m_hasBackButtonStartPart,
62                         "has-forward-stepper", &m_hasForwardButtonEndPart,
63                         "has-secondary-forward-stepper", &m_hasForwardButtonStartPart,
64                         "has-secondary-backward-stepper", &m_hasBackButtonEndPart, NULL);
65    m_minThumbLength = gtk_range_get_min_slider_size(GTK_RANGE(scrollbar));
66    updateScrollbarsFrameThickness();
67}
68
69static GtkWidget* getWidgetForScrollbar(ScrollbarThemeClient* scrollbar)
70{
71    RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get());
72    return scrollbar->orientation() == VerticalScrollbar ? theme->gtkVScrollbar() : theme->gtkHScrollbar();
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    WidgetRenderingContext widgetContext(context, fullScrollbarRect);
85    IntRect paintRect(IntPoint(), fullScrollbarRect.size());
86    widgetContext.gtkPaintBox(paintRect, getWidgetForScrollbar(scrollbar),
87                              GTK_STATE_ACTIVE, GTK_SHADOW_IN, "trough");
88}
89
90void ScrollbarThemeGtk::paintScrollbarBackground(GraphicsContext* context, ScrollbarThemeClient* scrollbar)
91{
92    IntRect fullScrollbarRect = IntRect(scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height());
93
94    WidgetRenderingContext widgetContext(context, fullScrollbarRect);
95    widgetContext.gtkPaintBox(fullScrollbarRect, getWidgetForScrollbar(scrollbar),
96                              GTK_STATE_NORMAL, GTK_SHADOW_IN, "scrolled_window");
97}
98
99void ScrollbarThemeGtk::paintThumb(GraphicsContext* context, ScrollbarThemeClient* scrollbar, const IntRect& rect)
100{
101    GtkWidget* widget = getWidgetForScrollbar(scrollbar);
102    gboolean activateSlider;
103    gtk_widget_style_get(widget, "activate-slider", &activateSlider, NULL);
104
105    GtkStateType stateType = GTK_STATE_NORMAL;
106    GtkShadowType shadowType = GTK_SHADOW_OUT;
107    if (activateSlider && scrollbar->pressedPart() == ThumbPart) {
108        stateType = GTK_STATE_ACTIVE;
109        shadowType = GTK_SHADOW_IN;
110    } else if (scrollbar->pressedPart() == ThumbPart || scrollbar->hoveredPart() == ThumbPart)
111        stateType = GTK_STATE_PRELIGHT;
112
113    // The adjustment controls the rendering of the scrollbar thumb. If it's not set
114    // properly the theme may not draw the thumb borders properly.
115    GtkAdjustment* adjustment = gtk_range_get_adjustment(GTK_RANGE(widget));
116    gtk_adjustment_set_value(adjustment, scrollbar->currentPos());
117    gtk_adjustment_set_lower(adjustment, 0);
118    gtk_adjustment_set_upper(adjustment, scrollbar->maximum());
119
120    GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL;
121    if (scrollbar->orientation() == VerticalScrollbar) {
122        gtk_adjustment_set_page_size(adjustment, rect.height());
123        orientation = GTK_ORIENTATION_VERTICAL;
124    } else
125        gtk_adjustment_set_page_size(adjustment, rect.width());
126
127    WidgetRenderingContext widgetContext(context, rect);
128    IntRect sliderRect(IntPoint(), rect.size());
129    widgetContext.gtkPaintSlider(sliderRect, widget, stateType, shadowType, "slider", orientation);
130}
131
132void ScrollbarThemeGtk::paintButton(GraphicsContext* context, ScrollbarThemeClient* scrollbar, const IntRect& rect, ScrollbarPart part)
133{
134    // The buttons will be disabled if the thumb is as the appropriate extreme.
135    GtkShadowType shadowType = GTK_SHADOW_OUT;
136    GtkStateType stateType = GTK_STATE_INSENSITIVE;
137    bool pressed = (part == scrollbar->pressedPart());
138
139    if ((BackButtonStartPart == part && scrollbar->currentPos())
140        || (BackButtonEndPart == part && scrollbar->currentPos())
141        || (ForwardButtonEndPart == part && scrollbar->currentPos() != scrollbar->maximum())
142        || (ForwardButtonStartPart == part && scrollbar->currentPos() != scrollbar->maximum())) {
143        stateType = GTK_STATE_NORMAL;
144        if (pressed) {
145            stateType = GTK_STATE_ACTIVE;
146            shadowType = GTK_SHADOW_IN;
147        } else if (part == scrollbar->hoveredPart())
148            stateType = GTK_STATE_PRELIGHT;
149    }
150
151    // Themes determine how to draw the button (which button to draw) based on the allocation
152    // of the widget. Where the target rect is in relation to the total widget allocation
153    // determines the button.
154    ScrollbarOrientation orientation = scrollbar->orientation();
155    int buttonSize = (orientation == VerticalScrollbar) ? rect.height() : rect.width();
156    int totalAllocation = buttonSize * 5; // One space for each button and one extra.
157    int buttonOffset = 0;
158    if (ForwardButtonStartPart == part)
159        buttonOffset = buttonSize;
160    else if (BackButtonEndPart == part)
161        buttonOffset = 3 * buttonSize;
162    else if (ForwardButtonEndPart == part)
163        buttonOffset = 4 * buttonSize;
164
165    // Now we want the allocation to be relative to the origin of the painted rect.
166    GtkWidget* widget = getWidgetForScrollbar(scrollbar);
167    GtkAllocation allocation;
168    gtk_widget_get_allocation(widget, &allocation);
169    allocation.x = allocation.y = 0;
170    allocation.width = rect.width();
171    allocation.height = rect.height();
172
173    if (orientation == VerticalScrollbar) {
174        allocation.height = totalAllocation;
175        allocation.y -= buttonOffset;
176    } else {
177        allocation.width = totalAllocation;
178        allocation.x -= buttonOffset;
179    }
180    gtk_widget_set_allocation(widget, &allocation);
181
182    const char* detail = orientation == VerticalScrollbar ? "vscrollbar" : "hscrollbar";
183    WidgetRenderingContext widgetContext(context, rect);
184
185    IntRect buttonRect(IntPoint(), rect.size());
186    widgetContext.gtkPaintBox(buttonRect, widget, stateType, shadowType, detail);
187
188    float arrowScaling;
189    gtk_widget_style_get(widget, "arrow-scaling", &arrowScaling, NULL);
190    IntSize arrowSize = rect.size();
191    arrowSize.scale(arrowScaling);
192    IntRect arrowRect(IntPoint(buttonRect.x() + (buttonRect.width() - arrowSize.width()) / 2,
193                               buttonRect.y() + (buttonRect.height() - arrowSize.height()) / 2),
194                      arrowSize);
195    if (pressed) {
196        int arrowDisplacementX, arrowDisplacementY;
197        gtk_widget_style_get(widget,
198                             "arrow-displacement-x", &arrowDisplacementX,
199                             "arrow-displacement-y", &arrowDisplacementY,
200                             NULL);
201        arrowRect.move(arrowDisplacementX, arrowDisplacementY);
202    }
203
204    GtkArrowType arrowType = GTK_ARROW_DOWN;
205    if (orientation == VerticalScrollbar) {
206        if (part == BackButtonEndPart || part == BackButtonStartPart)
207            arrowType = GTK_ARROW_UP;
208    } else if (orientation == HorizontalScrollbar) {
209        arrowType = GTK_ARROW_RIGHT;
210        if (part == BackButtonEndPart || part == BackButtonStartPart)
211            arrowType = GTK_ARROW_LEFT;
212    }
213    widgetContext.gtkPaintArrow(arrowRect, widget, stateType, shadowType, arrowType, detail);
214}
215
216} // namespace WebCore
217
218#endif // GTK_API_VERSION_2
219