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