fd49f7d01a2d7ac965a9966d9e82f68a51d7c161
[WebKit-https.git] / Source / WebCore / platform / gtk / RenderThemeGtk3.cpp
1 /*
2  * Copyright (C) 2007 Apple Inc.
3  * Copyright (C) 2007 Alp Toker <alp@atoker.com>
4  * Copyright (C) 2008 Collabora Ltd.
5  * Copyright (C) 2009 Kenneth Rohde Christiansen
6  * Copyright (C) 2010 Igalia S.L.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "RenderThemeGtk.h"
27
28 #ifndef GTK_API_VERSION_2
29
30 #include "CSSValueKeywords.h"
31 #include "GraphicsContext.h"
32 #include "GtkVersioning.h"
33 #include "HTMLNames.h"
34 #include "MediaControlElements.h"
35 #include "Page.h"
36 #include "PaintInfo.h"
37 #include "PlatformContextCairo.h"
38 #include "RenderElement.h"
39 #include "TextDirection.h"
40 #include "UserAgentStyleSheets.h"
41 #include <cmath>
42 #include <gdk/gdk.h>
43 #include <gtk/gtk.h>
44
45 namespace WebCore {
46
47 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c.
48 static const int minArrowSize = 15;
49 // This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c.
50 static const int minSpinButtonArrowSize = 6;
51
52 typedef HashMap<GType, GRefPtr<GtkStyleContext> > StyleContextMap;
53 static StyleContextMap& styleContextMap();
54
55 static void gtkStyleChangedCallback(GObject*, GParamSpec*)
56 {
57     StyleContextMap::const_iterator end = styleContextMap().end();
58     for (StyleContextMap::const_iterator iter = styleContextMap().begin(); iter != end; ++iter)
59         gtk_style_context_invalidate(iter->value.get());
60
61     Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
62 }
63
64 static StyleContextMap& styleContextMap()
65 {
66     DEPRECATED_DEFINE_STATIC_LOCAL(StyleContextMap, map, ());
67
68     static bool initialized = false;
69     if (!initialized) {
70         GtkSettings* settings = gtk_settings_get_default();
71         g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(gtkStyleChangedCallback), 0);
72         g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(gtkStyleChangedCallback), 0);
73         initialized = true;
74     }
75     return map;
76 }
77
78 static GtkStyleContext* getStyleContext(GType widgetType)
79 {
80     StyleContextMap::AddResult result = styleContextMap().add(widgetType, nullptr);
81     if (!result.isNewEntry)
82         return result.iterator->value.get();
83
84     GtkWidgetPath* path = gtk_widget_path_new();
85     gtk_widget_path_append_type(path, widgetType);
86
87     if (widgetType == GTK_TYPE_SCROLLBAR)
88         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCROLLBAR);
89     else if (widgetType == GTK_TYPE_ENTRY)
90         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_ENTRY);
91     else if (widgetType == GTK_TYPE_ARROW)
92         gtk_widget_path_iter_add_class(path, 0, "arrow");
93     else if (widgetType == GTK_TYPE_BUTTON) {
94         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_BUTTON);
95         gtk_widget_path_iter_add_class(path, 1, "text-button");
96     }
97     else if (widgetType == GTK_TYPE_SCALE)
98         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCALE);
99     else if (widgetType == GTK_TYPE_SEPARATOR)
100         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SEPARATOR);
101     else if (widgetType == GTK_TYPE_PROGRESS_BAR)
102         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_PROGRESSBAR);
103     else if (widgetType == GTK_TYPE_SPIN_BUTTON)
104         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SPINBUTTON);
105     else if (widgetType == GTK_TYPE_TREE_VIEW)
106         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_VIEW);
107     else if (widgetType == GTK_TYPE_CHECK_BUTTON)
108         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_CHECK);
109     else if (widgetType == GTK_TYPE_RADIO_BUTTON)
110         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_RADIO);
111
112     GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
113     gtk_style_context_set_path(context.get(), path);
114     gtk_widget_path_free(path);
115
116     result.iterator->value = context;
117     return context.get();
118 }
119
120 GtkStyleContext* RenderThemeGtk::gtkScrollbarStyle()
121 {
122     return getStyleContext(GTK_TYPE_SCROLLBAR);
123 }
124
125 // This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h.
126 extern GtkTextDirection gtkTextDirection(TextDirection);
127
128 void RenderThemeGtk::platformInit()
129 {
130 }
131
132 RenderThemeGtk::~RenderThemeGtk()
133 {
134 }
135
136 #if ENABLE(VIDEO)
137 void RenderThemeGtk::initMediaColors()
138 {
139     GdkRGBA color;
140     GtkStyleContext* containerContext = getStyleContext(GTK_TYPE_CONTAINER);
141
142     gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_NORMAL, &color);
143     m_panelColor = color;
144     gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_ACTIVE, &color);
145     m_sliderColor = color;
146     gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_SELECTED, &color);
147     m_sliderThumbColor = color;
148 }
149 #endif
150
151 static void adjustRectForFocus(GtkStyleContext* context, FloatRect& rect)
152 {
153     gint focusWidth, focusPad;
154     gtk_style_context_get_style(context,
155                                 "focus-line-width", &focusWidth,
156                                 "focus-padding", &focusPad, NULL);
157     rect.inflate(focusWidth + focusPad);
158 }
159
160 void RenderThemeGtk::adjustRepaintRect(const RenderObject& renderObject, FloatRect& rect)
161 {
162     GtkStyleContext* context = 0;
163     bool checkInteriorFocus = false;
164     ControlPart part = renderObject.style().appearance();
165     switch (part) {
166     case CheckboxPart:
167     case RadioPart:
168         context = getStyleContext(part == CheckboxPart ? GTK_TYPE_CHECK_BUTTON : GTK_TYPE_RADIO_BUTTON);
169
170         gint indicatorSpacing;
171         gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL);
172         rect.inflate(indicatorSpacing);
173
174         return;
175     case SliderVerticalPart:
176     case SliderHorizontalPart:
177         context = getStyleContext(GTK_TYPE_SCALE);
178         break;
179     case ButtonPart:
180     case MenulistButtonPart:
181     case MenulistPart:
182         context = getStyleContext(GTK_TYPE_BUTTON);
183         checkInteriorFocus = true;
184         break;
185     case TextFieldPart:
186     case TextAreaPart:
187         context = getStyleContext(GTK_TYPE_ENTRY);
188         checkInteriorFocus = true;
189         break;
190     default:
191         return;
192     }
193
194     ASSERT(context);
195     if (checkInteriorFocus) {
196         gboolean interiorFocus;
197         gtk_style_context_get_style(context, "interior-focus", &interiorFocus, NULL);
198         if (interiorFocus)
199             return;
200     }
201     adjustRectForFocus(context, rect);
202 }
203
204 static void setToggleSize(GtkStyleContext* context, RenderStyle* style)
205 {
206     // The width and height are both specified, so we shouldn't change them.
207     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
208         return;
209
210     // Other ports hard-code this to 13 which is also the default value defined by GTK+.
211     // GTK+ users tend to demand the native look.
212     // It could be made a configuration option values other than 13 actually break site compatibility.
213     gint indicatorSize;
214     gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL);
215
216     if (style->width().isIntrinsicOrAuto())
217         style->setWidth(Length(indicatorSize, Fixed));
218
219     if (style->height().isAuto())
220         style->setHeight(Length(indicatorSize, Fixed));
221 }
222
223 static void paintToggle(const RenderThemeGtk* theme, GType widgetType, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& fullRect)
224 {
225     GtkStyleContext* context = getStyleContext(widgetType);
226     gtk_style_context_save(context);
227
228     // Some themes do not render large toggle buttons properly, so we simply
229     // shrink the rectangle back down to the default size and then center it
230     // in the full toggle button region. The reason for not simply forcing toggle
231     // buttons to be a smaller size is that we don't want to break site layouts.
232     gint indicatorSize;
233     gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL);
234     IntRect rect(fullRect);
235     if (rect.width() > indicatorSize) {
236         rect.inflateX(-(rect.width() - indicatorSize) / 2);
237         rect.setWidth(indicatorSize); // In case rect.width() was equal to indicatorSize + 1.
238     }
239
240     if (rect.height() > indicatorSize) {
241         rect.inflateY(-(rect.height() - indicatorSize) / 2);
242         rect.setHeight(indicatorSize); // In case rect.height() was equal to indicatorSize + 1.
243     }
244
245     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
246     gtk_style_context_add_class(context, widgetType == GTK_TYPE_CHECK_BUTTON ? GTK_STYLE_CLASS_CHECK : GTK_STYLE_CLASS_RADIO);
247
248     guint flags = 0;
249     if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject))
250         flags |= GTK_STATE_FLAG_INSENSITIVE;
251     else if (theme->isHovered(renderObject))
252         flags |= GTK_STATE_FLAG_PRELIGHT;
253     if (theme->isIndeterminate(renderObject))
254         flags |= GTK_STATE_FLAG_INCONSISTENT;
255     else if (theme->isChecked(renderObject))
256         flags |= GTK_STATE_FLAG_ACTIVE;
257     if (theme->isPressed(renderObject))
258         flags |= GTK_STATE_FLAG_SELECTED;
259     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
260
261     if (widgetType == GTK_TYPE_CHECK_BUTTON)
262         gtk_render_check(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
263     else
264         gtk_render_option(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
265
266     if (theme->isFocused(renderObject)) {
267         IntRect indicatorRect(rect);
268         gint indicatorSpacing;
269         gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL);
270         indicatorRect.inflate(indicatorSpacing);
271         gtk_render_focus(context, paintInfo.context->platformContext()->cr(), indicatorRect.x(), indicatorRect.y(),
272                          indicatorRect.width(), indicatorRect.height());
273     }
274
275     gtk_style_context_restore(context);
276 }
277
278 void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
279 {
280     setToggleSize(getStyleContext(GTK_TYPE_CHECK_BUTTON), style);
281 }
282
283 bool RenderThemeGtk::paintCheckbox(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
284 {
285     paintToggle(this, GTK_TYPE_CHECK_BUTTON, renderObject, paintInfo, rect);
286     return false;
287 }
288
289 void RenderThemeGtk::setRadioSize(RenderStyle* style) const
290 {
291     setToggleSize(getStyleContext(GTK_TYPE_RADIO_BUTTON), style);
292 }
293
294 bool RenderThemeGtk::paintRadio(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
295 {
296     paintToggle(this, GTK_TYPE_RADIO_BUTTON, renderObject, paintInfo, rect);
297     return false;
298 }
299
300 static void renderButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
301 {
302     IntRect buttonRect(rect);
303
304     guint flags = 0;
305     if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject))
306         flags |= GTK_STATE_FLAG_INSENSITIVE;
307     else if (theme->isHovered(renderObject))
308         flags |= GTK_STATE_FLAG_PRELIGHT;
309     if (theme->isPressed(renderObject))
310         flags |= GTK_STATE_FLAG_ACTIVE;
311     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
312
313     if (theme->isDefault(renderObject)) {
314         GtkBorder* borderPtr = 0;
315         GtkBorder border = { 1, 1, 1, 1 };
316
317         gtk_style_context_get_style(context, "default-border", &borderPtr, NULL);
318         if (borderPtr) {
319             border = *borderPtr;
320             gtk_border_free(borderPtr);
321         }
322
323         buttonRect.move(border.left, border.top);
324         buttonRect.setWidth(buttonRect.width() - (border.left + border.right));
325         buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom));
326
327         gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT);
328     }
329
330     gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
331     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
332
333     if (theme->isFocused(renderObject)) {
334         gint focusWidth, focusPad;
335         gboolean displaceFocus, interiorFocus;
336         gtk_style_context_get_style(context,
337                                     "focus-line-width", &focusWidth,
338                                     "focus-padding", &focusPad,
339                                     "interior-focus", &interiorFocus,
340                                     "displace-focus", &displaceFocus,
341                                     NULL);
342
343         if (interiorFocus) {
344             GtkBorder borderWidth;
345             gtk_style_context_get_border(context, static_cast<GtkStateFlags>(flags), &borderWidth);
346
347             buttonRect = IntRect(buttonRect.x() + borderWidth.left + focusPad, buttonRect.y() + borderWidth.top + focusPad,
348                                  buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right),
349                                  buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom));
350         } else
351             buttonRect.inflate(focusWidth + focusPad);
352
353         if (displaceFocus && theme->isPressed(renderObject)) {
354             gint childDisplacementX;
355             gint childDisplacementY;
356             gtk_style_context_get_style(context,
357                                         "child-displacement-x", &childDisplacementX,
358                                         "child-displacement-y", &childDisplacementY,
359                                         NULL);
360             buttonRect.move(childDisplacementX, childDisplacementY);
361         }
362
363         gtk_render_focus(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
364     }
365 }
366 bool RenderThemeGtk::paintButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
367 {
368     GtkStyleContext* context = getStyleContext(GTK_TYPE_BUTTON);
369     gtk_style_context_save(context);
370
371     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
372     gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
373
374     renderButton(this, context, renderObject, paintInfo, rect);
375
376     gtk_style_context_restore(context);
377
378     return false;
379 }
380
381 static void getComboBoxMetrics(RenderStyle* style, GtkBorder& border, int& focus, int& separator)
382 {
383     // If this menu list button isn't drawn using the native theme, we
384     // don't add any extra padding beyond what WebCore already uses.
385     if (style->appearance() == NoControlPart)
386         return;
387
388     GtkStyleContext* context = getStyleContext(GTK_TYPE_COMBO_BOX);
389     gtk_style_context_save(context);
390
391     gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
392     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(style->direction())));
393
394     gtk_style_context_get_border(context, static_cast<GtkStateFlags>(0), &border);
395
396     gboolean interiorFocus;
397     gint focusWidth, focusPad;
398     gtk_style_context_get_style(context,
399                                 "interior-focus", &interiorFocus,
400                                 "focus-line-width", &focusWidth,
401                                 "focus-padding", &focusPad, NULL);
402     focus = interiorFocus ? focusWidth + focusPad : 0;
403
404     gtk_style_context_restore(context);
405
406     context = getStyleContext(GTK_TYPE_SEPARATOR);
407     gtk_style_context_save(context);
408
409     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(style->direction()));
410     gtk_style_context_set_direction(context, direction);
411     gtk_style_context_add_class(context, "separator");
412
413     gboolean wideSeparators;
414     gint separatorWidth;
415     gtk_style_context_get_style(context,
416                                 "wide-separators", &wideSeparators,
417                                 "separator-width", &separatorWidth,
418                                 NULL);
419
420     // GTK+ always uses border.left, regardless of text direction. See gtkseperator.c.
421     if (!wideSeparators)
422         separatorWidth = border.left;
423
424     separator = separatorWidth;
425
426     gtk_style_context_restore(context);
427 }
428
429 int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const
430 {
431     GtkBorder borderWidth = { 0, 0, 0, 0 };
432     int focusWidth = 0, separatorWidth = 0;
433     getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
434     int left = borderWidth.left + focusWidth;
435     if (style->direction() == RTL)
436         left += separatorWidth + minArrowSize;
437     return left;
438 }
439
440 int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const
441 {
442     GtkBorder borderWidth = { 0, 0, 0, 0 };
443     int focusWidth = 0, separatorWidth = 0;
444     getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
445     int right = borderWidth.right + focusWidth;
446     if (style->direction() == LTR)
447         right += separatorWidth + minArrowSize;
448     return right;
449 }
450
451 int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const
452 {
453     GtkBorder borderWidth = { 0, 0, 0, 0 };
454     int focusWidth = 0, separatorWidth = 0;
455     getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
456     return borderWidth.top + focusWidth;
457 }
458
459 int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const
460 {
461     GtkBorder borderWidth = { 0, 0, 0, 0 };
462     int focusWidth = 0, separatorWidth = 0;
463     getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
464     return borderWidth.bottom + focusWidth;
465 }
466
467 bool RenderThemeGtk::paintMenuList(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& r)
468 {
469     // FIXME: adopt subpixel themes.
470     IntRect rect = IntRect(r);   
471
472     cairo_t* cairoContext = paintInfo.context->platformContext()->cr();
473     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
474
475     // Paint the button.
476     GtkStyleContext* buttonStyleContext = getStyleContext(GTK_TYPE_BUTTON);
477     gtk_style_context_save(buttonStyleContext);
478     gtk_style_context_set_direction(buttonStyleContext, direction);
479     gtk_style_context_add_class(buttonStyleContext, GTK_STYLE_CLASS_BUTTON);
480     renderButton(this, buttonStyleContext, renderObject, paintInfo, rect);
481
482     // Get the inner rectangle.
483     gint focusWidth, focusPad;
484     GtkBorder* innerBorderPtr = 0;
485     GtkBorder innerBorder = { 1, 1, 1, 1 };
486     gtk_style_context_get_style(buttonStyleContext,
487                                 "inner-border", &innerBorderPtr,
488                                 "focus-line-width", &focusWidth,
489                                 "focus-padding", &focusPad,
490                                 NULL);
491     if (innerBorderPtr) {
492         innerBorder = *innerBorderPtr;
493         gtk_border_free(innerBorderPtr);
494     }
495
496     GtkBorder borderWidth;
497     GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext);
498     gtk_style_context_get_border(buttonStyleContext, state, &borderWidth);
499
500     focusWidth += focusPad;
501     IntRect innerRect(rect.x() + innerBorder.left + borderWidth.left + focusWidth,
502                       rect.y() + innerBorder.top + borderWidth.top + focusWidth,
503                       rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth),
504                       rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth));
505
506     if (isPressed(renderObject)) {
507         gint childDisplacementX;
508         gint childDisplacementY;
509         gtk_style_context_get_style(buttonStyleContext,
510                                     "child-displacement-x", &childDisplacementX,
511                                     "child-displacement-y", &childDisplacementY,
512                                     NULL);
513         innerRect.move(childDisplacementX, childDisplacementY);
514     }
515     innerRect.setWidth(std::max(1, innerRect.width()));
516     innerRect.setHeight(std::max(1, innerRect.height()));
517
518     gtk_style_context_restore(buttonStyleContext);
519
520     // Paint the arrow.
521     GtkStyleContext* arrowStyleContext = getStyleContext(GTK_TYPE_ARROW);
522     gtk_style_context_save(arrowStyleContext);
523
524     gtk_style_context_set_direction(arrowStyleContext, direction);
525     gtk_style_context_add_class(arrowStyleContext, "arrow");
526     gtk_style_context_add_class(arrowStyleContext, GTK_STYLE_CLASS_BUTTON);
527
528     gfloat arrowScaling;
529     gtk_style_context_get_style(arrowStyleContext, "arrow-scaling", &arrowScaling, NULL);
530
531     IntSize arrowSize(minArrowSize, innerRect.height());
532     FloatPoint arrowPosition(innerRect.location());
533     if (direction == GTK_TEXT_DIR_LTR)
534         arrowPosition.move(innerRect.width() - arrowSize.width(), 0);
535
536     // GTK+ actually fetches the xalign and valign values from the widget, but since we
537     // don't have a widget here, we are just using the default xalign and valign values of 0.5.
538     gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling;
539     arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2);
540
541     gtk_style_context_set_state(arrowStyleContext, state);
542     gtk_render_arrow(arrowStyleContext, cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
543
544     gtk_style_context_restore(arrowStyleContext);
545
546     // Paint the separator if needed.
547     GtkStyleContext* separatorStyleContext = getStyleContext(GTK_TYPE_COMBO_BOX);
548     gtk_style_context_save(separatorStyleContext);
549
550     gtk_style_context_set_direction(separatorStyleContext, direction);
551     gtk_style_context_add_class(separatorStyleContext, "separator");
552
553     gboolean wideSeparators;
554     gint separatorWidth;
555     gtk_style_context_get_style(separatorStyleContext,
556                                 "wide-separators", &wideSeparators,
557                                 "separator-width", &separatorWidth,
558                                 NULL);
559     if (wideSeparators && !separatorWidth) {
560         gtk_style_context_restore(separatorStyleContext);
561         return false;
562     }
563
564     gtk_style_context_set_state(separatorStyleContext, state);
565     IntPoint separatorPosition(arrowPosition.x(), innerRect.y());
566     if (wideSeparators) {
567         if (direction == GTK_TEXT_DIR_LTR)
568             separatorPosition.move(-separatorWidth, 0);
569         else
570             separatorPosition.move(arrowSize.width(), 0);
571
572         gtk_render_frame(separatorStyleContext, cairoContext,
573                          separatorPosition.x(), separatorPosition.y(),
574                          separatorWidth, innerRect.height());
575     } else {
576         GtkBorder padding;
577         gtk_style_context_get_padding(separatorStyleContext, state, &padding);
578         GtkBorder border;
579         gtk_style_context_get_border(separatorStyleContext, state, &border);
580
581         if (direction == GTK_TEXT_DIR_LTR)
582             separatorPosition.move(-(padding.left + border.left), 0);
583         else
584             separatorPosition.move(arrowSize.width(), 0);
585
586         cairo_save(cairoContext);
587
588         // An extra clip prevents the separator bleeding outside of the specified rectangle because of subpixel positioning.
589         cairo_rectangle(cairoContext, separatorPosition.x(), separatorPosition.y(), border.left, innerRect.height());
590         cairo_clip(cairoContext);
591         gtk_render_line(separatorStyleContext, cairoContext,
592                         separatorPosition.x(), separatorPosition.y(),
593                         separatorPosition.x(), innerRect.maxY());
594         cairo_restore(cairoContext);
595     }
596
597     gtk_style_context_restore(separatorStyleContext);
598     return false;
599 }
600
601 bool RenderThemeGtk::paintTextField(const RenderObject& renderObject, const PaintInfo& paintInfo, const FloatRect& rect)
602 {
603     GtkStyleContext* context = getStyleContext(GTK_TYPE_ENTRY);
604     gtk_style_context_save(context);
605
606     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction())));
607     gtk_style_context_add_class(context, GTK_STYLE_CLASS_ENTRY);
608
609     guint flags = 0;
610     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
611         flags |= GTK_STATE_FLAG_INSENSITIVE;
612     else if (isFocused(renderObject))
613         flags |= GTK_STATE_FLAG_FOCUSED;
614     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
615
616     gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
617     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
618
619     if (isFocused(renderObject) && isEnabled(renderObject)) {
620         gboolean interiorFocus;
621         gint focusWidth, focusPad;
622         gtk_style_context_get_style(context,
623                                     "interior-focus", &interiorFocus,
624                                     "focus-line-width", &focusWidth,
625                                     "focus-padding", &focusPad,
626                                     NULL);
627         if (!interiorFocus) {
628             IntRect focusRect(rect);
629             focusRect.inflate(focusWidth + focusPad);
630             gtk_render_focus(context, paintInfo.context->platformContext()->cr(),
631                              focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
632         }
633     }
634
635     gtk_style_context_restore(context);
636
637     return false;
638 }
639
640 static void applySliderStyleContextClasses(GtkStyleContext* context, ControlPart part)
641 {
642     gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE);
643     if (part == SliderHorizontalPart || part == SliderThumbHorizontalPart)
644         gtk_style_context_add_class(context, GTK_STYLE_CLASS_HORIZONTAL);
645     else if (part == SliderVerticalPart || part == SliderThumbVerticalPart)
646         gtk_style_context_add_class(context, GTK_STYLE_CLASS_VERTICAL);
647 }
648
649 bool RenderThemeGtk::paintSliderTrack(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
650 {
651     ControlPart part = renderObject.style().appearance();
652     ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart);
653
654     GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
655     gtk_style_context_save(context);
656
657     gtk_style_context_set_direction(context, gtkTextDirection(renderObject.style().direction()));
658     applySliderStyleContextClasses(context, part);
659     gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
660
661     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
662         gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE);
663
664     gtk_render_background(context, paintInfo.context->platformContext()->cr(),
665                           rect.x(), rect.y(), rect.width(), rect.height());
666     gtk_render_frame(context, paintInfo.context->platformContext()->cr(),
667                      rect.x(), rect.y(), rect.width(), rect.height());
668
669     if (isFocused(renderObject)) {
670         gint focusWidth, focusPad;
671         gtk_style_context_get_style(context,
672                                     "focus-line-width", &focusWidth,
673                                     "focus-padding", &focusPad, NULL);
674         IntRect focusRect(rect);
675         focusRect.inflate(focusWidth + focusPad);
676         gtk_render_focus(context, paintInfo.context->platformContext()->cr(),
677                          focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
678     }
679
680     gtk_style_context_restore(context);
681     return false;
682 }
683
684 bool RenderThemeGtk::paintSliderThumb(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
685 {
686     ControlPart part = renderObject.style().appearance();
687     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
688
689     GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
690     gtk_style_context_save(context);
691
692     gtk_style_context_set_direction(context, gtkTextDirection(renderObject.style().direction()));
693     applySliderStyleContextClasses(context, part);
694     gtk_style_context_add_class(context, GTK_STYLE_CLASS_SLIDER);
695
696     guint flags = 0;
697     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
698         flags |= GTK_STATE_FLAG_INSENSITIVE;
699     else if (isHovered(renderObject))
700         flags |= GTK_STATE_FLAG_PRELIGHT;
701     if (isPressed(renderObject))
702         flags |= GTK_STATE_FLAG_ACTIVE;
703     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
704
705     gtk_render_slider(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
706                       part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
707
708     gtk_style_context_restore(context);
709
710     return false;
711 }
712
713 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle* style, Element*) const
714 {
715     ControlPart part = style->appearance();
716     if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart)
717         return;
718
719     gint sliderWidth, sliderLength;
720     gtk_style_context_get_style(getStyleContext(GTK_TYPE_SCALE),
721                                 "slider-width", &sliderWidth,
722                                 "slider-length", &sliderLength,
723                                 NULL);
724     if (part == SliderThumbHorizontalPart) {
725         style->setWidth(Length(sliderLength, Fixed));
726         style->setHeight(Length(sliderWidth, Fixed));
727         return;
728     }
729     ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
730     style->setWidth(Length(sliderWidth, Fixed));
731     style->setHeight(Length(sliderLength, Fixed));
732 }
733
734 bool RenderThemeGtk::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
735 {
736     if (!renderObject.isProgress())
737         return true;
738
739     GtkStyleContext* context = getStyleContext(GTK_TYPE_PROGRESS_BAR);
740     gtk_style_context_save(context);
741
742     gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
743
744     gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
745     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
746
747     gtk_style_context_restore(context);
748
749     gtk_style_context_save(context);
750     gtk_style_context_add_class(context, GTK_STYLE_CLASS_PROGRESSBAR);
751
752
753     GtkBorder padding;
754     gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
755     IntRect progressRect(rect.x() + padding.left, rect.y() + padding.top,
756                          rect.width() - (padding.left + padding.right),
757                          rect.height() - (padding.top + padding.bottom));
758     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
759
760     if (!progressRect.isEmpty())
761         gtk_render_activity(context, paintInfo.context->platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
762
763     gtk_style_context_restore(context);
764     return false;
765 }
766
767 static gint spinButtonArrowSize(GtkStyleContext* context)
768 {
769     PangoFontDescription* fontDescription;
770     gtk_style_context_get(context, static_cast<GtkStateFlags>(0), "font", &fontDescription, NULL);
771     gint fontSize = pango_font_description_get_size(fontDescription);
772     gint arrowSize = std::max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
773     pango_font_description_free(fontDescription);
774
775     return arrowSize - arrowSize % 2; // Force even.
776 }
777
778 void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
779 {
780     GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
781
782     GtkBorder padding;
783     gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
784
785     int width = spinButtonArrowSize(context) + padding.left + padding.right;
786     style->setWidth(Length(width, Fixed));
787     style->setMinWidth(Length(width, Fixed));
788 }
789
790 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
791 {
792     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
793
794     gtk_style_context_save(context);
795     gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
796
797     GtkTextDirection direction = gtk_style_context_get_direction(context);
798     guint state = static_cast<guint>(gtk_style_context_get_state(context));
799     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
800         if (theme->isPressed(renderObject)) {
801             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
802                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
803                 state |= GTK_STATE_FLAG_ACTIVE;
804         } else if (theme->isHovered(renderObject)) {
805             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
806                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
807                 state |= GTK_STATE_FLAG_PRELIGHT;
808         }
809     }
810     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state));
811
812     // Paint button.
813     IntRect buttonRect(rect);
814     guint junction = gtk_style_context_get_junction_sides(context);
815     if (arrowType == GTK_ARROW_UP)
816         junction |= GTK_JUNCTION_BOTTOM;
817     else {
818         junction |= GTK_JUNCTION_TOP;
819         buttonRect.move(0, rect.height() / 2);
820     }
821     buttonRect.setHeight(rect.height() / 2);
822     gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction));
823
824     gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
825     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
826
827     // Paint arrow centered inside button.
828     // This code is based on gtkspinbutton.c code.
829     IntRect arrowRect;
830     gdouble angle;
831     if (arrowType == GTK_ARROW_UP) {
832         angle = 0;
833         arrowRect.setY(rect.y());
834         arrowRect.setHeight(rect.height() / 2 - 2);
835     } else {
836         angle = G_PI;
837         arrowRect.setY(rect.y() + buttonRect.y());
838         arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
839     }
840     arrowRect.setWidth(rect.width() - 3);
841     if (direction == GTK_TEXT_DIR_LTR)
842         arrowRect.setX(rect.x() + 1);
843     else
844         arrowRect.setX(rect.x() + 2);
845
846     gint width = arrowRect.width() / 2;
847     width -= width % 2 - 1; // Force odd.
848     gint height = (width + 1) / 2;
849
850     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
851     gtk_render_arrow(context, paintInfo.context->platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
852
853     gtk_style_context_restore(context);
854 }
855
856 bool RenderThemeGtk::paintInnerSpinButton(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
857 {
858     GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
859     gtk_style_context_save(context);
860
861     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject.style().direction()));
862     gtk_style_context_set_direction(context, direction);
863
864     guint flags = 0;
865     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
866         flags |= GTK_STATE_FLAG_INSENSITIVE;
867     else if (isFocused(renderObject))
868         flags |= GTK_STATE_FLAG_FOCUSED;
869     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
870     gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ENTRY);
871
872     paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP);
873     paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN);
874
875     gtk_style_context_restore(context);
876
877     return false;
878 }
879
880 GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize)
881 {
882     ASSERT(iconName);
883
884     GtkStyleContext* context = getStyleContext(widgetType);
885     GtkIconSet* iconSet = gtk_style_context_lookup_icon_set(context, iconName);
886
887     gtk_style_context_save(context);
888
889     guint flags = 0;
890     if (state == GTK_STATE_PRELIGHT)
891         flags |= GTK_STATE_FLAG_PRELIGHT;
892     else if (state == GTK_STATE_INSENSITIVE)
893         flags |= GTK_STATE_FLAG_INSENSITIVE;
894
895     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
896     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction));
897     GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context, static_cast<GtkIconSize>(iconSize));
898
899     gtk_style_context_restore(context);
900
901     return adoptGRef(icon);
902 }
903
904 GRefPtr<GdkPixbuf> getStockSymbolicIconForWidgetType(GType widgetType, const char* symbolicIconName, const char* fallbackStockIconName, gint direction, gint state, gint iconSize)
905 {
906     GtkStyleContext* context = getStyleContext(widgetType);
907
908     gtk_style_context_save(context);
909
910     guint flags = 0;
911     if (state == GTK_STATE_PRELIGHT)
912         flags |= GTK_STATE_FLAG_PRELIGHT;
913     else if (state == GTK_STATE_INSENSITIVE)
914         flags |= GTK_STATE_FLAG_INSENSITIVE;
915
916     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
917     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction));
918     GtkIconInfo* info = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), symbolicIconName, iconSize,
919         static_cast<GtkIconLookupFlags>(GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_FORCE_SIZE));
920     GdkPixbuf* icon = 0;
921     if (info) {
922         icon = gtk_icon_info_load_symbolic_for_context(info, context, 0, 0);
923         gtk_icon_info_free(info);
924     }
925
926     gtk_style_context_restore(context);
927
928     if (!icon) {
929         if (!fallbackStockIconName)
930             return nullptr;
931         return getStockIconForWidgetType(widgetType, fallbackStockIconName, direction, state, iconSize);
932     }
933
934     return adoptGRef(icon);
935 }
936
937 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
938 {
939     GdkRGBA gdkRGBAColor;
940     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
941     return gdkRGBAColor;
942 }
943
944 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
945 {
946     GdkRGBA gdkRGBAColor;
947     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
948     return gdkRGBAColor;
949 }
950
951 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
952 {
953     GdkRGBA gdkRGBAColor;
954     gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
955     return gdkRGBAColor;
956 }
957
958 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
959 {
960     GdkRGBA gdkRGBAColor;
961     gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
962     return gdkRGBAColor;
963 }
964
965 Color RenderThemeGtk::platformActiveListBoxSelectionBackgroundColor() const
966 {
967     GdkRGBA gdkRGBAColor;
968     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
969     return gdkRGBAColor;
970 }
971
972 Color RenderThemeGtk::platformInactiveListBoxSelectionBackgroundColor() const
973 {
974     GdkRGBA gdkRGBAColor;
975     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
976     return gdkRGBAColor;
977 }
978
979 Color RenderThemeGtk::platformActiveListBoxSelectionForegroundColor() const
980 {
981     GdkRGBA gdkRGBAColor;
982     gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
983     return gdkRGBAColor;
984 }
985
986 Color RenderThemeGtk::platformInactiveListBoxSelectionForegroundColor() const
987 {
988     GdkRGBA gdkRGBAColor;
989     gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
990     return gdkRGBAColor;
991 }
992
993 Color RenderThemeGtk::systemColor(CSSValueID cssValueId) const
994 {
995     GdkRGBA gdkRGBAColor;
996
997     switch (cssValueId) {
998     case CSSValueButtontext:
999         gtk_style_context_get_color(getStyleContext(GTK_TYPE_BUTTON), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
1000         return gdkRGBAColor;
1001     case CSSValueCaptiontext:
1002         gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(0), &gdkRGBAColor);
1003         return gdkRGBAColor;
1004     default:
1005         return RenderTheme::systemColor(cssValueId);
1006     }
1007 }
1008
1009 } // namespace WebCore
1010
1011 #endif // !GTK_API_VERSION_2