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