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 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB.  If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24#include "config.h"
25#include "RenderThemeGtk.h"
26
27#include "AffineTransform.h"
28#include "CString.h"
29#include "GOwnPtr.h"
30#include "GraphicsContext.h"
31#include "HTMLMediaElement.h"
32#include "HTMLNames.h"
33#include "NotImplemented.h"
34#include "RenderBox.h"
35#include "RenderObject.h"
36#include "UserAgentStyleSheets.h"
37#include "gtkdrawing.h"
38
39#include <gdk/gdk.h>
40#include <gtk/gtk.h>
41
42namespace WebCore {
43
44using namespace HTMLNames;
45
46#if ENABLE(VIDEO)
47static HTMLMediaElement* getMediaElementFromRenderObject(RenderObject* o)
48{
49    Node* node = o->node();
50    Node* mediaNode = node ? node->shadowAncestorNode() : 0;
51    if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
52        return 0;
53
54    return static_cast<HTMLMediaElement*>(mediaNode);
55}
56
57static gchar* getIconNameForTextDirection(const char* baseName)
58{
59    GString* nameWithDirection = g_string_new(baseName);
60    GtkTextDirection textDirection = gtk_widget_get_default_direction();
61
62    if (textDirection == GTK_TEXT_DIR_RTL)
63        g_string_append(nameWithDirection, "-rtl");
64    else if (textDirection == GTK_TEXT_DIR_LTR)
65        g_string_append(nameWithDirection, "-ltr");
66
67    return g_string_free(nameWithDirection, FALSE);
68}
69
70void RenderThemeGtk::initMediaStyling(GtkStyle* style, bool force)
71{
72    static bool stylingInitialized = false;
73
74    if (!stylingInitialized || force) {
75        m_panelColor = style->bg[GTK_STATE_NORMAL];
76        m_sliderColor = style->bg[GTK_STATE_ACTIVE];
77        m_sliderThumbColor = style->bg[GTK_STATE_SELECTED];
78
79        // Names of these icons can vary because of text direction.
80        gchar* playButtonIconName = getIconNameForTextDirection("gtk-media-play");
81        gchar* seekBackButtonIconName = getIconNameForTextDirection("gtk-media-rewind");
82        gchar* seekForwardButtonIconName = getIconNameForTextDirection("gtk-media-forward");
83
84        m_fullscreenButton.clear();
85        m_muteButton.clear();
86        m_unmuteButton.clear();
87        m_playButton.clear();
88        m_pauseButton.clear();
89        m_seekBackButton.clear();
90        m_seekForwardButton.clear();
91
92        m_fullscreenButton = Image::loadPlatformThemeIcon("gtk-fullscreen", m_mediaIconSize);
93        m_muteButton = Image::loadPlatformThemeIcon("audio-volume-muted", m_mediaIconSize);
94        m_unmuteButton = Image::loadPlatformThemeIcon("audio-volume-high", m_mediaIconSize);
95        m_playButton = Image::loadPlatformThemeIcon(reinterpret_cast<const char*>(playButtonIconName), m_mediaIconSize);
96        m_pauseButton = Image::loadPlatformThemeIcon("gtk-media-pause", m_mediaIconSize).releaseRef();
97        m_seekBackButton = Image::loadPlatformThemeIcon(reinterpret_cast<const char*>(seekBackButtonIconName), m_mediaIconSize);
98        m_seekForwardButton = Image::loadPlatformThemeIcon(reinterpret_cast<const char*>(seekForwardButtonIconName), m_mediaIconSize);
99
100        g_free(playButtonIconName);
101        g_free(seekBackButtonIconName);
102        g_free(seekForwardButtonIconName);
103        stylingInitialized = true;
104    }
105}
106#endif
107
108PassRefPtr<RenderTheme> RenderThemeGtk::create()
109{
110    return adoptRef(new RenderThemeGtk());
111}
112
113PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
114{
115    static RenderTheme* rt = RenderThemeGtk::create().releaseRef();
116    return rt;
117}
118
119static int mozGtkRefCount = 0;
120
121RenderThemeGtk::RenderThemeGtk()
122    : m_gtkWindow(0)
123    , m_gtkContainer(0)
124    , m_gtkEntry(0)
125    , m_gtkTreeView(0)
126    , m_panelColor(Color::white)
127    , m_sliderColor(Color::white)
128    , m_sliderThumbColor(Color::white)
129    , m_mediaIconSize(16)
130    , m_mediaSliderHeight(14)
131    , m_mediaSliderThumbWidth(12)
132    , m_mediaSliderThumbHeight(12)
133    , m_fullscreenButton(0)
134    , m_muteButton(0)
135    , m_unmuteButton(0)
136    , m_playButton(0)
137    , m_pauseButton(0)
138    , m_seekBackButton(0)
139    , m_seekForwardButton(0)
140    , m_partsTable(adoptGRef(g_hash_table_new_full(0, 0, 0, g_free)))
141{
142    if (!mozGtkRefCount) {
143        moz_gtk_init();
144
145        // Use the theme parts for the default drawable.
146        moz_gtk_use_theme_parts(partsForDrawable(0));
147    }
148
149    ++mozGtkRefCount;
150
151#if ENABLE(VIDEO)
152    initMediaStyling(gtk_rc_get_style(GTK_WIDGET(gtkContainer())), false);
153#endif
154}
155
156RenderThemeGtk::~RenderThemeGtk()
157{
158    --mozGtkRefCount;
159
160    if (!mozGtkRefCount)
161        moz_gtk_shutdown();
162
163    m_fullscreenButton.clear();
164    m_muteButton.clear();
165    m_unmuteButton.clear();
166    m_playButton.clear();
167    m_pauseButton.clear();
168    m_seekBackButton.clear();
169    m_seekForwardButton.clear();
170
171    GList* values = g_hash_table_get_values(m_partsTable.get());
172    for (guint i = 0; i < g_list_length(values); i++)
173        moz_gtk_destroy_theme_parts_widgets(
174            static_cast<GtkThemeParts*>(g_list_nth_data(values, i)));
175}
176
177GtkThemeParts* RenderThemeGtk::partsForDrawable(GdkDrawable* drawable) const
178{
179    // A null drawable represents the default screen colormap.
180    GdkColormap* colormap = 0;
181    if (!drawable)
182        colormap = gdk_screen_get_default_colormap(gdk_screen_get_default());
183    else
184        colormap = gdk_drawable_get_colormap(drawable);
185
186    GtkThemeParts* parts = static_cast<GtkThemeParts*>(g_hash_table_lookup(m_partsTable.get(), colormap));
187    if (!parts) {
188        parts = g_new0(GtkThemeParts, 1);
189        parts->colormap = colormap;
190        g_hash_table_insert(m_partsTable.get(), colormap, parts);
191    }
192
193    return parts;
194}
195
196static bool supportsFocus(ControlPart appearance)
197{
198    switch (appearance) {
199    case PushButtonPart:
200    case ButtonPart:
201    case TextFieldPart:
202    case TextAreaPart:
203    case SearchFieldPart:
204    case MenulistPart:
205    case RadioPart:
206    case CheckboxPart:
207        return true;
208    default:
209        return false;
210    }
211}
212
213bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const
214{
215    return supportsFocus(style->appearance());
216}
217
218bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const
219{
220    return isEnabled(o);
221}
222
223int RenderThemeGtk::baselinePosition(const RenderObject* o) const
224{
225    if (!o->isBox())
226        return 0;
227
228    // FIXME: This strategy is possibly incorrect for the GTK+ port.
229    if (o->style()->appearance() == CheckboxPart
230        || o->style()->appearance() == RadioPart) {
231        const RenderBox* box = toRenderBox(o);
232        return box->marginTop() + box->height() - 2;
233    }
234
235    return RenderTheme::baselinePosition(o);
236}
237
238static GtkTextDirection gtkTextDirection(TextDirection direction)
239{
240    switch (direction) {
241    case RTL:
242        return GTK_TEXT_DIR_RTL;
243    case LTR:
244        return GTK_TEXT_DIR_LTR;
245    default:
246        return GTK_TEXT_DIR_NONE;
247    }
248}
249
250static void adjustMozillaStyle(const RenderThemeGtk* theme, RenderStyle* style, GtkThemeWidgetType type)
251{
252    gint left, top, right, bottom;
253    GtkTextDirection direction = gtkTextDirection(style->direction());
254    gboolean inhtml = true;
255
256    if (moz_gtk_get_widget_border(type, &left, &top, &right, &bottom, direction, inhtml) != MOZ_GTK_SUCCESS)
257        return;
258
259    // FIXME: This approach is likely to be incorrect. See other ports and layout tests to see the problem.
260    const int xpadding = 1;
261    const int ypadding = 1;
262
263    style->setPaddingLeft(Length(xpadding + left, Fixed));
264    style->setPaddingTop(Length(ypadding + top, Fixed));
265    style->setPaddingRight(Length(xpadding + right, Fixed));
266    style->setPaddingBottom(Length(ypadding + bottom, Fixed));
267}
268
269static void setMozillaState(const RenderTheme* theme, GtkWidgetState* state, RenderObject* o)
270{
271    state->active = theme->isPressed(o);
272    state->focused = theme->isFocused(o);
273    state->inHover = theme->isHovered(o);
274    // FIXME: Disabled does not always give the correct appearance for ReadOnly
275    state->disabled = !theme->isEnabled(o) || theme->isReadOnlyControl(o);
276    state->isDefault = false;
277    state->canDefault = false;
278    state->depressed = false;
279}
280
281static bool paintMozillaGtkWidget(const RenderThemeGtk* theme, GtkThemeWidgetType type, RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
282{
283    // No GdkWindow to render to, so return true to fall back
284    if (!i.context->gdkDrawable())
285        return true;
286
287    // Painting is disabled so just claim to have succeeded
288    if (i.context->paintingDisabled())
289        return false;
290
291    GtkWidgetState mozState;
292    setMozillaState(theme, &mozState, o);
293
294    int flags;
295
296    // We might want to make setting flags the caller's job at some point rather than doing it here.
297    switch (type) {
298    case MOZ_GTK_BUTTON:
299        flags = GTK_RELIEF_NORMAL;
300        break;
301    case MOZ_GTK_CHECKBUTTON:
302    case MOZ_GTK_RADIOBUTTON:
303        flags = theme->isChecked(o);
304        break;
305    default:
306        flags = 0;
307        break;
308    }
309
310    AffineTransform ctm = i.context->getCTM();
311
312    IntPoint pos = ctm.mapPoint(rect.location());
313    GdkRectangle gdkRect = IntRect(pos.x(), pos.y(), rect.width(), rect.height());
314    GtkTextDirection direction = gtkTextDirection(o->style()->direction());
315
316    // Find the clip rectangle
317    cairo_t* cr = i.context->platformContext();
318    double clipX1, clipX2, clipY1, clipY2;
319    cairo_clip_extents(cr, &clipX1, &clipY1, &clipX2, &clipY2);
320
321    GdkRectangle gdkClipRect;
322    gdkClipRect.width = clipX2 - clipX1;
323    gdkClipRect.height = clipY2 - clipY1;
324    IntPoint clipPos = ctm.mapPoint(IntPoint(clipX1, clipY1));
325    gdkClipRect.x = clipPos.x();
326    gdkClipRect.y = clipPos.y();
327
328    gdk_rectangle_intersect(&gdkRect, &gdkClipRect, &gdkClipRect);
329
330    // Since the theme renderer is going to be drawing onto this GdkDrawable,
331    // select the appropriate widgets for the drawable depth.
332    moz_gtk_use_theme_parts(theme->partsForDrawable(i.context->gdkDrawable()));
333    return moz_gtk_widget_paint(type, i.context->gdkDrawable(), &gdkRect, &gdkClipRect, &mozState, flags, direction) != MOZ_GTK_SUCCESS;
334}
335
336static void setButtonPadding(RenderStyle* style)
337{
338    // FIXME: This looks incorrect.
339    const int padding = 8;
340    style->setPaddingLeft(Length(padding, Fixed));
341    style->setPaddingRight(Length(padding, Fixed));
342    style->setPaddingTop(Length(padding / 2, Fixed));
343    style->setPaddingBottom(Length(padding / 2, Fixed));
344}
345
346static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, ControlPart appearance)
347{
348    // The width and height are both specified, so we shouldn't change them.
349    if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
350        return;
351
352    // FIXME: This is probably not correct use of indicatorSize and indicatorSpacing.
353    gint indicatorSize, indicatorSpacing;
354
355    switch (appearance) {
356    case CheckboxPart:
357        if (moz_gtk_checkbox_get_metrics(&indicatorSize, &indicatorSpacing) != MOZ_GTK_SUCCESS)
358            return;
359        break;
360    case RadioPart:
361        if (moz_gtk_radio_get_metrics(&indicatorSize, &indicatorSpacing) != MOZ_GTK_SUCCESS)
362            return;
363        break;
364    default:
365        return;
366    }
367
368    // Other ports hard-code this to 13, but GTK+ users tend to demand the native look.
369    // It could be made a configuration option values other than 13 actually break site compatibility.
370    int length = indicatorSize + indicatorSpacing;
371    if (style->width().isIntrinsicOrAuto())
372        style->setWidth(Length(length, Fixed));
373
374    if (style->height().isAuto())
375        style->setHeight(Length(length, Fixed));
376}
377
378void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
379{
380    setToggleSize(this, style, RadioPart);
381}
382
383bool RenderThemeGtk::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
384{
385    return paintMozillaGtkWidget(this, MOZ_GTK_CHECKBUTTON, o, i, rect);
386}
387
388void RenderThemeGtk::setRadioSize(RenderStyle* style) const
389{
390    setToggleSize(this, style, RadioPart);
391}
392
393bool RenderThemeGtk::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
394{
395    return paintMozillaGtkWidget(this, MOZ_GTK_RADIOBUTTON, o, i, rect);
396}
397
398void RenderThemeGtk::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const
399{
400    // FIXME: Is this condition necessary?
401    if (style->appearance() == PushButtonPart) {
402        style->resetBorder();
403        style->setHeight(Length(Auto));
404        style->setWhiteSpace(PRE);
405        setButtonPadding(style);
406    } else {
407        // FIXME: This should not be hard-coded.
408        style->setMinHeight(Length(14, Fixed));
409        style->resetBorderTop();
410        style->resetBorderBottom();
411    }
412}
413
414bool RenderThemeGtk::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
415{
416    return paintMozillaGtkWidget(this, MOZ_GTK_BUTTON, o, i, rect);
417}
418
419void RenderThemeGtk::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const
420{
421    style->resetBorder();
422    style->resetPadding();
423    style->setHeight(Length(Auto));
424    style->setWhiteSpace(PRE);
425    adjustMozillaStyle(this, style, MOZ_GTK_DROPDOWN);
426}
427
428bool RenderThemeGtk::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
429{
430    return paintMozillaGtkWidget(this, MOZ_GTK_DROPDOWN, o, i, rect);
431}
432
433void RenderThemeGtk::adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
434{
435    style->resetBorder();
436    style->resetPadding();
437    style->setHeight(Length(Auto));
438    style->setWhiteSpace(PRE);
439    adjustMozillaStyle(this, style, MOZ_GTK_ENTRY);
440}
441
442bool RenderThemeGtk::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
443{
444    return paintMozillaGtkWidget(this, MOZ_GTK_ENTRY, o, i, rect);
445}
446
447bool RenderThemeGtk::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
448{
449    return paintTextField(o, i, r);
450}
451
452void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
453{
454    adjustSearchFieldCancelButtonStyle(selector, style, e);
455}
456
457bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
458{
459    return paintMozillaGtkWidget(this, MOZ_GTK_DROPDOWN_ARROW, o, i, rect);
460}
461
462void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
463{
464    style->resetBorder();
465    style->resetPadding();
466
467    // FIXME: This should not be hard-coded.
468    IntSize size = IntSize(14, 14);
469    style->setWidth(Length(size.width(), Fixed));
470    style->setHeight(Length(size.height(), Fixed));
471}
472
473bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
474{
475    return paintMozillaGtkWidget(this, MOZ_GTK_CHECKMENUITEM, o, i, rect);
476}
477
478void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
479{
480    style->resetBorder();
481    style->resetPadding();
482
483    // FIXME: This should not be hard-coded.
484    IntSize size = IntSize(14, 14);
485    style->setWidth(Length(size.width(), Fixed));
486    style->setHeight(Length(size.height(), Fixed));
487}
488
489bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
490{
491    return paintMozillaGtkWidget(this, MOZ_GTK_CHECKMENUITEM, o, i, rect);
492}
493
494void RenderThemeGtk::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
495{
496    adjustTextFieldStyle(selector, style, e);
497}
498
499bool RenderThemeGtk::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
500{
501    return paintTextField(o, i, rect);
502}
503
504void RenderThemeGtk::adjustSliderThumbSize(RenderObject* o) const
505{
506#if ENABLE(VIDEO)
507    if (o->style()->appearance() == MediaSliderThumbPart) {
508        o->style()->setWidth(Length(m_mediaSliderThumbWidth, Fixed));
509        o->style()->setHeight(Length(m_mediaSliderThumbHeight, Fixed));
510    }
511#endif
512}
513
514Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
515{
516    GtkWidget* widget = gtkEntry();
517    return widget->style->base[GTK_STATE_SELECTED];
518}
519
520Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
521{
522    GtkWidget* widget = gtkEntry();
523    return widget->style->base[GTK_STATE_ACTIVE];
524}
525
526Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
527{
528    GtkWidget* widget = gtkEntry();
529    return widget->style->text[GTK_STATE_SELECTED];
530}
531
532Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
533{
534    GtkWidget* widget = gtkEntry();
535    return widget->style->text[GTK_STATE_ACTIVE];
536}
537
538Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const
539{
540    GtkWidget* widget = gtkTreeView();
541    return widget->style->base[GTK_STATE_SELECTED];
542}
543
544Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const
545{
546    GtkWidget* widget = gtkTreeView();
547    return widget->style->base[GTK_STATE_ACTIVE];
548}
549
550Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const
551{
552    GtkWidget* widget = gtkTreeView();
553    return widget->style->text[GTK_STATE_SELECTED];
554}
555
556Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const
557{
558    GtkWidget* widget = gtkTreeView();
559    return widget->style->text[GTK_STATE_ACTIVE];
560}
561
562double RenderThemeGtk::caretBlinkInterval() const
563{
564    GtkSettings* settings = gtk_settings_get_default();
565
566    gboolean shouldBlink;
567    gint time;
568
569    g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL);
570
571    if (!shouldBlink)
572        return 0;
573
574    return time / 2000.;
575}
576
577void RenderThemeGtk::systemFont(int, FontDescription&) const
578{
579    // If you remove this notImplemented(), replace it with an comment that explains why.
580    notImplemented();
581}
582
583static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, RenderTheme* renderTheme)
584{
585    // FIXME: Make sure this function doesn't get called many times for a single GTK+ style change signal.
586    renderTheme->platformColorsDidChange();
587}
588
589GtkContainer* RenderThemeGtk::gtkContainer() const
590{
591    if (m_gtkContainer)
592        return m_gtkContainer;
593
594    m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP);
595    m_gtkContainer = GTK_CONTAINER(gtk_fixed_new());
596    g_signal_connect(m_gtkWindow, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
597    gtk_container_add(GTK_CONTAINER(m_gtkWindow), GTK_WIDGET(m_gtkContainer));
598    gtk_widget_realize(m_gtkWindow);
599
600    return m_gtkContainer;
601}
602
603GtkWidget* RenderThemeGtk::gtkEntry() const
604{
605    if (m_gtkEntry)
606        return m_gtkEntry;
607
608    m_gtkEntry = gtk_entry_new();
609    g_signal_connect(m_gtkEntry, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
610    gtk_container_add(gtkContainer(), m_gtkEntry);
611    gtk_widget_realize(m_gtkEntry);
612
613    return m_gtkEntry;
614}
615
616GtkWidget* RenderThemeGtk::gtkTreeView() const
617{
618    if (m_gtkTreeView)
619        return m_gtkTreeView;
620
621    m_gtkTreeView = gtk_tree_view_new();
622    g_signal_connect(m_gtkTreeView, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
623    gtk_container_add(gtkContainer(), m_gtkTreeView);
624    gtk_widget_realize(m_gtkTreeView);
625
626    return m_gtkTreeView;
627}
628
629void RenderThemeGtk::platformColorsDidChange()
630{
631#if ENABLE(VIDEO)
632    initMediaStyling(gtk_rc_get_style(GTK_WIDGET(gtkContainer())), true);
633#endif
634    RenderTheme::platformColorsDidChange();
635}
636
637#if ENABLE(VIDEO)
638String RenderThemeGtk::extraMediaControlsStyleSheet()
639{
640    return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
641}
642
643static inline bool paintMediaButton(GraphicsContext* context, const IntRect& r, Image* image, Color panelColor, int mediaIconSize)
644{
645    context->fillRect(FloatRect(r), panelColor, DeviceColorSpace);
646    context->drawImage(image, DeviceColorSpace,
647                       IntRect(r.x() + (r.width() - mediaIconSize) / 2,
648                               r.y() + (r.height() - mediaIconSize) / 2,
649                               mediaIconSize, mediaIconSize));
650
651    return false;
652}
653
654bool RenderThemeGtk::paintMediaFullscreenButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
655{
656    return paintMediaButton(paintInfo.context, r, m_fullscreenButton.get(), m_panelColor, m_mediaIconSize);
657}
658
659bool RenderThemeGtk::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
660{
661    HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o);
662    if (!mediaElement)
663        return false;
664
665    return paintMediaButton(paintInfo.context, r, mediaElement->muted() ? m_unmuteButton.get() : m_muteButton.get(), m_panelColor, m_mediaIconSize);
666}
667
668bool RenderThemeGtk::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
669{
670    HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o);
671    if (!mediaElement)
672        return false;
673
674    return paintMediaButton(paintInfo.context, r, mediaElement->canPlay() ? m_playButton.get() : m_pauseButton.get(), m_panelColor, m_mediaIconSize);
675}
676
677bool RenderThemeGtk::paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
678{
679    return paintMediaButton(paintInfo.context, r, m_seekBackButton.get(), m_panelColor, m_mediaIconSize);
680}
681
682bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
683{
684    return paintMediaButton(paintInfo.context, r, m_seekForwardButton.get(), m_panelColor, m_mediaIconSize);
685}
686
687bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
688{
689    paintInfo.context->fillRect(FloatRect(r), m_panelColor, DeviceColorSpace);
690    paintInfo.context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2,
691                                                  r.width(), m_mediaSliderHeight)), m_sliderColor, DeviceColorSpace);
692    return false;
693}
694
695bool RenderThemeGtk::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
696{
697    // Make the thumb nicer with rounded corners.
698    paintInfo.context->fillRoundedRect(r, IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), m_sliderThumbColor, DeviceColorSpace);
699    return false;
700}
701#endif
702
703}
704