3563002578fc303788fdc52c99f42f123484a26b
[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_BUTTON);
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_SEPARATOR);
526     gtk_style_context_save(separatorStyleContext);
527
528     gtk_style_context_set_direction(separatorStyleContext, direction);
529     gtk_style_context_add_class(separatorStyleContext, "separator");
530     gtk_style_context_add_class(separatorStyleContext, GTK_STYLE_CLASS_BUTTON);
531
532     gboolean wideSeparators;
533     gint separatorWidth;
534     gtk_style_context_get_style(separatorStyleContext,
535                                 "wide-separators", &wideSeparators,
536                                 "separator-width", &separatorWidth,
537                                 NULL);
538     if (wideSeparators && !separatorWidth) {
539         gtk_style_context_restore(separatorStyleContext);
540         return false;
541     }
542
543     gtk_style_context_set_state(separatorStyleContext, state);
544     IntPoint separatorPosition(arrowPosition.x(), innerRect.y());
545     if (wideSeparators) {
546         if (direction == GTK_TEXT_DIR_LTR)
547             separatorPosition.move(-separatorWidth, 0);
548         else
549             separatorPosition.move(arrowSize.width(), 0);
550
551         gtk_render_frame(separatorStyleContext, cairoContext,
552                          separatorPosition.x(), separatorPosition.y(),
553                          separatorWidth, innerRect.height());
554     } else {
555         GtkBorder padding;
556         gtk_style_context_get_padding(separatorStyleContext, state, &padding);
557         GtkBorder border;
558         gtk_style_context_get_border(separatorStyleContext, state, &border);
559
560         if (direction == GTK_TEXT_DIR_LTR)
561             separatorPosition.move(-(padding.left + border.left), 0);
562         else
563             separatorPosition.move(arrowSize.width(), 0);
564
565         cairo_save(cairoContext);
566
567         // An extra clip prevents the separator bleeding outside of the specified rectangle because of subpixel positioning.
568         cairo_rectangle(cairoContext, separatorPosition.x(), separatorPosition.y(), border.left, innerRect.height());
569         cairo_clip(cairoContext);
570         gtk_render_line(separatorStyleContext, cairoContext,
571                         separatorPosition.x(), separatorPosition.y(),
572                         separatorPosition.x(), innerRect.maxY());
573         cairo_restore(cairoContext);
574     }
575
576     gtk_style_context_restore(separatorStyleContext);
577     return false;
578 }
579
580 bool RenderThemeGtk::paintTextField(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
581 {
582     GtkStyleContext* context = getStyleContext(GTK_TYPE_ENTRY);
583     gtk_style_context_save(context);
584
585     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())));
586     gtk_style_context_add_class(context, GTK_STYLE_CLASS_ENTRY);
587
588     guint flags = 0;
589     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
590         flags |= GTK_STATE_FLAG_INSENSITIVE;
591     else if (isFocused(renderObject))
592         flags |= GTK_STATE_FLAG_FOCUSED;
593     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
594
595     gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
596     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
597
598     if (isFocused(renderObject) && isEnabled(renderObject)) {
599         gboolean interiorFocus;
600         gint focusWidth, focusPad;
601         gtk_style_context_get_style(context,
602                                     "interior-focus", &interiorFocus,
603                                     "focus-line-width", &focusWidth,
604                                     "focus-padding", &focusPad,
605                                     NULL);
606         if (!interiorFocus) {
607             IntRect focusRect(rect);
608             focusRect.inflate(focusWidth + focusPad);
609             gtk_render_focus(context, paintInfo.context->platformContext()->cr(),
610                              focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
611         }
612     }
613
614     gtk_style_context_restore(context);
615
616     return false;
617 }
618
619 static void applySliderStyleContextClasses(GtkStyleContext* context, ControlPart part)
620 {
621     gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE);
622     if (part == SliderHorizontalPart || part == SliderThumbHorizontalPart)
623         gtk_style_context_add_class(context, GTK_STYLE_CLASS_HORIZONTAL);
624     else if (part == SliderVerticalPart || part == SliderThumbVerticalPart)
625         gtk_style_context_add_class(context, GTK_STYLE_CLASS_VERTICAL);
626 }
627
628 bool RenderThemeGtk::paintSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
629 {
630     ControlPart part = renderObject->style()->appearance();
631     ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart);
632
633     GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
634     gtk_style_context_save(context);
635
636     gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction()));
637     applySliderStyleContextClasses(context, part);
638     gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
639
640     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
641         gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE);
642
643     gtk_render_background(context, paintInfo.context->platformContext()->cr(),
644                           rect.x(), rect.y(), rect.width(), rect.height());
645     gtk_render_frame(context, paintInfo.context->platformContext()->cr(),
646                      rect.x(), rect.y(), rect.width(), rect.height());
647
648     if (isFocused(renderObject)) {
649         gint focusWidth, focusPad;
650         gtk_style_context_get_style(context,
651                                     "focus-line-width", &focusWidth,
652                                     "focus-padding", &focusPad, NULL);
653         IntRect focusRect(rect);
654         focusRect.inflate(focusWidth + focusPad);
655         gtk_render_focus(context, paintInfo.context->platformContext()->cr(),
656                          focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height());
657     }
658
659     gtk_style_context_restore(context);
660     return false;
661 }
662
663 bool RenderThemeGtk::paintSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
664 {
665     ControlPart part = renderObject->style()->appearance();
666     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
667
668     GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE);
669     gtk_style_context_save(context);
670
671     gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction()));
672     applySliderStyleContextClasses(context, part);
673     gtk_style_context_add_class(context, GTK_STYLE_CLASS_SLIDER);
674
675     guint flags = 0;
676     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
677         flags |= GTK_STATE_FLAG_INSENSITIVE;
678     else if (isHovered(renderObject))
679         flags |= GTK_STATE_FLAG_PRELIGHT;
680     if (isPressed(renderObject))
681         flags |= GTK_STATE_FLAG_ACTIVE;
682     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
683
684     gtk_render_slider(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(),
685                       part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
686
687     gtk_style_context_restore(context);
688
689     return false;
690 }
691
692 void RenderThemeGtk::adjustSliderThumbSize(RenderStyle* style) const
693 {
694     ControlPart part = style->appearance();
695 #if ENABLE(VIDEO)
696     if (part == MediaSliderThumbPart) {
697         adjustMediaSliderThumbSize(style);
698         return;
699     }
700 #endif
701
702     gint sliderWidth, sliderLength;
703     gtk_style_context_get_style(getStyleContext(GTK_TYPE_SCALE),
704                                 "slider-width", &sliderWidth,
705                                 "slider-length", &sliderLength,
706                                 NULL);
707     if (part == SliderThumbHorizontalPart) {
708         style->setWidth(Length(sliderLength, Fixed));
709         style->setHeight(Length(sliderWidth, Fixed));
710         return;
711     }
712     ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
713     style->setWidth(Length(sliderWidth, Fixed));
714     style->setHeight(Length(sliderLength, Fixed));
715 }
716
717 #if ENABLE(PROGRESS_TAG)
718 bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
719 {
720     if (!renderObject->isProgress())
721         return true;
722
723     GtkStyleContext* context = getStyleContext(GTK_TYPE_PROGRESS_BAR);
724     gtk_style_context_save(context);
725
726     gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH);
727
728     gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
729     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height());
730
731     gtk_style_context_restore(context);
732
733     gtk_style_context_save(context);
734     gtk_style_context_add_class(context, GTK_STYLE_CLASS_PROGRESSBAR);
735
736
737     GtkBorder padding;
738     gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
739     IntRect progressRect(rect.x() + padding.left, rect.y() + padding.top,
740                          rect.width() - (padding.left + padding.right),
741                          rect.height() - (padding.top + padding.bottom));
742     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
743
744     if (!progressRect.isEmpty())
745         gtk_render_activity(context, paintInfo.context->platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height());
746
747     gtk_style_context_restore(context);
748     return false;
749 }
750 #endif
751
752 static gint spinButtonArrowSize(GtkStyleContext* context)
753 {
754     const PangoFontDescription* fontDescription = gtk_style_context_get_font(context, static_cast<GtkStateFlags>(0));
755     gint fontSize = pango_font_description_get_size(fontDescription);
756     gint arrowSize = max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
757
758     return arrowSize - arrowSize % 2; // Force even.
759 }
760
761 void RenderThemeGtk::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
762 {
763     GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
764
765     GtkBorder padding;
766     gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding);
767
768     int width = spinButtonArrowSize(context) + padding.left + padding.right;
769     style->setWidth(Length(width, Fixed));
770     style->setMinWidth(Length(width, Fixed));
771 }
772
773 static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType)
774 {
775     ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN);
776
777     gtk_style_context_save(context);
778     gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
779
780     GtkTextDirection direction = gtk_style_context_get_direction(context);
781     guint state = static_cast<guint>(gtk_style_context_get_state(context));
782     if (!(state & GTK_STATE_FLAG_INSENSITIVE)) {
783         if (theme->isPressed(renderObject)) {
784             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject))
785                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject)))
786                 state |= GTK_STATE_FLAG_ACTIVE;
787         } else if (theme->isHovered(renderObject)) {
788             if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject))
789                 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject)))
790                 state |= GTK_STATE_FLAG_PRELIGHT;
791         }
792     }
793     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state));
794
795     // Paint button.
796     IntRect buttonRect(rect);
797     guint junction = gtk_style_context_get_junction_sides(context);
798     if (arrowType == GTK_ARROW_UP)
799         junction |= GTK_JUNCTION_BOTTOM;
800     else {
801         junction |= GTK_JUNCTION_TOP;
802         buttonRect.move(0, rect.height() / 2);
803     }
804     buttonRect.setHeight(rect.height() / 2);
805     gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction));
806
807     gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
808     gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height());
809
810     // Paint arrow centered inside button.
811     // This code is based on gtkspinbutton.c code.
812     IntRect arrowRect;
813     gdouble angle;
814     if (arrowType == GTK_ARROW_UP) {
815         angle = 0;
816         arrowRect.setY(rect.y());
817         arrowRect.setHeight(rect.height() / 2 - 2);
818     } else {
819         angle = G_PI;
820         arrowRect.setY(rect.y() + buttonRect.y());
821         arrowRect.setHeight(rect.height() - arrowRect.y() - 2);
822     }
823     arrowRect.setWidth(rect.width() - 3);
824     if (direction == GTK_TEXT_DIR_LTR)
825         arrowRect.setX(rect.x() + 1);
826     else
827         arrowRect.setX(rect.x() + 2);
828
829     gint width = arrowRect.width() / 2;
830     width -= width % 2 - 1; // Force odd.
831     gint height = (width + 1) / 2;
832
833     arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2);
834     gtk_render_arrow(context, paintInfo.context->platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width);
835
836     gtk_style_context_restore(context);
837 }
838
839 bool RenderThemeGtk::paintInnerSpinButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
840 {
841     GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON);
842     gtk_style_context_save(context);
843
844     GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()));
845     gtk_style_context_set_direction(context, direction);
846
847     guint flags = 0;
848     if (!isEnabled(renderObject) || isReadOnlyControl(renderObject))
849         flags |= GTK_STATE_FLAG_INSENSITIVE;
850     else if (isFocused(renderObject))
851         flags |= GTK_STATE_FLAG_FOCUSED;
852     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
853     gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ENTRY);
854
855     paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP);
856     paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN);
857
858     gtk_style_context_restore(context);
859
860     return false;
861 }
862
863 GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize)
864 {
865     GtkStyleContext* context = getStyleContext(widgetType);
866     GtkIconSet* iconSet = gtk_style_context_lookup_icon_set(context, iconName);
867
868     gtk_style_context_save(context);
869
870     guint flags = 0;
871     if (state == GTK_STATE_PRELIGHT)
872         flags |= GTK_STATE_FLAG_PRELIGHT;
873     else if (state == GTK_STATE_INSENSITIVE)
874         flags |= GTK_STATE_FLAG_INSENSITIVE;
875
876     gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags));
877     gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction));
878     GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context, static_cast<GtkIconSize>(iconSize));
879
880     gtk_style_context_restore(context);
881
882     return adoptGRef(icon);
883 }
884
885 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
886 {
887     GdkRGBA gdkRGBAColor;
888     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
889     return gdkRGBAColor;
890 }
891
892 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
893 {
894     GdkRGBA gdkRGBAColor;
895     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
896     return gdkRGBAColor;
897 }
898
899 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
900 {
901     GdkRGBA gdkRGBAColor;
902     gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
903     return gdkRGBAColor;
904 }
905
906 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
907 {
908     GdkRGBA gdkRGBAColor;
909     gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
910     return gdkRGBAColor;
911 }
912
913 Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const
914 {
915     GdkRGBA gdkRGBAColor;
916     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
917     return gdkRGBAColor;
918 }
919
920 Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const
921 {
922     GdkRGBA gdkRGBAColor;
923     gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
924     return gdkRGBAColor;
925 }
926
927 Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const
928 {
929     GdkRGBA gdkRGBAColor;
930     gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor);
931     return gdkRGBAColor;
932 }
933
934 Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const
935 {
936     GdkRGBA gdkRGBAColor;
937     gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor);
938     return gdkRGBAColor;
939 }
940
941 Color RenderThemeGtk::systemColor(int cssValueId) const
942 {
943     GdkRGBA gdkRGBAColor;
944
945     switch (cssValueId) {
946     case CSSValueButtontext:
947         gtk_style_context_get_color(getStyleContext(GTK_TYPE_BUTTON), static_cast<GtkStateFlags>(0), &gdkRGBAColor);
948         return gdkRGBAColor;
949     case CSSValueCaptiontext:
950         gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(0), &gdkRGBAColor);
951         return gdkRGBAColor;
952     default:
953         return RenderTheme::systemColor(cssValueId);
954     }
955 }
956
957 } // namespace WebCore
958
959 #endif // !GTK_API_VERSION_2