2011-01-12 Martin Robinson <mrobinson@igalia.com>
[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 "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 "gtkdrawing.h"
45 #include <gdk/gdk.h>
46 #include <gtk/gtk.h>
47
48 #if ENABLE(PROGRESS_TAG)
49 #include "RenderProgress.h"
50 #endif
51
52 namespace WebCore {
53
54 // This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h.
55 extern GtkTextDirection gtkTextDirection(TextDirection);
56
57 static int mozGtkRefCount = 0;
58 void RenderThemeGtk::platformInit()
59 {
60     m_themePartsHaveRGBAColormap = true;
61     m_gtkWindow = 0;
62     m_gtkContainer = 0;
63     m_gtkButton = 0;
64     m_gtkEntry = 0;
65     m_gtkTreeView = 0;
66     m_gtkVScale = 0;
67     m_gtkHScale = 0;
68     m_gtkRadioButton = 0;
69     m_gtkCheckButton = 0;
70
71     memset(&m_themeParts, 0, sizeof(GtkThemeParts));
72     GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default());
73     if (!colormap) {
74         m_themePartsHaveRGBAColormap = false;
75         colormap = gdk_screen_get_default_colormap(gdk_screen_get_default());
76     }
77     m_themeParts.colormap = colormap;
78
79     // Initialize the Mozilla theme drawing code.
80     if (!mozGtkRefCount) {
81         moz_gtk_init();
82         moz_gtk_use_theme_parts(&m_themeParts);
83     }
84     ++mozGtkRefCount;
85 }
86
87 RenderThemeGtk::~RenderThemeGtk()
88 {
89     --mozGtkRefCount;
90
91     if (!mozGtkRefCount)
92         moz_gtk_shutdown();
93
94     if (m_gtkWindow)
95         gtk_widget_destroy(m_gtkWindow);
96 }
97
98 #if ENABLE(VIDEO)
99 void RenderThemeGtk::initMediaColors()
100 {
101     GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkContainer()));
102     m_panelColor = style->bg[GTK_STATE_NORMAL];
103     m_sliderColor = style->bg[GTK_STATE_ACTIVE];
104     m_sliderThumbColor = style->bg[GTK_STATE_SELECTED];
105 }
106 #endif
107
108 static void adjustRectForFocus(GtkWidget* widget, IntRect& rect, bool ignoreInteriorFocusProperty = false)
109 {
110     gint focusWidth, focusPad;
111     gboolean interiorFocus = 0;
112     gtk_widget_style_get(widget,
113                          "interior-focus", &interiorFocus,
114                          "focus-line-width", &focusWidth,
115                          "focus-padding", &focusPad, NULL);
116     if (!ignoreInteriorFocusProperty && interiorFocus)
117         return;
118     rect.inflate(focusWidth + focusPad);
119 }
120
121 void RenderThemeGtk::adjustRepaintRect(const RenderObject* renderObject, IntRect& rect)
122 {
123     ControlPart part = renderObject->style()->appearance();
124     switch (part) {
125     case CheckboxPart:
126     case RadioPart: {
127         // We ignore the interior focus property and always expand the focus rect. In GTK+, the
128         // focus indicator is usually on the text next to a checkbox or radio button, but that doesn't
129         // happen in WebCore. By expanding the focus rectangle unconditionally we increase its prominence.
130         adjustRectForFocus(part == CheckboxPart ? gtkCheckButton() : gtkRadioButton(), rect, true);
131         return;
132     }
133     default:
134         return;
135     }
136 }
137
138 static GtkStateType getGtkStateType(RenderThemeGtk* theme, RenderObject* object)
139 {
140     if (!theme->isEnabled(object) || theme->isReadOnlyControl(object))
141         return GTK_STATE_INSENSITIVE;
142     if (theme->isPressed(object))
143         return GTK_STATE_ACTIVE;
144     if (theme->isHovered(object))
145         return GTK_STATE_PRELIGHT;
146     return GTK_STATE_NORMAL;
147 }
148
149 bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, int flags)
150 {
151     // Painting is disabled so just claim to have succeeded
152     if (context->paintingDisabled())
153         return false;
154
155     GtkWidgetState widgetState;
156     widgetState.active = isPressed(renderObject);
157     widgetState.focused = isFocused(renderObject);
158
159     // https://bugs.webkit.org/show_bug.cgi?id=18364
160     // The Mozilla theme drawing code, only paints a button as pressed when it's pressed 
161     // while hovered. Until we move away from the Mozila code, work-around the issue by
162     // forcing a pressed button into the hovered state. This ensures that buttons activated
163     // via the keyboard have the proper rendering.
164     widgetState.inHover = isHovered(renderObject) || (type == MOZ_GTK_BUTTON && isPressed(renderObject));
165
166     // FIXME: Disabled does not always give the correct appearance for ReadOnly
167     widgetState.disabled = !isEnabled(renderObject) || isReadOnlyControl(renderObject);
168     widgetState.isDefault = false;
169     widgetState.canDefault = false;
170     widgetState.depressed = false;
171
172     WidgetRenderingContext widgetContext(context, rect);
173     return !widgetContext.paintMozillaWidget(type, &widgetState, flags,
174                                              gtkTextDirection(renderObject->style()->direction()));
175 }
176
177 static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, GtkWidget* widget)
178 {
179     // The width and height are both specified, so we shouldn't change them.
180     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
181         return;
182
183     gint indicatorSize;
184     gtk_widget_style_get(widget, "indicator-size", &indicatorSize, NULL);
185     if (style->width().isIntrinsicOrAuto())
186         style->setWidth(Length(indicatorSize, Fixed));
187     if (style->height().isAuto())
188         style->setHeight(Length(indicatorSize, Fixed));
189 }
190
191 static void paintToggle(RenderThemeGtk* theme, RenderObject* renderObject, const PaintInfo& info, const IntRect& rect, GtkWidget* widget)
192 {
193     // We do not call gtk_toggle_button_set_active here, because some themes begin a series of
194     // animation frames in a "toggled" signal handler. This puts some checkboxes in a half-way
195     // checked state. Every GTK+ theme I tested merely looks at the shadow type (and not the
196     // 'active' property) to determine whether or not to draw the check.
197     gtk_widget_set_sensitive(widget, theme->isEnabled(renderObject) && !theme->isReadOnlyControl(renderObject));
198     gtk_widget_set_direction(widget, gtkTextDirection(renderObject->style()->direction()));
199
200     bool indeterminate = theme->isIndeterminate(renderObject);
201     gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(widget), indeterminate);
202
203     GtkShadowType shadowType = GTK_SHADOW_OUT;
204     if (indeterminate) // This originates from the Mozilla code.
205         shadowType = GTK_SHADOW_ETCHED_IN;
206     else if (theme->isChecked(renderObject))
207         shadowType = GTK_SHADOW_IN;
208
209     WidgetRenderingContext widgetContext(info.context, rect);
210     IntRect buttonRect(IntPoint(), rect.size());
211     GtkStateType toggleState = getGtkStateType(theme, renderObject);
212     const char* detail = 0;
213     if (GTK_IS_RADIO_BUTTON(widget)) {
214         detail = "radiobutton";
215         widgetContext.gtkPaintOption(buttonRect, widget, toggleState, shadowType, detail);
216     } else {
217         detail = "checkbutton";
218         widgetContext.gtkPaintCheck(buttonRect, widget, toggleState, shadowType, detail);
219     }
220
221     if (theme->isFocused(renderObject)) {
222         IntRect focusRect(buttonRect);
223         adjustRectForFocus(widget, focusRect, true);
224         widgetContext.gtkPaintFocus(focusRect, widget, toggleState, detail);
225     }
226 }
227
228 void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
229 {
230     setToggleSize(this, style, gtkCheckButton());
231 }
232
233 bool RenderThemeGtk::paintCheckbox(RenderObject* renderObject, const PaintInfo& info, const IntRect& rect)
234 {
235     paintToggle(this, renderObject, info, rect, gtkCheckButton());
236     return false;
237 }
238
239 void RenderThemeGtk::setRadioSize(RenderStyle* style) const
240 {
241     setToggleSize(this, style, gtkRadioButton());
242 }
243
244 bool RenderThemeGtk::paintRadio(RenderObject* renderObject, const PaintInfo& info, const IntRect& rect)
245 {
246     paintToggle(this, renderObject, info, rect, gtkRadioButton());
247     return false;
248 }
249
250 static void setWidgetHasFocus(GtkWidget* widget, gboolean hasFocus)
251 {
252     g_object_set(widget, "has-focus", hasFocus, NULL);
253
254     // These functions are deprecated in GTK+ 2.22, yet theme engines still look
255     // at these flags when determining if a widget has focus, so we must use them.
256     if (hasFocus)
257         GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
258     else
259         GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
260 }
261
262 bool RenderThemeGtk::paintButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
263 {
264     if (info.context->paintingDisabled())
265         return false;
266
267     GtkWidget* widget = gtkButton();
268     IntRect buttonRect(IntPoint(), rect.size());
269     IntRect focusRect(buttonRect);
270
271     GtkStateType state = getGtkStateType(this, object);
272     gtk_widget_set_state(widget, state);
273     gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
274
275     if (isFocused(object)) {
276         setWidgetHasFocus(widget, TRUE);
277
278         gboolean interiorFocus = 0, focusWidth = 0, focusPadding = 0;
279         gtk_widget_style_get(widget,
280                              "interior-focus", &interiorFocus,
281                              "focus-line-width", &focusWidth,
282                              "focus-padding", &focusPadding, NULL);
283         // If we are using exterior focus, we shrink the button rect down before
284         // drawing. If we are using interior focus we shrink the focus rect. This
285         // approach originates from the Mozilla theme drawing code (gtk2drawing.c).
286         if (interiorFocus) {
287             GtkStyle* style = gtk_widget_get_style(widget);
288             focusRect.inflateX(-style->xthickness - focusPadding);
289             focusRect.inflateY(-style->ythickness - focusPadding);
290         } else {
291             buttonRect.inflateX(-focusWidth - focusPadding);
292             buttonRect.inflateY(-focusPadding - focusPadding);
293         }
294     }
295
296     WidgetRenderingContext widgetContext(info.context, rect);
297     GtkShadowType shadowType = state == GTK_STATE_ACTIVE ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
298     widgetContext.gtkPaintBox(buttonRect, widget, state, shadowType, "button");
299     if (isFocused(object))
300         widgetContext.gtkPaintFocus(focusRect, widget, state, "button");
301
302     setWidgetHasFocus(widget, FALSE);
303     return false;
304 }
305
306 static void getComboBoxPadding(RenderStyle* style, int& left, int& top, int& right, int& bottom)
307 {
308     // If this menu list button isn't drawn using the native theme, we
309     // don't add any extra padding beyond what WebCore already uses.
310     if (style->appearance() == NoControlPart)
311         return;
312     moz_gtk_get_widget_border(MOZ_GTK_DROPDOWN, &left, &top, &right, &bottom,
313                               gtkTextDirection(style->direction()), TRUE);
314 }
315
316 int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const
317 {
318     int left = 0, top = 0, right = 0, bottom = 0;
319     getComboBoxPadding(style, left, top, right, bottom);
320     return left;
321 }
322
323 int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const
324 {
325     int left = 0, top = 0, right = 0, bottom = 0;
326     getComboBoxPadding(style, left, top, right, bottom);
327     return right;
328 }
329
330 int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const
331 {
332     int left = 0, top = 0, right = 0, bottom = 0;
333     getComboBoxPadding(style, left, top, right, bottom);
334     return top;
335 }
336
337 int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const
338 {
339     int left = 0, top = 0, right = 0, bottom = 0;
340     getComboBoxPadding(style, left, top, right, bottom);
341     return bottom;
342 }
343
344 bool RenderThemeGtk::paintMenuList(RenderObject* object, const PaintInfo& info, const IntRect& rect)
345 {
346     return paintRenderObject(MOZ_GTK_DROPDOWN, object, info.context, rect);
347 }
348
349 bool RenderThemeGtk::paintTextField(RenderObject* renderObject, const PaintInfo& info, const IntRect& rect)
350 {
351     GtkWidget* widget = gtkEntry();
352
353     bool enabled = isEnabled(renderObject) && !isReadOnlyControl(renderObject);
354     GtkStateType backgroundState = enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE;
355     gtk_widget_set_sensitive(widget, enabled);
356     gtk_widget_set_direction(widget, gtkTextDirection(renderObject->style()->direction()));
357     setWidgetHasFocus(widget, isFocused(renderObject));
358
359     WidgetRenderingContext widgetContext(info.context, rect);
360     IntRect textFieldRect(IntPoint(), rect.size());
361
362     // The entry background is only painted over the interior part of the GTK+ entry, not
363     // the entire frame. This happens in the Mozilla theme drawing code as well.
364     IntRect interiorRect(textFieldRect);
365     GtkStyle* style = gtk_widget_get_style(widget);
366     interiorRect.inflateX(-style->xthickness);
367     interiorRect.inflateY(-style->ythickness);
368     widgetContext.gtkPaintFlatBox(interiorRect, widget, backgroundState, GTK_SHADOW_NONE, "entry_bg");
369
370     // This is responsible for drawing the actual frame.
371     widgetContext.gtkPaintShadow(textFieldRect, widget, GTK_STATE_NORMAL, GTK_SHADOW_IN, "entry");
372
373     gboolean interiorFocus;
374     gint focusWidth;
375     gtk_widget_style_get(widget,
376                          "interior-focus", &interiorFocus,
377                          "focus-line-width", &focusWidth,  NULL);
378     if (isFocused(renderObject) && !interiorFocus) {
379         // When GTK+ paints a text entry with focus, it shrinks the size of the frame area by the
380         // focus width and paints over the previously unfocused text entry. We need to emulate that
381         // by drawing both the unfocused frame above and the focused frame here.
382         IntRect shadowRect(textFieldRect);
383         shadowRect.inflate(-focusWidth);
384         widgetContext.gtkPaintShadow(shadowRect, widget, GTK_STATE_NORMAL, GTK_SHADOW_IN, "entry");
385
386         widgetContext.gtkPaintFocus(textFieldRect, widget, GTK_STATE_NORMAL, "entry");
387     }
388
389     return false;
390 }
391
392 bool RenderThemeGtk::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
393 {
394     if (info.context->paintingDisabled())
395         return false;
396
397     ControlPart part = object->style()->appearance();
398     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart);
399
400     // We shrink the trough rect slightly to make room for the focus indicator.
401     IntRect troughRect(IntPoint(), rect.size()); // This is relative to rect.
402     GtkWidget* widget = 0;
403     if (part == SliderHorizontalPart) {
404         widget = gtkHScale();
405         troughRect.inflateX(-gtk_widget_get_style(widget)->xthickness);
406     } else {
407         widget = gtkVScale();
408         troughRect.inflateY(-gtk_widget_get_style(widget)->ythickness);
409     }
410     gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
411
412     WidgetRenderingContext widgetContext(info.context, rect);
413     widgetContext.gtkPaintBox(troughRect, widget, GTK_STATE_ACTIVE, GTK_SHADOW_OUT, "trough");
414     if (isFocused(object))
415         widgetContext.gtkPaintFocus(IntRect(IntPoint(), rect.size()), widget, getGtkStateType(this, object), "trough");
416
417     return false;
418 }
419
420 bool RenderThemeGtk::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
421 {
422     if (info.context->paintingDisabled())
423         return false;
424
425     ControlPart part = object->style()->appearance();
426     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
427
428     GtkWidget* widget = 0;
429     const char* detail = 0;
430     GtkOrientation orientation;
431     if (part == SliderThumbHorizontalPart) {
432         widget = gtkHScale();
433         detail = "hscale";
434         orientation = GTK_ORIENTATION_HORIZONTAL;
435     } else {
436         widget = gtkVScale();
437         detail = "vscale";
438         orientation = GTK_ORIENTATION_VERTICAL;
439     }
440     gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
441
442     // Only some themes have slider thumbs respond to clicks and some don't. This information is
443     // gathered via the 'activate-slider' property, but it's deprecated in GTK+ 2.22 and removed in
444     // GTK+ 3.x. The drawback of not honoring it is that slider thumbs change color when you click
445     // on them. 
446     IntRect thumbRect(IntPoint(), rect.size());
447     WidgetRenderingContext widgetContext(info.context, rect);
448     widgetContext.gtkPaintSlider(thumbRect, widget, getGtkStateType(this, object), GTK_SHADOW_OUT, detail, orientation);
449     return false;
450 }
451
452 void RenderThemeGtk::adjustSliderThumbSize(RenderObject* o) const
453 {
454     ControlPart part = o->style()->appearance();
455 #if ENABLE(VIDEO)
456     if (part == MediaSliderThumbPart) {
457         adjustMediaSliderThumbSize(o);
458         return;
459     }
460 #endif
461
462     GtkWidget* widget = part == SliderThumbHorizontalPart ? gtkHScale() : gtkVScale();
463     int length = 0, width = 0;
464     gtk_widget_style_get(widget,
465                          "slider_length", &length,
466                          "slider_width", &width,
467                          NULL);
468
469     if (part == SliderThumbHorizontalPart) {
470         o->style()->setWidth(Length(length, Fixed));
471         o->style()->setHeight(Length(width, Fixed));
472         return;
473     }
474     ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart);
475     o->style()->setWidth(Length(width, Fixed));
476     o->style()->setHeight(Length(length, Fixed));
477 }
478
479 #if ENABLE(PROGRESS_TAG)
480 double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const
481 {
482     // FIXME: It doesn't look like there is a good way yet to support animated
483     // progress bars with the Mozilla theme drawing code.
484     return 0;
485 }
486
487 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const
488 {
489     // FIXME: It doesn't look like there is a good way yet to support animated
490     // progress bars with the Mozilla theme drawing code.
491     return 0;
492 }
493
494 bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
495 {
496     if (!renderObject->isProgress())
497         return true;
498
499     GtkWidget* progressBarWidget = moz_gtk_get_progress_widget();
500     if (!progressBarWidget)
501         return true;
502
503     if (paintRenderObject(MOZ_GTK_PROGRESSBAR, renderObject, paintInfo.context, rect))
504         return true;
505
506     IntRect chunkRect(rect);
507     RenderProgress* renderProgress = toRenderProgress(renderObject);
508
509     GtkStyle* style = gtk_widget_get_style(progressBarWidget);
510     chunkRect.setHeight(chunkRect.height() - (2 * style->ythickness));
511     chunkRect.setY(chunkRect.y() + style->ythickness);
512     chunkRect.setWidth((chunkRect.width() - (2 * style->xthickness)) * renderProgress->position());
513     if (renderObject->style()->direction() == RTL)
514         chunkRect.setX(rect.x() + rect.width() - chunkRect.width() - style->xthickness);
515     else
516         chunkRect.setX(chunkRect.x() + style->xthickness);
517
518     return paintRenderObject(MOZ_GTK_PROGRESS_CHUNK, renderObject, paintInfo.context, chunkRect);
519 }
520 #endif
521
522 GRefPtr<GdkPixbuf> RenderThemeGtk::getStockIcon(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize)
523 {
524     ASSERT(widgetType == GTK_TYPE_CONTAINER || widgetType == GTK_TYPE_ENTRY);
525     GtkWidget* widget = widgetType == GTK_TYPE_CONTAINER ? GTK_WIDGET(gtkContainer()) : gtkEntry();
526     GtkStyle* style = gtk_widget_get_style(widget);
527     GtkIconSet* iconSet = gtk_style_lookup_icon_set(style, iconName);
528     return adoptGRef(gtk_icon_set_render_icon(iconSet, style,
529                                               static_cast<GtkTextDirection>(direction),
530                                               static_cast<GtkStateType>(state),
531                                               static_cast<GtkIconSize>(iconSize), 0, 0));
532 }
533
534
535 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
536 {
537     GtkWidget* widget = gtkEntry();
538     return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED];
539 }
540
541 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
542 {
543     GtkWidget* widget = gtkEntry();
544     return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE];
545 }
546
547 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
548 {
549     GtkWidget* widget = gtkEntry();
550     return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED];
551 }
552
553 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
554 {
555     GtkWidget* widget = gtkEntry();
556     return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE];
557 }
558
559 Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const
560 {
561     GtkWidget* widget = gtkTreeView();
562     return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED];
563 }
564
565 Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const
566 {
567     GtkWidget* widget = gtkTreeView();
568     return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE];
569 }
570
571 Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const
572 {
573     GtkWidget* widget = gtkTreeView();
574     return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED];
575 }
576
577 Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const
578 {
579     GtkWidget* widget = gtkTreeView();
580     return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE];
581 }
582
583 Color RenderThemeGtk::systemColor(int cssValueId) const
584 {
585     switch (cssValueId) {
586     case CSSValueButtontext:
587         return Color(gtk_widget_get_style(gtkButton())->fg[GTK_STATE_NORMAL]);
588     case CSSValueCaptiontext:
589         return Color(gtk_widget_get_style(gtkEntry())->fg[GTK_STATE_NORMAL]);
590     default:
591         return RenderTheme::systemColor(cssValueId);
592     }
593 }
594
595 static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, RenderTheme* renderTheme)
596 {
597     // FIXME: Make sure this function doesn't get called many times for a single GTK+ style change signal.
598     renderTheme->platformColorsDidChange();
599 }
600
601 void RenderThemeGtk::setupWidgetAndAddToContainer(GtkWidget* widget, GtkWidget* window) const
602 {
603     gtk_container_add(GTK_CONTAINER(window), widget);
604     gtk_widget_realize(widget);
605     g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
606
607     // FIXME: Perhaps this should only be called for the containing window or parent container.
608     g_signal_connect(widget, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
609 }
610
611 GtkWidget* RenderThemeGtk::gtkContainer() const
612 {
613     if (m_gtkContainer)
614         return m_gtkContainer;
615
616     m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP);
617     gtk_widget_set_colormap(m_gtkWindow, m_themeParts.colormap);
618     gtk_widget_realize(m_gtkWindow);
619     gtk_widget_set_name(m_gtkWindow, "MozillaGtkWidget");
620
621     m_gtkContainer = gtk_fixed_new();
622     setupWidgetAndAddToContainer(m_gtkContainer, m_gtkWindow);
623     return m_gtkContainer;
624 }
625
626 GtkWidget* RenderThemeGtk::gtkButton() const
627 {
628     if (m_gtkButton)
629         return m_gtkButton;
630     m_gtkButton = gtk_button_new();
631     setupWidgetAndAddToContainer(m_gtkButton, gtkContainer());
632     return m_gtkButton;
633 }
634
635 GtkWidget* RenderThemeGtk::gtkEntry() const
636 {
637     if (m_gtkEntry)
638         return m_gtkEntry;
639     m_gtkEntry = gtk_entry_new();
640     setupWidgetAndAddToContainer(m_gtkEntry, gtkContainer());
641     return m_gtkEntry;
642 }
643
644 GtkWidget* RenderThemeGtk::gtkTreeView() const
645 {
646     if (m_gtkTreeView)
647         return m_gtkTreeView;
648     m_gtkTreeView = gtk_tree_view_new();
649     setupWidgetAndAddToContainer(m_gtkTreeView, gtkContainer());
650     return m_gtkTreeView;
651 }
652
653 GtkWidget* RenderThemeGtk::gtkVScale() const
654 {
655     if (m_gtkVScale)
656         return m_gtkVScale;
657     m_gtkVScale = gtk_vscale_new(0);
658     setupWidgetAndAddToContainer(m_gtkVScale, gtkContainer());
659     return m_gtkVScale;
660 }
661
662 GtkWidget* RenderThemeGtk::gtkHScale() const
663 {
664     if (m_gtkHScale)
665         return m_gtkHScale;
666     m_gtkHScale = gtk_hscale_new(0);
667     setupWidgetAndAddToContainer(m_gtkHScale, gtkContainer());
668     return m_gtkHScale;
669 }
670
671 GtkWidget* RenderThemeGtk::gtkRadioButton() const
672 {
673     if (m_gtkRadioButton)
674         return m_gtkRadioButton;
675     m_gtkRadioButton = gtk_radio_button_new(0);
676     setupWidgetAndAddToContainer(m_gtkRadioButton, gtkContainer());
677     return m_gtkRadioButton;
678 }
679
680 GtkWidget* RenderThemeGtk::gtkCheckButton() const
681 {
682     if (m_gtkCheckButton)
683         return m_gtkCheckButton;
684     m_gtkCheckButton = gtk_check_button_new();
685     setupWidgetAndAddToContainer(m_gtkCheckButton, gtkContainer());
686     return m_gtkCheckButton;
687 }
688
689 GtkWidget* RenderThemeGtk::gtkScrollbar()
690 {
691     return moz_gtk_get_scrollbar_widget();
692 }
693
694 } // namespace WebCore
695
696 #endif // GTK_API_VERSION_2