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