Home | History | Annotate | Download | only in gtk
      1 /*
      2  * Copyright (C) 2007 Apple Inc.
      3  * Copyright (C) 2007 Alp Toker <alp (at) 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 #ifdef GTK_API_VERSION_2
     29 
     30 // We need this to allow building while using GTK_WIDGET_SET_FLAGS. It's deprecated
     31 // but some theme engines require it to ensure proper rendering of focus indicators.
     32 #undef GTK_DISABLE_DEPRECATED
     33 
     34 #include "CSSValueKeywords.h"
     35 #include "GraphicsContext.h"
     36 #include "GtkVersioning.h"
     37 #include "HTMLNames.h"
     38 #include "MediaControlElements.h"
     39 #include "PaintInfo.h"
     40 #include "RenderObject.h"
     41 #include "TextDirection.h"
     42 #include "UserAgentStyleSheets.h"
     43 #include "WidgetRenderingContext.h"
     44 #include <gdk/gdk.h>
     45 #include <gtk/gtk.h>
     46 
     47 namespace WebCore {
     48 
     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 // This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h.
     53 extern GtkTextDirection gtkTextDirection(TextDirection);
     54 
     55 void RenderThemeGtk::platformInit()
     56 {
     57     m_themePartsHaveRGBAColormap = true;
     58     m_gtkWindow = 0;
     59     m_gtkContainer = 0;
     60     m_gtkButton = 0;
     61     m_gtkEntry = 0;
     62     m_gtkTreeView = 0;
     63     m_gtkVScale = 0;
     64     m_gtkHScale = 0;
     65     m_gtkRadioButton = 0;
     66     m_gtkCheckButton = 0;
     67     m_gtkProgressBar = 0;
     68     m_gtkComboBox = 0;
     69     m_gtkComboBoxButton = 0;
     70     m_gtkComboBoxArrow = 0;
     71     m_gtkComboBoxSeparator = 0;
     72     m_gtkVScrollbar = 0;
     73     m_gtkHScrollbar = 0;
     74     m_gtkSpinButton = 0;
     75 
     76     m_colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default());
     77     if (!m_colormap) {
     78         m_themePartsHaveRGBAColormap = false;
     79         m_colormap = gdk_screen_get_default_colormap(gdk_screen_get_default());
     80     }
     81 }
     82 
     83 RenderThemeGtk::~RenderThemeGtk()
     84 {
     85     if (m_gtkWindow)
     86         gtk_widget_destroy(m_gtkWindow);
     87 }
     88 
     89 #if ENABLE(VIDEO)
     90 void RenderThemeGtk::initMediaColors()
     91 {
     92     GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkContainer()));
     93     m_panelColor = style->bg[GTK_STATE_NORMAL];
     94     m_sliderColor = style->bg[GTK_STATE_ACTIVE];
     95     m_sliderThumbColor = style->bg[GTK_STATE_SELECTED];
     96 }
     97 #endif
     98 
     99 static void adjustRectForFocus(GtkWidget* widget, IntRect& rect, bool ignoreInteriorFocusProperty = false)
    100 {
    101     gint focusWidth, focusPad;
    102     gboolean interiorFocus = 0;
    103     gtk_widget_style_get(widget,
    104                          "interior-focus", &interiorFocus,
    105                          "focus-line-width", &focusWidth,
    106                          "focus-padding", &focusPad, NULL);
    107     if (!ignoreInteriorFocusProperty && interiorFocus)
    108         return;
    109     rect.inflate(focusWidth + focusPad);
    110 }
    111 
    112 void RenderThemeGtk::adjustRepaintRect(const RenderObject* renderObject, IntRect& rect)
    113 {
    114     ControlPart part = renderObject->style()->appearance();
    115     switch (part) {
    116     case CheckboxPart:
    117     case RadioPart: {
    118         // We ignore the interior focus property and always expand the focus rect. In GTK+, the
    119         // focus indicator is usually on the text next to a checkbox or radio button, but that doesn't
    120         // happen in WebCore. By expanding the focus rectangle unconditionally we increase its prominence.
    121         adjustRectForFocus(part == CheckboxPart ? gtkCheckButton() : gtkRadioButton(), rect, true);
    122         return;
    123     }
    124     case InnerSpinButtonPart:
    125         // See paintInnerSpinButton for an explanation of why we expand the painting rect.
    126         rect.inflateY(2);
    127         rect.setWidth(rect.width() + 2);
    128     default:
    129         return;
    130     }
    131 }
    132 
    133 static GtkStateType getGtkStateType(RenderThemeGtk* theme, RenderObject* object)
    134 {
    135     if (!theme->isEnabled(object) || theme->isReadOnlyControl(object))
    136         return GTK_STATE_INSENSITIVE;
    137     if (theme->isPressed(object))
    138         return GTK_STATE_ACTIVE;
    139     if (theme->isHovered(object))
    140         return GTK_STATE_PRELIGHT;
    141     return GTK_STATE_NORMAL;
    142 }
    143 
    144 static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, GtkWidget* widget)
    145 {
    146     // The width and height are both specified, so we shouldn't change them.
    147     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
    148         return;
    149 
    150     gint indicatorSize;
    151     gtk_widget_style_get(widget, "indicator-size", &indicatorSize, NULL);
    152     if (style->width().isIntrinsicOrAuto())
    153         style->setWidth(Length(indicatorSize, Fixed));
    154     if (style->height().isAuto())
    155         style->setHeight(Length(indicatorSize, Fixed));
    156 }
    157 
    158 static void paintToggle(RenderThemeGtk* theme, RenderObject* renderObject, const PaintInfo& info, const IntRect& rect, GtkWidget* widget)
    159 {
    160     // We do not call gtk_toggle_button_set_active here, because some themes begin a series of
    161     // animation frames in a "toggled" signal handler. This puts some checkboxes in a half-way
    162     // checked state. Every GTK+ theme I tested merely looks at the shadow type (and not the
    163     // 'active' property) to determine whether or not to draw the check.
    164     gtk_widget_set_sensitive(widget, theme->isEnabled(renderObject) && !theme->isReadOnlyControl(renderObject));
    165     gtk_widget_set_direction(widget, gtkTextDirection(renderObject->style()->direction()));
    166 
    167     bool indeterminate = theme->isIndeterminate(renderObject);
    168     gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(widget), indeterminate);
    169 
    170     GtkShadowType shadowType = GTK_SHADOW_OUT;
    171     if (indeterminate) // This originates from the Mozilla code.
    172         shadowType = GTK_SHADOW_ETCHED_IN;
    173     else if (theme->isChecked(renderObject))
    174         shadowType = GTK_SHADOW_IN;
    175 
    176     WidgetRenderingContext widgetContext(info.context, rect);
    177     IntRect buttonRect(IntPoint(), rect.size());
    178     GtkStateType toggleState = getGtkStateType(theme, renderObject);
    179     const char* detail = 0;
    180     if (GTK_IS_RADIO_BUTTON(widget)) {
    181         detail = "radiobutton";
    182         widgetContext.gtkPaintOption(buttonRect, widget, toggleState, shadowType, detail);
    183     } else {
    184         detail = "checkbutton";
    185         widgetContext.gtkPaintCheck(buttonRect, widget, toggleState, shadowType, detail);
    186     }
    187 
    188     if (theme->isFocused(renderObject)) {
    189         IntRect focusRect(buttonRect);
    190         adjustRectForFocus(widget, focusRect, true);
    191         widgetContext.gtkPaintFocus(focusRect, widget, toggleState, detail);
    192     }
    193 }
    194 
    195 void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
    196 {
    197     setToggleSize(this, style, gtkCheckButton());
    198 }
    199 
    200 bool RenderThemeGtk::paintCheckbox(RenderObject* renderObject, const PaintInfo& info, const IntRect& rect)
    201 {
    202     paintToggle(this, renderObject, info, rect, gtkCheckButton());
    203     return false;
    204 }
    205 
    206 void RenderThemeGtk::setRadioSize(RenderStyle* style) const
    207 {
    208     setToggleSize(this, style, gtkRadioButton());
    209 }
    210 
    211 bool RenderThemeGtk::paintRadio(RenderObject* renderObject, const PaintInfo& info, const IntRect& rect)
    212 {
    213     paintToggle(this, renderObject, info, rect, gtkRadioButton());
    214     return false;
    215 }
    216 
    217 static void setWidgetHasFocus(GtkWidget* widget, gboolean hasFocus)
    218 {
    219     g_object_set(widget, "has-focus", hasFocus, NULL);
    220 
    221     // These functions are deprecated in GTK+ 2.22, yet theme engines still look
    222     // at these flags when determining if a widget has focus, so we must use them.
    223     if (hasFocus)
    224         GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
    225     else
    226         GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
    227 }
    228 
    229 bool RenderThemeGtk::paintButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
    230 {
    231     if (info.context->paintingDisabled())
    232         return false;
    233 
    234     GtkWidget* widget = gtkButton();
    235     IntRect buttonRect(IntPoint(), rect.size());
    236     IntRect focusRect(buttonRect);
    237 
    238     GtkStateType state = getGtkStateType(this, object);
    239     gtk_widget_set_state(widget, state);
    240     gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
    241 
    242     if (isFocused(object)) {
    243         setWidgetHasFocus(widget, TRUE);
    244 
    245         gboolean interiorFocus = 0, focusWidth = 0, focusPadding = 0;
    246         gtk_widget_style_get(widget,
    247                              "interior-focus", &interiorFocus,
    248                              "focus-line-width", &focusWidth,
    249                              "focus-padding", &focusPadding, NULL);
    250         // If we are using exterior focus, we shrink the button rect down before
    251         // drawing. If we are using interior focus we shrink the focus rect. This
    252         // approach originates from the Mozilla theme drawing code (gtk2drawing.c).
    253         if (interiorFocus) {
    254             GtkStyle* style = gtk_widget_get_style(widget);
    255             focusRect.inflateX(-style->xthickness - focusPadding);
    256             focusRect.inflateY(-style->ythickness - focusPadding);
    257         } else {
    258             buttonRect.inflateX(-focusWidth - focusPadding);
    259             buttonRect.inflateY(-focusPadding - focusPadding);
    260         }
    261     }
    262 
    263     WidgetRenderingContext widgetContext(info.context, rect);
    264     GtkShadowType shadowType = state == GTK_STATE_ACTIVE ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
    265     widgetContext.gtkPaintBox(buttonRect, widget, state, shadowType, "button");
    266     if (isFocused(object))
    267         widgetContext.gtkPaintFocus(focusRect, widget, state, "button");
    268 
    269     setWidgetHasFocus(widget, FALSE);
    270     return false;
    271 }
    272 
    273 int RenderThemeGtk::getComboBoxSeparatorWidth() const
    274 {
    275     GtkWidget* separator = gtkComboBoxSeparator();
    276     if (!separator)
    277         return 0;
    278 
    279     gboolean hasWideSeparators = FALSE;
    280     gint separatorWidth = 0;
    281     gtk_widget_style_get(separator,
    282                          "wide-separators", &hasWideSeparators,
    283                          "separator-width", &separatorWidth,
    284                          NULL);
    285     if (hasWideSeparators)
    286         return separatorWidth;
    287     return gtk_widget_get_style(separator)->xthickness;
    288 }
    289 
    290 int RenderThemeGtk::comboBoxArrowSize(RenderStyle* style) const
    291 {
    292     // Taking the font size and reversing the DPI conversion seems to match
    293     // GTK+ rendering as closely as possible.
    294     return style->font().size() * (72.0 / RenderThemeGtk::getScreenDPI());
    295 }
    296 
    297 static void getButtonInnerBorder(GtkWidget* button, int& left, int& top, int& right, int& bottom)
    298 {
    299     GtkStyle* style = gtk_widget_get_style(button);
    300     int outerBorder = gtk_container_get_border_width(GTK_CONTAINER(button));
    301     static GtkBorder defaultInnerBorder = {1, 1, 1, 1};
    302     GtkBorder* innerBorder;
    303     gtk_widget_style_get(button, "inner-border", &innerBorder, NULL);
    304     if (!innerBorder)
    305         innerBorder = &defaultInnerBorder;
    306 
    307     left = outerBorder + innerBorder->left + style->xthickness;
    308     right = outerBorder + innerBorder->right + style->xthickness;
    309     top = outerBorder + innerBorder->top + style->ythickness;
    310     bottom = outerBorder + innerBorder->bottom + style->ythickness;
    311 
    312     if (innerBorder != &defaultInnerBorder)
    313         gtk_border_free(innerBorder);
    314 }
    315 
    316 
    317 void RenderThemeGtk::getComboBoxPadding(RenderStyle* style, int& left, int& top, int& right, int& bottom) const
    318 {
    319     // If this menu list button isn't drawn using the native theme, we
    320     // don't add any extra padding beyond what WebCore already uses.
    321     if (style->appearance() == NoControlPart)
    322         return;
    323 
    324     // A combo box button is a button with widgets packed into it.
    325     GtkStyle* buttonWidgetStyle = gtk_widget_get_style(gtkComboBoxButton());
    326     getButtonInnerBorder(gtkComboBoxButton(), left, top, right, bottom);
    327 
    328     // Add xthickness amount of padding for each side of the separator. This ensures
    329     // that the text does not bump up against the separator.
    330     int arrowAndSeperatorLength = comboBoxArrowSize(style) +
    331         getComboBoxSeparatorWidth() + (3 * buttonWidgetStyle->xthickness);
    332 
    333     if (style->direction() == RTL)
    334         left += arrowAndSeperatorLength;
    335     else
    336         right += arrowAndSeperatorLength;
    337 }
    338 
    339 int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const
    340 {
    341     int left = 0, top = 0, right = 0, bottom = 0;
    342     getComboBoxPadding(style, left, top, right, bottom);
    343     return left;
    344 }
    345 
    346 int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const
    347 {
    348     int left = 0, top = 0, right = 0, bottom = 0;
    349     getComboBoxPadding(style, left, top, right, bottom);
    350     return right;
    351 }
    352 
    353 int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const
    354 {
    355     int left = 0, top = 0, right = 0, bottom = 0;
    356     getComboBoxPadding(style, left, top, right, bottom);
    357     return top;
    358 }
    359 
    360 int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const
    361 {
    362     int left = 0, top = 0, right = 0, bottom = 0;
    363     getComboBoxPadding(style, left, top, right, bottom);
    364     return bottom;
    365 }
    366 
    367 bool RenderThemeGtk::paintMenuList(RenderObject* object, const PaintInfo& info, const IntRect& rect)
    368 {
    369     if (paintButton(object, info, rect))
    370         return true;
    371 
    372     // Menu list button painting strategy.
    373     // For buttons with appears-as-list set to false (having a separator):
    374     // | left border | Button text | xthickness | vseparator | xthickness | arrow | xthickness | right border |
    375     // For buttons with appears-as-list set to true (not having a separator):
    376     // | left border | Button text | arrow | xthickness | right border |
    377 
    378     int leftBorder = 0, rightBorder = 0, bottomBorder = 0, topBorder = 0;
    379     getButtonInnerBorder(gtkComboBoxButton(), leftBorder, topBorder, rightBorder, bottomBorder);
    380     RenderStyle* style = object->style();
    381     int arrowSize = comboBoxArrowSize(style);
    382     GtkStyle* buttonStyle = gtk_widget_get_style(gtkComboBoxButton());
    383 
    384     IntRect arrowRect(0, (rect.height() - arrowSize) / 2, arrowSize, arrowSize);
    385     if (style->direction() == RTL)
    386         arrowRect.setX(leftBorder + buttonStyle->xthickness);
    387     else
    388         arrowRect.setX(rect.width() - rightBorder - buttonStyle->xthickness - arrowSize);
    389     GtkShadowType shadowType = isPressed(object) ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
    390 
    391     WidgetRenderingContext widgetContext(info.context, rect);
    392     GtkStateType stateType = getGtkStateType(this, object);
    393     widgetContext.gtkPaintArrow(arrowRect, gtkComboBoxArrow(), stateType, shadowType, GTK_ARROW_DOWN, "arrow");
    394 
    395     // Some combo boxes do not have a separator.
    396     GtkWidget* separator = gtkComboBoxSeparator();
    397     if (!separator)
    398         return false;
    399 
    400     // We want to decrease the height of the separator based on the focus padding of the button.
    401     gint focusPadding = 0, focusWidth = 0;
    402     gtk_widget_style_get(gtkComboBoxButton(),
    403                          "focus-line-width", &focusWidth,
    404                          "focus-padding", &focusPadding, NULL);
    405     topBorder += focusPadding + focusWidth;
    406     bottomBorder += focusPadding + focusWidth;
    407     int separatorWidth = getComboBoxSeparatorWidth();
    408     IntRect separatorRect(0, topBorder, separatorWidth, rect.height() - topBorder - bottomBorder);
    409     if (style->direction() == RTL)
    410         separatorRect.setX(arrowRect.x() + arrowRect.width() + buttonStyle->xthickness + separatorWidth);
    411     else
    412         separatorRect.setX(arrowRect.x() - buttonStyle->xthickness - separatorWidth);
    413 
    414     gboolean hasWideSeparators = FALSE;
    415     gtk_widget_style_get(separator, "wide-separators", &hasWideSeparators, NULL);
    416     if (hasWideSeparators)
    417         widgetContext.gtkPaintBox(separatorRect, separator, GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT, "vseparator");
    418     else
    419         widgetContext.gtkPaintVLine(separatorRect, separator, GTK_STATE_NORMAL, "vseparator");
    420 
    421     return false;
    422 }
    423 
    424 bool RenderThemeGtk::paintTextField(RenderObject* renderObject, const PaintInfo& info, const IntRect& rect)
    425 {
    426     GtkWidget* widget = gtkEntry();
    427 
    428     bool enabled = isEnabled(renderObject) && !isReadOnlyControl(renderObject);
    429     GtkStateType backgroundState = enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE;
    430     gtk_widget_set_sensitive(widget, enabled);
    431     gtk_widget_set_direction(widget, gtkTextDirection(renderObject->style()->direction()));
    432     setWidgetHasFocus(widget, isFocused(renderObject));
    433 
    434     WidgetRenderingContext widgetContext(info.context, rect);
    435     IntRect textFieldRect(IntPoint(), rect.size());
    436 
    437     // The entry background is only painted over the interior part of the GTK+ entry, not
    438     // the entire frame. This happens in the Mozilla theme drawing code as well.
    439     IntRect interiorRect(textFieldRect);
    440     GtkStyle* style = gtk_widget_get_style(widget);
    441     interiorRect.inflateX(-style->xthickness);
    442     interiorRect.inflateY(-style->ythickness);
    443     widgetContext.gtkPaintFlatBox(interiorRect, widget, backgroundState, GTK_SHADOW_NONE, "entry_bg");
    444 
    445     // This is responsible for drawing the actual frame.
    446     widgetContext.gtkPaintShadow(textFieldRect, widget, GTK_STATE_NORMAL, GTK_SHADOW_IN, "entry");
    447 
    448     gboolean interiorFocus;
    449     gint focusWidth;
    450     gtk_widget_style_get(widget,
    451                          "interior-focus", &interiorFocus,
    452                          "focus-line-width", &focusWidth,  NULL);
    453     if (isFocused(renderObject) && !interiorFocus) {
    454         // When GTK+ paints a text entry with focus, it shrinks the size of the frame area by the
    455         // focus width and paints over the previously unfocused text entry. We need to emulate that
    456         // by drawing both the unfocused frame above and the focused frame here.
    457         IntRect shadowRect(textFieldRect);
    458         shadowRect.inflate(-focusWidth);
    459         widgetContext.gtkPaintShadow(shadowRect, widget, GTK_STATE_NORMAL, GTK_SHADOW_IN, "entry");
    460 
    461         widgetContext.gtkPaintFocus(textFieldRect, widget, GTK_STATE_NORMAL, "entry");
    462     }
    463 
    464     return false;
    465 }
    466 
    467 bool RenderThemeGtk::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
    468 {
    469     if (info.context->paintingDisabled())
    470         return false;
    471 
    472     ControlPart part = object->style()->appearance();
    473     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart);
    474 
    475     // We shrink the trough rect slightly to make room for the focus indicator.
    476     IntRect troughRect(IntPoint(), rect.size()); // This is relative to rect.
    477     GtkWidget* widget = 0;
    478     if (part == SliderHorizontalPart) {
    479         widget = gtkHScale();
    480         troughRect.inflateX(-gtk_widget_get_style(widget)->xthickness);
    481     } else {
    482         widget = gtkVScale();
    483         troughRect.inflateY(-gtk_widget_get_style(widget)->ythickness);
    484     }
    485     gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
    486 
    487     WidgetRenderingContext widgetContext(info.context, rect);
    488     widgetContext.gtkPaintBox(troughRect, widget, GTK_STATE_ACTIVE, GTK_SHADOW_OUT, "trough");
    489     if (isFocused(object))
    490         widgetContext.gtkPaintFocus(IntRect(IntPoint(), rect.size()), widget, getGtkStateType(this, object), "trough");
    491 
    492     return false;
    493 }
    494 
    495 bool RenderThemeGtk::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
    496 {
    497     if (info.context->paintingDisabled())
    498         return false;
    499 
    500     ControlPart part = object->style()->appearance();
    501     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
    502 
    503     GtkWidget* widget = 0;
    504     const char* detail = 0;
    505     GtkOrientation orientation;
    506     if (part == SliderThumbHorizontalPart) {
    507         widget = gtkHScale();
    508         detail = "hscale";
    509         orientation = GTK_ORIENTATION_HORIZONTAL;
    510     } else {
    511         widget = gtkVScale();
    512         detail = "vscale";
    513         orientation = GTK_ORIENTATION_VERTICAL;
    514     }
    515     gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
    516 
    517     // Only some themes have slider thumbs respond to clicks and some don't. This information is
    518     // gathered via the 'activate-slider' property, but it's deprecated in GTK+ 2.22 and removed in
    519     // GTK+ 3.x. The drawback of not honoring it is that slider thumbs change color when you click
    520     // on them.
    521     IntRect thumbRect(IntPoint(), rect.size());
    522     WidgetRenderingContext widgetContext(info.context, rect);
    523     widgetContext.gtkPaintSlider(thumbRect, widget, getGtkStateType(this, object), GTK_SHADOW_OUT, detail, orientation);
    524     return false;
    525 }
    526 
    527 void RenderThemeGtk::adjustSliderThumbSize(RenderObject* o) const
    528 {
    529     ControlPart part = o->style()->appearance();
    530 #if ENABLE(VIDEO)
    531     if (part == MediaSliderThumbPart) {
    532         adjustMediaSliderThumbSize(o);
    533         return;
    534     }
    535 #endif
    536 
    537     GtkWidget* widget = part == SliderThumbHorizontalPart ? gtkHScale() : gtkVScale();
    538     int length = 0, width = 0;
    539     gtk_widget_style_get(widget,
    540                          "slider_length", &length,
    541                          "slider_width", &width,
    542                          NULL);
    543 
    544     if (part == SliderThumbHorizontalPart) {
    545         o->style()->setWidth(Length(length, Fixed));
    546         o->style()->setHeight(Length(width, Fixed));
    547         return;
    548     }
    549     ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
    550     o->style()->setWidth(Length(width, Fixed));
    551     o->style()->setHeight(Length(length, Fixed));
    552 }
    553 
    554 #if ENABLE(PROGRESS_TAG)
    555 bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
    556 {
    557     GtkWidget* widget = gtkProgressBar();
    558     gtk_widget_set_direction(widget, gtkTextDirection(renderObject->style()->direction()));
    559 
    560     WidgetRenderingContext widgetContext(paintInfo.context, rect);
    561     IntRect fullProgressBarRect(IntPoint(), rect.size());
    562     widgetContext.gtkPaintBox(fullProgressBarRect, widget, GTK_STATE_NORMAL, GTK_SHADOW_IN, "trough");
    563 
    564     GtkStyle* style = gtk_widget_get_style(widget);
    565     IntRect progressRect(fullProgressBarRect);
    566     progressRect.inflateX(-style->xthickness);
    567     progressRect.inflateY(-style->ythickness);
    568     progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect);
    569 
    570     if (!progressRect.isEmpty())
    571         widgetContext.gtkPaintBox(progressRect, widget, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, "bar");
    572 
    573     return false;
    574 }
    575 #endif
    576 
    577 void RenderThemeGtk::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
    578 {
    579     GtkStyle* gtkStyle = gtk_widget_get_style(gtkSpinButton());
    580     const PangoFontDescription* fontDescription = gtkStyle->font_desc;
    581     gint fontSize = pango_font_description_get_size(fontDescription);
    582 
    583     // Force an odd arrow size here. GTK+ 3.x forces even in this case, but
    584     // Nodoka-based themes look incorrect with an even arrow size.
    585     int width = max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize);
    586     width += -((width % 2) - 1) + gtkStyle->xthickness;
    587 
    588     style->setWidth(Length(width, Fixed));
    589     style->setMinWidth(Length(width, Fixed));
    590 }
    591 
    592 bool RenderThemeGtk::paintInnerSpinButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
    593 {
    594     // We expand the painted area by 2 pixels on the top and bottom and 2 pixels on the right. This
    595     // is because GTK+ themes want to draw over the text box borders, but WebCore renders the inner
    596     // spin button inside the text box.
    597     IntRect expandedRect(rect);
    598     expandedRect.inflateY(2);
    599     expandedRect.setWidth(rect.width() + 2);
    600 
    601     WidgetRenderingContext widgetContext(paintInfo.context, expandedRect);
    602     GtkWidget* widget = gtkSpinButton();
    603     gtk_widget_set_direction(widget, gtkTextDirection(renderObject->style()->direction()));
    604 
    605     IntRect fullSpinButtonRect(IntPoint(), expandedRect.size());
    606     widgetContext.gtkPaintBox(fullSpinButtonRect, widget, GTK_STATE_NORMAL, GTK_SHADOW_IN, "spinbutton");
    607 
    608     bool upPressed = isSpinUpButtonPartPressed(renderObject);
    609     bool upHovered = isSpinUpButtonPartHovered(renderObject);
    610     bool controlActive = isEnabled(renderObject) && !isReadOnlyControl(renderObject);
    611     GtkShadowType shadowType = upPressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
    612 
    613     GtkStateType stateType = GTK_STATE_INSENSITIVE;
    614     if (controlActive) {
    615         if (isPressed(renderObject) && upPressed)
    616             stateType = GTK_STATE_ACTIVE;
    617         else if (isHovered(renderObject) && upHovered)
    618             stateType = GTK_STATE_PRELIGHT;
    619         else
    620             stateType = GTK_STATE_NORMAL;
    621     }
    622     IntRect topRect(IntPoint(), expandedRect.size());
    623     topRect.setHeight(expandedRect.height() / 2);
    624     widgetContext.gtkPaintBox(topRect, widget, stateType, shadowType, "spinbutton_up");
    625 
    626     // The arrow size/position calculation here is based on the arbitrary gymnastics that happen
    627     // in gtkspinbutton.c. It isn't pretty there and it isn't pretty here. This manages to make
    628     // the button look native for many themes though.
    629     IntRect arrowRect;
    630     int arrowSize = (expandedRect.width() - 3) / 2;
    631     arrowSize -= (arrowSize % 2) - 1; // Force odd.
    632     arrowRect.setWidth(arrowSize);
    633     arrowRect.setHeight(arrowSize);
    634     arrowRect.move((expandedRect.width() - arrowRect.width()) / 2,
    635                    (topRect.height() - arrowRect.height()) / 2 + 1);
    636     widgetContext.gtkPaintArrow(arrowRect, widget, stateType, shadowType, GTK_ARROW_UP, "spinbutton");
    637 
    638     shadowType = isPressed(renderObject) && !upPressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
    639     if (controlActive) {
    640         if (isPressed(renderObject) && !upPressed)
    641             stateType = GTK_STATE_ACTIVE;
    642         else if (isHovered(renderObject) && !upHovered)
    643             stateType = GTK_STATE_PRELIGHT;
    644         else
    645             stateType = GTK_STATE_NORMAL;
    646     }
    647     IntRect bottomRect(IntPoint(0, expandedRect.height() / 2), expandedRect.size());
    648     bottomRect.setHeight(expandedRect.height() - bottomRect.y());
    649     widgetContext.gtkPaintBox(bottomRect, widget, stateType, shadowType, "spinbutton_down");
    650 
    651     arrowRect.setY(arrowRect.y() + bottomRect.y() - 1);
    652     widgetContext.gtkPaintArrow(arrowRect, widget, stateType, shadowType, GTK_ARROW_DOWN, "spinbutton");
    653 
    654     return false;
    655 }
    656 
    657 GRefPtr<GdkPixbuf> RenderThemeGtk::getStockIcon(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize)
    658 {
    659     ASSERT(widgetType == GTK_TYPE_CONTAINER || widgetType == GTK_TYPE_ENTRY);
    660     GtkWidget* widget = widgetType == GTK_TYPE_CONTAINER ? GTK_WIDGET(gtkContainer()) : gtkEntry();
    661     GtkStyle* style = gtk_widget_get_style(widget);
    662     GtkIconSet* iconSet = gtk_style_lookup_icon_set(style, iconName);
    663     return adoptGRef(gtk_icon_set_render_icon(iconSet, style,
    664                                               static_cast<GtkTextDirection>(direction),
    665                                               static_cast<GtkStateType>(state),
    666                                               static_cast<GtkIconSize>(iconSize), 0, 0));
    667 }
    668 
    669 
    670 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
    671 {
    672     GtkWidget* widget = gtkEntry();
    673     return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED];
    674 }
    675 
    676 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
    677 {
    678     GtkWidget* widget = gtkEntry();
    679     return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE];
    680 }
    681 
    682 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
    683 {
    684     GtkWidget* widget = gtkEntry();
    685     return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED];
    686 }
    687 
    688 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
    689 {
    690     GtkWidget* widget = gtkEntry();
    691     return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE];
    692 }
    693 
    694 Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const
    695 {
    696     GtkWidget* widget = gtkTreeView();
    697     return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED];
    698 }
    699 
    700 Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const
    701 {
    702     GtkWidget* widget = gtkTreeView();
    703     return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE];
    704 }
    705 
    706 Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const
    707 {
    708     GtkWidget* widget = gtkTreeView();
    709     return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED];
    710 }
    711 
    712 Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const
    713 {
    714     GtkWidget* widget = gtkTreeView();
    715     return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE];
    716 }
    717 
    718 Color RenderThemeGtk::systemColor(int cssValueId) const
    719 {
    720     switch (cssValueId) {
    721     case CSSValueButtontext:
    722         return Color(gtk_widget_get_style(gtkButton())->fg[GTK_STATE_NORMAL]);
    723     case CSSValueCaptiontext:
    724         return Color(gtk_widget_get_style(gtkEntry())->fg[GTK_STATE_NORMAL]);
    725     default:
    726         return RenderTheme::systemColor(cssValueId);
    727     }
    728 }
    729 
    730 static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, RenderTheme* renderTheme)
    731 {
    732     // FIXME: Make sure this function doesn't get called many times for a single GTK+ style change signal.
    733     renderTheme->platformColorsDidChange();
    734 }
    735 
    736 static void setupWidget(GtkWidget* widget)
    737 {
    738     gtk_widget_realize(widget);
    739     g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
    740 }
    741 
    742 void RenderThemeGtk::setupWidgetAndAddToContainer(GtkWidget* widget, GtkWidget* window) const
    743 {
    744     gtk_container_add(GTK_CONTAINER(window), widget);
    745     setupWidget(widget);
    746 
    747     // FIXME: Perhaps this should only be called for the containing window or parent container.
    748     g_signal_connect(widget, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
    749 }
    750 
    751 GtkWidget* RenderThemeGtk::gtkContainer() const
    752 {
    753     if (m_gtkContainer)
    754         return m_gtkContainer;
    755 
    756     m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP);
    757     gtk_widget_set_colormap(m_gtkWindow, m_colormap);
    758     setupWidget(m_gtkWindow);
    759     gtk_widget_set_name(m_gtkWindow, "MozillaGtkWidget");
    760 
    761     m_gtkContainer = gtk_fixed_new();
    762     setupWidgetAndAddToContainer(m_gtkContainer, m_gtkWindow);
    763     return m_gtkContainer;
    764 }
    765 
    766 GtkWidget* RenderThemeGtk::gtkButton() const
    767 {
    768     if (m_gtkButton)
    769         return m_gtkButton;
    770     m_gtkButton = gtk_button_new();
    771     setupWidgetAndAddToContainer(m_gtkButton, gtkContainer());
    772     return m_gtkButton;
    773 }
    774 
    775 GtkWidget* RenderThemeGtk::gtkEntry() const
    776 {
    777     if (m_gtkEntry)
    778         return m_gtkEntry;
    779     m_gtkEntry = gtk_entry_new();
    780     setupWidgetAndAddToContainer(m_gtkEntry, gtkContainer());
    781     return m_gtkEntry;
    782 }
    783 
    784 GtkWidget* RenderThemeGtk::gtkTreeView() const
    785 {
    786     if (m_gtkTreeView)
    787         return m_gtkTreeView;
    788     m_gtkTreeView = gtk_tree_view_new();
    789     setupWidgetAndAddToContainer(m_gtkTreeView, gtkContainer());
    790     return m_gtkTreeView;
    791 }
    792 
    793 GtkWidget* RenderThemeGtk::gtkVScale() const
    794 {
    795     if (m_gtkVScale)
    796         return m_gtkVScale;
    797     m_gtkVScale = gtk_vscale_new(0);
    798     setupWidgetAndAddToContainer(m_gtkVScale, gtkContainer());
    799     return m_gtkVScale;
    800 }
    801 
    802 GtkWidget* RenderThemeGtk::gtkHScale() const
    803 {
    804     if (m_gtkHScale)
    805         return m_gtkHScale;
    806     m_gtkHScale = gtk_hscale_new(0);
    807     setupWidgetAndAddToContainer(m_gtkHScale, gtkContainer());
    808     return m_gtkHScale;
    809 }
    810 
    811 GtkWidget* RenderThemeGtk::gtkRadioButton() const
    812 {
    813     if (m_gtkRadioButton)
    814         return m_gtkRadioButton;
    815     m_gtkRadioButton = gtk_radio_button_new(0);
    816     setupWidgetAndAddToContainer(m_gtkRadioButton, gtkContainer());
    817     return m_gtkRadioButton;
    818 }
    819 
    820 GtkWidget* RenderThemeGtk::gtkCheckButton() const
    821 {
    822     if (m_gtkCheckButton)
    823         return m_gtkCheckButton;
    824     m_gtkCheckButton = gtk_check_button_new();
    825     setupWidgetAndAddToContainer(m_gtkCheckButton, gtkContainer());
    826     return m_gtkCheckButton;
    827 }
    828 
    829 GtkWidget* RenderThemeGtk::gtkProgressBar() const
    830 {
    831     if (m_gtkProgressBar)
    832         return m_gtkProgressBar;
    833     m_gtkProgressBar = gtk_progress_bar_new();
    834     setupWidgetAndAddToContainer(m_gtkProgressBar, gtkContainer());
    835     return m_gtkProgressBar;
    836 }
    837 
    838 static void getGtkComboBoxButton(GtkWidget* widget, gpointer target)
    839 {
    840     if (!GTK_IS_TOGGLE_BUTTON(widget))
    841         return;
    842     GtkWidget** widgetTarget = static_cast<GtkWidget**>(target);
    843     *widgetTarget = widget;
    844 }
    845 
    846 typedef struct {
    847     GtkWidget* arrow;
    848     GtkWidget* separator;
    849 } ComboBoxWidgetPieces;
    850 
    851 static void getGtkComboBoxPieces(GtkWidget* widget, gpointer data)
    852 {
    853     if (GTK_IS_ARROW(widget)) {
    854         static_cast<ComboBoxWidgetPieces*>(data)->arrow = widget;
    855         return;
    856     }
    857     if (GTK_IS_SEPARATOR(widget))
    858         static_cast<ComboBoxWidgetPieces*>(data)->separator = widget;
    859 }
    860 
    861 GtkWidget* RenderThemeGtk::gtkComboBox() const
    862 {
    863     if (m_gtkComboBox)
    864         return m_gtkComboBox;
    865     m_gtkComboBox = gtk_combo_box_new();
    866     setupWidgetAndAddToContainer(m_gtkComboBox, gtkContainer());
    867     return m_gtkComboBox;
    868 }
    869 
    870 void RenderThemeGtk::refreshComboBoxChildren() const
    871 {
    872     gtkComboBox(); // Ensure that we've initialized the combo box.
    873 
    874     // Some themes look at widget ancestry to determine how to render widgets, so
    875     // get the GtkButton that is the actual child of the combo box.
    876     gtk_container_forall(GTK_CONTAINER(m_gtkComboBox), getGtkComboBoxButton, &m_gtkComboBoxButton);
    877     ASSERT(m_gtkComboBoxButton);
    878     setupWidget(m_gtkComboBoxButton);
    879     g_object_add_weak_pointer(G_OBJECT(m_gtkComboBoxButton), reinterpret_cast<gpointer*>(&m_gtkComboBoxButton));
    880 
    881     ComboBoxWidgetPieces pieces = { 0, 0 };
    882     GtkWidget* buttonChild = gtk_bin_get_child(GTK_BIN(gtkComboBoxButton()));
    883     if (GTK_IS_HBOX(buttonChild))
    884         gtk_container_forall(GTK_CONTAINER(buttonChild), getGtkComboBoxPieces, &pieces);
    885     else if (GTK_IS_ARROW(buttonChild))
    886         pieces.arrow = buttonChild;
    887 
    888     ASSERT(pieces.arrow);
    889     m_gtkComboBoxArrow = pieces.arrow;
    890     setupWidget(m_gtkComboBoxArrow);
    891     // When the style changes, the combo box may destroy its children.
    892     g_object_add_weak_pointer(G_OBJECT(m_gtkComboBoxArrow), reinterpret_cast<gpointer*>(&m_gtkComboBoxArrow));
    893 
    894     m_gtkComboBoxSeparator = pieces.separator;
    895     if (m_gtkComboBoxSeparator) {
    896         setupWidget(m_gtkComboBoxSeparator);
    897         // When the style changes, the combo box may destroy its children.
    898         g_object_add_weak_pointer(G_OBJECT(m_gtkComboBoxSeparator), reinterpret_cast<gpointer*>(&m_gtkComboBoxSeparator));
    899     }
    900 }
    901 
    902 GtkWidget* RenderThemeGtk::gtkComboBoxButton() const
    903 {
    904     if (m_gtkComboBoxButton)
    905         return m_gtkComboBoxButton;
    906     refreshComboBoxChildren();
    907     ASSERT(m_gtkComboBoxButton);
    908     return m_gtkComboBoxButton;
    909 }
    910 
    911 GtkWidget* RenderThemeGtk::gtkComboBoxArrow() const
    912 {
    913     if (m_gtkComboBoxArrow)
    914         return m_gtkComboBoxArrow;
    915     refreshComboBoxChildren();
    916     ASSERT(m_gtkComboBoxArrow);
    917     return m_gtkComboBoxArrow;
    918 }
    919 
    920 GtkWidget* RenderThemeGtk::gtkComboBoxSeparator() const
    921 {
    922     // m_gtkComboBoxSeparator may be null either because we haven't initialized the combo box
    923     // or because the combo boxes in this theme don't have separators. If m_gtkComboBoxArrow
    924     // arrow isn't null, we definitely have initialized the combo box.
    925     if (m_gtkComboBoxArrow || m_gtkComboBoxButton)
    926         return m_gtkComboBoxSeparator;
    927     refreshComboBoxChildren();
    928     return m_gtkComboBoxSeparator;
    929 }
    930 
    931 GtkWidget* RenderThemeGtk::gtkHScrollbar() const
    932 {
    933     if (m_gtkHScrollbar)
    934         return m_gtkHScrollbar;
    935     m_gtkHScrollbar = gtk_hscrollbar_new(0);
    936     setupWidgetAndAddToContainer(m_gtkHScrollbar, gtkContainer());
    937     return m_gtkHScrollbar;
    938 }
    939 
    940 GtkWidget* RenderThemeGtk::gtkVScrollbar() const
    941 {
    942     if (m_gtkVScrollbar)
    943         return m_gtkVScrollbar;
    944     m_gtkVScrollbar = gtk_vscrollbar_new(0);
    945     setupWidgetAndAddToContainer(m_gtkVScrollbar, gtkContainer());
    946     return m_gtkVScrollbar;
    947 }
    948 
    949 GtkWidget* RenderThemeGtk::gtkSpinButton() const
    950 {
    951     if (m_gtkSpinButton)
    952         return m_gtkSpinButton;
    953     m_gtkSpinButton = gtk_spin_button_new_with_range(0, 10, 1);
    954     setupWidgetAndAddToContainer(m_gtkSpinButton, gtkContainer());
    955     return m_gtkSpinButton;
    956 }
    957 
    958 } // namespace WebCore
    959 
    960 #endif // GTK_API_VERSION_2
    961