[GTK] Menulist buttons have separators even when the theme turns them off
[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 "RenderObject.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->second.get());
60
61     Page::scheduleForcedStyleRecalcForAllPages();
62 }
63
64 static StyleContextMap& styleContextMap()
65 {
66     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     std::pair<StyleContextMap::iterator, bool> result = styleContextMap().add(widgetType, 0);
81     if (!result.second)
82         return result.first->second.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     else if (widgetType == GTK_TYPE_SCALE)
96         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCALE);
97     else if (widgetType == GTK_TYPE_SEPARATOR)
98         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SEPARATOR);
99     else if (widgetType == GTK_TYPE_PROGRESS_BAR)
100         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_PROGRESSBAR);
101     else if (widgetType == GTK_TYPE_SPIN_BUTTON)
102         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SPINBUTTON);
103     else if (widgetType == GTK_TYPE_TREE_VIEW)
104         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_VIEW);
105     else if (widgetType == GTK_TYPE_CHECK_BUTTON)
106         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_CHECK);
107     else if (widgetType == GTK_TYPE_RADIO_BUTTON)
108         gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_RADIO);
109
110     GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new());
111     gtk_style_context_set_path(context.get(), path);
112     gtk_widget_path_free(path);
113
114     result.first->second = context;
115     return context.get();
116 }
117
118 GtkStyleContext* RenderThemeGtk::gtkScrollbarStyle()
119 {
120     return getStyleContext(GTK_TYPE_SCROLLBAR);
121 }
122
123 // This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h.
124 extern GtkTextDirection gtkTextDirection(TextDirection);
125
126 void RenderThemeGtk::platformInit()
127 {
128 }
129
130 RenderThemeGtk::~RenderThemeGtk()
131 {
132 }
133
134 #if ENABLE(VIDEO)
135 void RenderThemeGtk::initMediaColors()
136 {
137     GdkRGBA color;
138     GtkStyleContext* containerContext = getStyleContext(GTK_TYPE_CONTAINER);
139
140     gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_NORMAL, &color);
141     m_panelColor = color;
142     gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_ACTIVE, &color);
143     m_sliderColor = color;
144     gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_SELECTED, &color);
145     m_sliderThumbColor = color;
146 }
147 #endif
148
149 static void adjustRectForFocus(GtkStyleContext* context, IntRect& rect)
150 {
151     gint focusWidth, focusPad;
152     gtk_style_context_get_style(context,
153                                 "focus-line-width", &focusWidth,
154                                 "focus-padding", &focusPad, NULL);
155     rect.inflate(focusWidth + focusPad);
156 }
157
158 void RenderThemeGtk::adjustRepaintRect(const RenderObject* renderObject, IntRect& rect)
159 {
160     GtkStyleContext* context = 0;
161     bool checkInteriorFocus = false;
162     ControlPart part = renderObject->style()->appearance();
163     switch (part) {
164     case CheckboxPart:
165     case RadioPart:
166         context = getStyleContext(part == CheckboxPart ? GTK_TYPE_CHECK_BUTTON : GTK_TYPE_RADIO_BUTTON);
167
168         gint indicatorSpacing;
169         gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL);
170         rect.inflate(indicatorSpacing);
171
172         return;
173     case SliderVerticalPart:
174     case SliderHorizontalPart:
175         context = getStyleContext(GTK_TYPE_SCALE);
176         break;
177     case ButtonPart:
178     case MenulistButtonPart:
179     case MenulistPart:
180         context = getStyleContext(GTK_TYPE_BUTTON);
181         checkInteriorFocus = true;
182         break;
183     case TextFieldPart:
184     case TextAreaPart:
185         context = getStyleContext(GTK_TYPE_ENTRY);
186         checkInteriorFocus = true;
187         break;
188     default:
189         return;
190     }
191
192     ASSERT(context);
193     if (checkInteriorFocus) {
194         gboolean interiorFocus;
195         gtk_style_context_get_style(context, "interior-focus", &interiorFocus, NULL);
196         if (interiorFocus)
197             return;
198     }
199     adjustRectForFocus(context, rect);
200 }
201
202 static void setToggleSize(GtkStyleContext* context, RenderStyle* style)
203 {
204     // The width and height are both specified, so we shouldn't change them.
205     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
206         return;
207
208     // Other ports hard-code this to 13 which is also the default value defined by GTK+.
209     // GTK+ users tend to demand the native look.
210     // It could be made a configuration option values other than 13 actually break site compatibility.
211     gint indicatorSize;
212     gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL);
213
214     if (style->width().isIntrinsicOrAuto())
215         style->setWidth(Length(indicatorSize, Fixed));
216
217     if (style->height().isAuto())
218         style->setHeight(Length(indicatorSize, Fixed));
219 }
220
221 static void paintToggle(const RenderThemeGtk* theme, GType widgetType, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
222 {
223     GtkStyleContext* context = getStyleContext(widgetType);
224     gtk_style_context_save(context);
225
226     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())));
227     gtk_style_context_add_class(context, widgetType == GTK_TYPE_CHECK_BUTTON ? GTK_STYLE_CLASS_CHECK : GTK_STYLE_CLASS_RADIO);
228
229     guint flags = 0;
230     if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject))
231         flags |= GTK_STATE_FLAG_INSENSITIVE;
232     else if (theme->isHovered(renderObject))
233         flags |= GTK_STATE_FLAG_PRELIGHT;
234     if (theme->isIndeterminate(renderObject))
235         flags |= GTK_STATE_FLAG_INCONSISTENT;
236     else if (theme->isChecked(renderObject))
237         flags |= GTK_STATE_FLAG_ACTIVE;
238     if (theme->isPressed(renderObject))
239         flags |= GTK_STATE_FLAG_SELECTED;
240     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
241
242     if (widgetType == GTK_TYPE_CHECK_BUTTON)
243         gtk_render_check(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
244     else
245         gtk_render_option(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
246
247     if (theme->isFocused(renderObject)) {
248         IntRect indicatorRect(rect);
249         gint indicatorSpacing;
250         gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL);
251         indicatorRect.inflate(indicatorSpacing);
252         gtk_render_focus(context, paintInfo.context->platformContext()->cr(), indicatorRect.x(), indicatorRect.y(),
253                          indicatorRect.width(), indicatorRect.height());
254     }
255
256     gtk_style_context_restore(context);
257 }
258
259 void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
260 {
261     setToggleSize(getStyleContext(GTK_TYPE_CHECK_BUTTON), style);
262 }
263
264 bool RenderThemeGtk::paintCheckbox(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
265 {
266     paintToggle(this, GTK_TYPE_CHECK_BUTTON, renderObject, paintInfo, rect);
267     return false;
268 }
269
270 void RenderThemeGtk::setRadioSize(RenderStyle* style) const
271 {
272     setToggleSize(getStyleContext(GTK_TYPE_RADIO_BUTTON), style);
273 }
274
275 bool RenderThemeGtk::paintRadio(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
276 {
277     paintToggle(this, GTK_TYPE_RADIO_BUTTON, renderObject, paintInfo, rect);
278     return false;
279 }
280
281 static void renderButton(RenderTheme* theme, GtkStyleContext* context, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
282 {
283     IntRect buttonRect(rect);
284
285     guint flags = 0;
286     if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject))
287         flags |= GTK_STATE_FLAG_INSENSITIVE;
288     else if (theme->isHovered(renderObject))
289         flags |= GTK_STATE_FLAG_PRELIGHT;
290     if (theme->isPressed(renderObject))
291         flags |= GTK_STATE_FLAG_ACTIVE;
292     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
293
294     if (theme->isDefault(renderObject)) {
295         GtkBorder* borderPtr = 0;
296         GtkBorder border = { 1, 1, 1, 1 };
297
298         gtk_style_context_get_style(context, "default-border", &borderPtr, NULL);
299         if (borderPtr) {
300             border = *borderPtr;
301             gtk_border_free(borderPtr);
302         }
303
304         buttonRect.move(border.left, border.top);
305         buttonRect.setWidth(buttonRect.width() - (border.left + border.right));
306         buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom));
307
308         gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT);
309     }
310
311     gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
312     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
313
314     if (theme->isFocused(renderObject)) {
315         gint focusWidth, focusPad;
316         gboolean displaceFocus, interiorFocus;
317         gtk_style_context_get_style(context,
318                                     "focus-line-width", &focusWidth,
319                                     "focus-padding", &focusPad,
320                                     "interior-focus", &interiorFocus,
321                                     "displace-focus", &displaceFocus,
322                                     NULL);
323
324         if (interiorFocus) {
325             GtkBorder borderWidth;
326             gtk_style_context_get_border(context, static_cast<GtkStateFlags>(flags), &borderWidth);
327
328             buttonRect = IntRect(buttonRect.x() + borderWidth.left + focusPad, buttonRect.y() + borderWidth.top + focusPad,
329                                  buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right),
330                                  buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom));
331         } else
332             buttonRect.inflate(focusWidth + focusPad);
333
334         if (displaceFocus && theme->isPressed(renderObject)) {
335             gint childDisplacementX;
336             gint childDisplacementY;
337             gtk_style_context_get_style(context,
338                                         "child-displacement-x", &childDisplacementX,
339                                         "child-displacement-y", &childDisplacementY,
340                                         NULL);
341             buttonRect.move(childDisplacementX, childDisplacementY);
342         }
343
344         gtk_render_focus(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
345     }
346 }
347 bool RenderThemeGtk::paintButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
348 {
349     GtkStyleContext* context = getStyleContext(GTK_TYPE_BUTTON);
350     gtk_style_context_save(context);
351
352     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())));
353     gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
354
355     renderButton(this, context, renderObject, paintInfo, rect);
356
357     gtk_style_context_restore(context);
358
359     return false;
360 }
361
362 static void getComboBoxMetrics(RenderStyle* style, GtkBorder& border, int& focus, int& separator)
363 {
364     // If this menu list button isn't drawn using the native theme, we
365     // don't add any extra padding beyond what WebCore already uses.
366     if (style->appearance() == NoControlPart)
367         return;
368
369     GtkStyleContext* context = getStyleContext(GTK_TYPE_COMBO_BOX);
370     gtk_style_context_save(context);
371
372     gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
373     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(style->direction())));
374
375     gtk_style_context_get_border(context, static_cast<GtkStateFlags>(0), &border);
376
377     gboolean interiorFocus;
378     gint focusWidth, focusPad;
379     gtk_style_context_get_style(context,
380                                 "interior-focus", &interiorFocus,
381                                 "focus-line-width", &focusWidth,
382                                 "focus-padding", &focusPad, NULL);
383     focus = interiorFocus ? focusWidth + focusPad : 0;
384
385     gtk_style_context_restore(context);
386
387     context = getStyleContext(GTK_TYPE_SEPARATOR);
388     gtk_style_context_save(context);
389
390     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(style->direction()));
391     gtk_style_context_set_direction(context, direction);
392     gtk_style_context_add_class(context, "separator");
393
394     gboolean wideSeparators;
395     gint separatorWidth;
396     gtk_style_context_get_style(context,
397                                 "wide-separators", &wideSeparators,
398                                 "separator-width", &separatorWidth,
399                                 NULL);
400
401     // GTK+ always uses border.left, regardless of text direction. See gtkseperator.c.
402     if (!wideSeparators)
403         separatorWidth = border.left;
404
405     separator = separatorWidth;
406
407     gtk_style_context_restore(context);
408 }
409
410 int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const
411 {
412     GtkBorder borderWidth = { 0, 0, 0, 0 };
413     int focusWidth = 0, separatorWidth = 0;
414     getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
415     int left = borderWidth.left + focusWidth;
416     if (style->direction() == RTL)
417         left += separatorWidth + minArrowSize;
418     return left;
419 }
420
421 int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const
422 {
423     GtkBorder borderWidth = { 0, 0, 0, 0 };
424     int focusWidth = 0, separatorWidth = 0;
425     getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
426     int right = borderWidth.right + focusWidth;
427     if (style->direction() == LTR)
428         right += separatorWidth + minArrowSize;
429     return right;
430 }
431
432 int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const
433 {
434     GtkBorder borderWidth = { 0, 0, 0, 0 };
435     int focusWidth = 0, separatorWidth = 0;
436     getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
437     return borderWidth.top + focusWidth;
438 }
439
440 int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const
441 {
442     GtkBorder borderWidth = { 0, 0, 0, 0 };
443     int focusWidth = 0, separatorWidth = 0;
444     getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth);
445     return borderWidth.bottom + focusWidth;
446 }
447
448 bool RenderThemeGtk::paintMenuList(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
449 {
450     cairo_t* cairoContext = paintInfo.context->platformContext()->cr();
451     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()));
452
453     // Paint the button.
454     GtkStyleContext* buttonStyleContext = getStyleContext(GTK_TYPE_BUTTON);
455     gtk_style_context_save(buttonStyleContext);
456     gtk_style_context_set_direction(buttonStyleContext, direction);
457     gtk_style_context_add_class(buttonStyleContext, GTK_STYLE_CLASS_BUTTON);
458     renderButton(this, buttonStyleContext, renderObject, paintInfo, rect);
459
460     // Get the inner rectangle.
461     gint focusWidth, focusPad;
462     GtkBorder* innerBorderPtr = 0;
463     GtkBorder innerBorder = { 1, 1, 1, 1 };
464     gtk_style_context_get_style(buttonStyleContext,
465                                 "inner-border", &innerBorderPtr,
466                                 "focus-line-width", &focusWidth,
467                                 "focus-padding", &focusPad,
468                                 NULL);
469     if (innerBorderPtr) {
470         innerBorder = *innerBorderPtr;
471         gtk_border_free(innerBorderPtr);
472     }
473
474     GtkBorder borderWidth;
475     GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext);
476     gtk_style_context_get_border(buttonStyleContext, state, &borderWidth);
477
478     focusWidth += focusPad;
479     IntRect innerRect(rect.x() + innerBorder.left + borderWidth.left + focusWidth,
480                       rect.y() + innerBorder.top + borderWidth.top + focusWidth,
481                       rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth),
482                       rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth));
483
484     if (isPressed(renderObject)) {
485         gint childDisplacementX;
486         gint childDisplacementY;
487         gtk_style_context_get_style(buttonStyleContext,
488                                     "child-displacement-x", &childDisplacementX,
489                                     "child-displacement-y", &childDisplacementY,
490                                     NULL);
491         innerRect.move(childDisplacementX, childDisplacementY);
492     }
493     innerRect.setWidth(max(1, innerRect.width()));
494     innerRect.setHeight(max(1, innerRect.height()));
495
496     gtk_style_context_restore(buttonStyleContext);
497
498     // Paint the arrow.
499     GtkStyleContext* arrowStyleContext = getStyleContext(GTK_TYPE_ARROW);
500     gtk_style_context_save(arrowStyleContext);
501
502     gtk_style_context_set_direction(arrowStyleContext, direction);
503     gtk_style_context_add_class(arrowStyleContext, "arrow");
504     gtk_style_context_add_class(arrowStyleContext, GTK_STYLE_CLASS_BUTTON);
505
506     gfloat arrowScaling;
507     gtk_style_context_get_style(arrowStyleContext, "arrow-scaling", &arrowScaling, NULL);
508
509     IntSize arrowSize(minArrowSize, innerRect.height());
510     FloatPoint arrowPosition(innerRect.location());
511     if (direction == GTK_TEXT_DIR_LTR)
512         arrowPosition.move(innerRect.width() - arrowSize.width(), 0);
513
514     // GTK+ actually fetches the xalign and valign values from the widget, but since we
515     // don't have a widget here, we are just using the default xalign and valign values of 0.5.
516     gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling;
517     arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2);
518
519     gtk_style_context_set_state(arrowStyleContext, state);
520     gtk_render_arrow(arrowStyleContext, cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent);
521
522     gtk_style_context_restore(arrowStyleContext);
523
524     // Paint the separator if needed.
525     GtkStyleContext* separatorStyleContext = getStyleContext(GTK_TYPE_COMBO_BOX);
526     gtk_style_context_save(separatorStyleContext);
527
528     gtk_style_context_set_direction(separatorStyleContext, direction);
529     gtk_style_context_add_class(separatorStyleContext, "separator");
530
531     gboolean wideSeparators;
532     gint separatorWidth;
533     gtk_style_context_get_style(separatorStyleContext,
534                                 "wide-separators", &wideSeparators,
535                                 "separator-width", &separatorWidth,
536                                 NULL);
537     if (wideSeparators && !separatorWidth) {
538         gtk_style_context_restore(separatorStyleContext);
539         return false;
540     }
541
542     gtk_style_context_set_state(separatorStyleContext, state);
543     IntPoint separatorPosition(arrowPosition.x(), innerRect.y());
544     if (wideSeparators) {
545         if (direction == GTK_TEXT_DIR_LTR)
546             separatorPosition.move(-separatorWidth, 0);
547         else
548             separatorPosition.move(arrowSize.width(), 0);
549
550         gtk_render_frame(separatorStyleContext, cairoContext,
551                          separatorPosition.x(), separatorPosition.y(),
552                          separatorWidth, innerRect.height());
553     } else {
554         GtkBorder padding;
555         gtk_style_context_get_padding(separatorStyleContext, state, &padding);
556         GtkBorder border;
557         gtk_style_context_get_border(separatorStyleContext, state, &border);
558
559         if (direction == GTK_TEXT_DIR_LTR)
560             separatorPosition.move(-(padding.left + border.left), 0);
561         else
562             separatorPosition.move(arrowSize.width(), 0);
563
564         cairo_save(cairoContext);
565
566         // An extra clip prevents the separator bleeding outside of the specified rectangle because of subpixel positioning.
567         cairo_rectangle(cairoContext, separatorPosition.x(), separatorPosition.y(), border.left, innerRect.height());
568         cairo_clip(cairoContext);
569         gtk_render_line(separatorStyleContext, cairoContext,
570                         separatorPosition.x(), separatorPosition.y(),
571                         separatorPosition.x(), innerRect.maxY());
572         cairo_restore(cairoContext);
573     }
574
575     gtk_style_context_restore(separatorStyleContext);
576     return false;
577 }
578
579 bool RenderThemeGtk::paintTextField(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
580 {
581     GtkStyleContext* context = getStyleContext(GTK_TYPE_ENTRY);
582     gtk_style_context_save(context);
583
584     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())));
585     gtk_style_context_add_class(context, GTK_STYLE_CLASS_ENTRY);
586
587     guint flags = 0;
588     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
589         flags |= GTK_STATE_FLAG_INSENSITIVE;
590     else if (isFocused(renderObject))
591         flags |= GTK_STATE_FLAG_FOCUSED;
592     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
593
594     gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
595     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
596
597     if (isFocused(renderObject) && isEnabled(renderObject)) {
598         gboolean interiorFocus;
599         gint focusWidth, focusPad;
600         gtk_style_context_get_style(context,
601                                     "interior-focus", &interiorFocus,
602                                     "focus-line-width", &focusWidth,
603                                     "focus-padding", &focusPad,
604                                     NULL);
605         if (!interiorFocus) {
606             IntRect focusRect(rect);
607             focusRect.inflate(focusWidth + focusPad);
608             gtk_render_focus(context, paintInfo.context->platformContext()->cr(),
609                              focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
610         }
611     }
612
613     gtk_style_context_restore(context);
614
615     return false;
616 }
617
618 static void applySliderStyleContextClasses(GtkStyleContext* context, ControlPart part)
619 {
620     gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE);
621     if (part == SliderHorizontalPart || part == SliderThumbHorizontalPart)
622         gtk_style_context_add_class(context, GTK_STYLE_CLASS_HORIZONTAL);
623     else if (part == SliderVerticalPart || part == SliderThumbVerticalPart)
624         gtk_style_context_add_class(context, GTK_STYLE_CLASS_VERTICAL);
625 }
626
627 bool RenderThemeGtk::paintSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
628 {
629     ControlPart part = renderObject->style()->appearance();
630     ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart);
631
632     GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
633     gtk_style_context_save(context);
634
635     gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction()));
636     applySliderStyleContextClasses(context, part);
637     gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
638
639     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
640         gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE);
641
642     gtk_render_background(context, paintInfo.context->platformContext()->cr(),
643                           rect.x(), rect.y(), rect.width(), rect.height());
644     gtk_render_frame(context, paintInfo.context->platformContext()->cr(),
645                      rect.x(), rect.y(), rect.width(), rect.height());
646
647     if (isFocused(renderObject)) {
648         gint focusWidth, focusPad;
649         gtk_style_context_get_style(context,
650                                     "focus-line-width", &focusWidth,
651                                     "focus-padding", &focusPad, NULL);
652         IntRect focusRect(rect);
653         focusRect.inflate(focusWidth + focusPad);
654         gtk_render_focus(context, paintInfo.context->platformContext()->cr(),
655                          focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
656     }
657
658     gtk_style_context_restore(context);
659     return false;
660 }
661
662 bool RenderThemeGtk::paintSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
663 {
664     ControlPart part = renderObject->style()->appearance();
665     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
666
667     GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
668     gtk_style_context_save(context);
669
670     gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction()));
671     applySliderStyleContextClasses(context, part);
672     gtk_style_context_add_class(context, GTK_STYLE_CLASS_SLIDER);
673
674     guint flags = 0;
675     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
676         flags |= GTK_STATE_FLAG_INSENSITIVE;
677     else if (isHovered(renderObject))
678         flags |= GTK_STATE_FLAG_PRELIGHT;
679     if (isPressed(renderObject))
680         flags |= GTK_STATE_FLAG_ACTIVE;
681     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
682
683     gtk_render_slider(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
684                       part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
685
686     gtk_style_context_restore(context);
687
688     return false;
689 }
690
691 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle* style) const
692 {
693     ControlPart part = style->appearance();
694 #if ENABLE(VIDEO)
695     if (part == MediaSliderThumbPart) {
696         adjustMediaSliderThumbSize(style);
697         return;
698     }
699 #endif
700
701     gint sliderWidth, sliderLength;
702     gtk_style_context_get_style(getStyleContext(GTK_TYPE_SCALE),
703                                 "slider-width", &sliderWidth,
704                                 "slider-length", &sliderLength,
705                                 NULL);
706     if (part == SliderThumbHorizontalPart) {
707         style->setWidth(Length(sliderLength, Fixed));
708         style->setHeight(Length(sliderWidth, Fixed));
709         return;
710     }
711     ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
712     style->setWidth(Length(sliderWidth, Fixed));
713     style->setHeight(Length(sliderLength, Fixed));
714 }
715
716 #if ENABLE(PROGRESS_TAG)
717 bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
718 {
719     if (!renderObject->isProgress())
720         return true;
721
722     GtkStyleContext* context = getStyleContext(GTK_TYPE_PROGRESS_BAR);
723     gtk_style_context_save(context);
724
725     gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
726
727     gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
728     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
729
730     gtk_style_context_restore(context);
731
732     gtk_style_context_save(context);
733     gtk_style_context_add_class(context, GTK_STYLE_CLASS_PROGRESSBAR);
734
735
736     GtkBorder padding;
737     gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
738     IntRect progressRect(rect.x() + padding.left, rect.y() + padding.top,
739                          rect.width() - (padding.left + padding.right),
740                          rect.height() - (padding.top + padding.bottom));
741     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
742
743     if (!progressRect.isEmpty())
744         gtk_render_activity(context, paintInfo.context->platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
745
746     gtk_style_context_restore(context);
747     return false;
748 }
749 #endif
750
751 static gint spinButtonArrowSize(GtkStyleContext* context)
752 {
753     const PangoFontDescription* fontDescription = gtk_style_context_get_font(context, static_cast<GtkStateFlags>(0));
754     gint fontSize = pango_font_description_get_size(fontDescription);
755     gint arrowSize = max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
756
757     return arrowSize - arrowSize % 2; // Force even.
758 }
759
760 void RenderThemeGtk::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
761 {
762     GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
763
764     GtkBorder padding;
765     gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
766
767     int width = spinButtonArrowSize(context) + padding.left + padding.right;
768     style->setWidth(Length(width, Fixed));
769     style->setMinWidth(Length(width, Fixed));
770 }
771
772 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
773 {
774     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
775
776     gtk_style_context_save(context);
777     gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
778
779     GtkTextDirection direction = gtk_style_context_get_direction(context);
780     guint state = static_cast<guint>(gtk_style_context_get_state(context));
781     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
782         if (theme->isPressed(renderObject)) {
783             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
784                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
785                 state |= GTK_STATE_FLAG_ACTIVE;
786         } else if (theme->isHovered(renderObject)) {
787             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
788                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
789                 state |= GTK_STATE_FLAG_PRELIGHT;
790         }
791     }
792     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state));
793
794     // Paint button.
795     IntRect buttonRect(rect);
796     guint junction = gtk_style_context_get_junction_sides(context);
797     if (arrowType == GTK_ARROW_UP)
798         junction |= GTK_JUNCTION_BOTTOM;
799     else {
800         junction |= GTK_JUNCTION_TOP;
801         buttonRect.move(0, rect.height() / 2);
802     }
803     buttonRect.setHeight(rect.height() / 2);
804     gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction));
805
806     gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
807     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
808
809     // Paint arrow centered inside button.
810     // This code is based on gtkspinbutton.c code.
811     IntRect arrowRect;
812     gdouble angle;
813     if (arrowType == GTK_ARROW_UP) {
814         angle = 0;
815         arrowRect.setY(rect.y());
816         arrowRect.setHeight(rect.height() / 2 - 2);
817     } else {
818         angle = G_PI;
819         arrowRect.setY(rect.y() + buttonRect.y());
820         arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
821     }
822     arrowRect.setWidth(rect.width() - 3);
823     if (direction == GTK_TEXT_DIR_LTR)
824         arrowRect.setX(rect.x() + 1);
825     else
826         arrowRect.setX(rect.x() + 2);
827
828     gint width = arrowRect.width() / 2;
829     width -= width % 2 - 1; // Force odd.
830     gint height = (width + 1) / 2;
831
832     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
833     gtk_render_arrow(context, paintInfo.context->platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
834
835     gtk_style_context_restore(context);
836 }
837
838 bool RenderThemeGtk::paintInnerSpinButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
839 {
840     GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
841     gtk_style_context_save(context);
842
843     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()));
844     gtk_style_context_set_direction(context, direction);
845
846     guint flags = 0;
847     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
848         flags |= GTK_STATE_FLAG_INSENSITIVE;
849     else if (isFocused(renderObject))
850         flags |= GTK_STATE_FLAG_FOCUSED;
851     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
852     gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ENTRY);
853
854     paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP);
855     paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN);
856
857     gtk_style_context_restore(context);
858
859     return false;
860 }
861
862 GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize)
863 {
864     GtkStyleContext* context = getStyleContext(widgetType);
865     GtkIconSet* iconSet = gtk_style_context_lookup_icon_set(context, iconName);
866
867     gtk_style_context_save(context);
868
869     guint flags = 0;
870     if (state == GTK_STATE_PRELIGHT)
871         flags |= GTK_STATE_FLAG_PRELIGHT;
872     else if (state == GTK_STATE_INSENSITIVE)
873         flags |= GTK_STATE_FLAG_INSENSITIVE;
874
875     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
876     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction));
877     GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context, static_cast<GtkIconSize>(iconSize));
878
879     gtk_style_context_restore(context);
880
881     return adoptGRef(icon);
882 }
883
884 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
885 {
886     GdkRGBA gdkRGBAColor;
887     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
888     return gdkRGBAColor;
889 }
890
891 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
892 {
893     GdkRGBA gdkRGBAColor;
894     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
895     return gdkRGBAColor;
896 }
897
898 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
899 {
900     GdkRGBA gdkRGBAColor;
901     gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
902     return gdkRGBAColor;
903 }
904
905 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
906 {
907     GdkRGBA gdkRGBAColor;
908     gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
909     return gdkRGBAColor;
910 }
911
912 Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const
913 {
914     GdkRGBA gdkRGBAColor;
915     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
916     return gdkRGBAColor;
917 }
918
919 Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const
920 {
921     GdkRGBA gdkRGBAColor;
922     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
923     return gdkRGBAColor;
924 }
925
926 Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const
927 {
928     GdkRGBA gdkRGBAColor;
929     gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
930     return gdkRGBAColor;
931 }
932
933 Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const
934 {
935     GdkRGBA gdkRGBAColor;
936     gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
937     return gdkRGBAColor;
938 }
939
940 Color RenderThemeGtk::systemColor(int cssValueId) const
941 {
942     GdkRGBA gdkRGBAColor;
943
944     switch (cssValueId) {
945     case CSSValueButtontext:
946         gtk_style_context_get_color(getStyleContext(GTK_TYPE_BUTTON), static_cast<GtkStateFlags>(0), &gdkRGBAColor);
947         return gdkRGBAColor;
948     case CSSValueCaptiontext:
949         gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(0), &gdkRGBAColor);
950         return gdkRGBAColor;
951     default:
952         return RenderTheme::systemColor(cssValueId);
953     }
954 }
955
956 } // namespace WebCore
957
958 #endif // !GTK_API_VERSION_2