2010-12-08 Carlos Garcia Campos <cgarcia@igalia.com>
[WebKit-https.git] / WebCore / platform / gtk / RenderThemeGtk.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 #include "AffineTransform.h"
29 #include "CSSValueKeywords.h"
30 #include "GOwnPtr.h"
31 #include "Gradient.h"
32 #include "GraphicsContext.h"
33 #include "GtkVersioning.h"
34 #include "HTMLMediaElement.h"
35 #include "HTMLNames.h"
36 #include "MediaControlElements.h"
37 #include "NotImplemented.h"
38 #include "PlatformMouseEvent.h"
39 #include "RenderBox.h"
40 #include "RenderObject.h"
41 #include "Scrollbar.h"
42 #include "TimeRanges.h"
43 #include "UserAgentStyleSheets.h"
44 #include "WidgetRenderingContext.h"
45 #include "gtkdrawing.h"
46 #include <gdk/gdk.h>
47 #include <gtk/gtk.h>
48 #include <wtf/text/CString.h>
49
50 #if ENABLE(PROGRESS_TAG)
51 #include "RenderProgress.h"
52 #endif
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
58 #if ENABLE(VIDEO)
59 static HTMLMediaElement* getMediaElementFromRenderObject(RenderObject* o)
60 {
61     Node* node = o->node();
62     Node* mediaNode = node ? node->shadowAncestorNode() : 0;
63     if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
64         return 0;
65
66     return static_cast<HTMLMediaElement*>(mediaNode);
67 }
68
69 static void paintStockIcon(GraphicsContext* context, const IntPoint& iconPoint, GtkStyle* style, const char* iconName,
70                            GtkTextDirection direction, GtkStateType state, GtkIconSize iconSize)
71 {
72     GtkIconSet* iconSet = gtk_style_lookup_icon_set(style, iconName);
73     PlatformRefPtr<GdkPixbuf> icon = adoptPlatformRef(gtk_icon_set_render_icon(iconSet, style, direction, state, iconSize, 0, 0));
74
75     cairo_t* cr = context->platformContext();
76     cairo_save(cr);
77     gdk_cairo_set_source_pixbuf(cr, icon.get(), iconPoint.x(), iconPoint.y());
78     cairo_paint(cr);
79     cairo_restore(cr);
80 }
81
82 static GtkIconSize getMediaButtonIconSize(int mediaIconSize)
83 {
84     GtkIconSize iconSize = gtk_icon_size_from_name("webkit-media-button-size");
85     if (!iconSize)
86         iconSize = gtk_icon_size_register("webkit-media-button-size", mediaIconSize, mediaIconSize);
87     return iconSize;
88 }
89
90 void RenderThemeGtk::initMediaColors()
91 {
92     GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkContainer()));
93     m_panelColor = style->bg[GTK_STATE_NORMAL];
94     m_sliderColor = style->bg[GTK_STATE_ACTIVE];
95     m_sliderThumbColor = style->bg[GTK_STATE_SELECTED];
96 }
97
98 void RenderThemeGtk::initMediaButtons()
99 {
100     static bool iconsInitialized = false;
101
102     if (iconsInitialized)
103         return;
104
105     PlatformRefPtr<GtkIconFactory> iconFactory = adoptPlatformRef(gtk_icon_factory_new());
106     GtkIconSource* iconSource = gtk_icon_source_new();
107     const char* icons[] = { "audio-volume-high", "audio-volume-muted" };
108
109     gtk_icon_factory_add_default(iconFactory.get());
110
111     for (size_t i = 0; i < G_N_ELEMENTS(icons); ++i) {
112         gtk_icon_source_set_icon_name(iconSource, icons[i]);
113         GtkIconSet* iconSet = gtk_icon_set_new();
114         gtk_icon_set_add_source(iconSet, iconSource);
115         gtk_icon_factory_add(iconFactory.get(), icons[i], iconSet);
116         gtk_icon_set_unref(iconSet);
117     }
118
119     gtk_icon_source_free(iconSource);
120
121     iconsInitialized = true;
122 }
123 #endif
124
125 PassRefPtr<RenderTheme> RenderThemeGtk::create()
126 {
127     return adoptRef(new RenderThemeGtk());
128 }
129
130 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
131 {
132     static RenderTheme* rt = RenderThemeGtk::create().releaseRef();
133     return rt;
134 }
135
136 static int mozGtkRefCount = 0;
137
138 RenderThemeGtk::RenderThemeGtk()
139     : m_gtkWindow(0)
140     , m_gtkContainer(0)
141     , m_gtkButton(0)
142     , m_gtkEntry(0)
143     , m_gtkTreeView(0)
144     , m_panelColor(Color::white)
145     , m_sliderColor(Color::white)
146     , m_sliderThumbColor(Color::white)
147     , m_mediaIconSize(16)
148     , m_mediaSliderHeight(14)
149     , m_mediaSliderThumbWidth(12)
150     , m_mediaSliderThumbHeight(12)
151 #ifdef GTK_API_VERSION_2
152     , m_themePartsHaveRGBAColormap(true)
153 #endif
154 {
155
156     memset(&m_themeParts, 0, sizeof(GtkThemeParts));
157 #ifdef GTK_API_VERSION_2
158     GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default());
159     if (!colormap) {
160         m_themePartsHaveRGBAColormap = false;
161         colormap = gdk_screen_get_default_colormap(gdk_screen_get_default());
162     }
163     m_themeParts.colormap = colormap;
164 #endif
165
166     // Initialize the Mozilla theme drawing code.
167     if (!mozGtkRefCount) {
168         moz_gtk_init();
169         moz_gtk_use_theme_parts(&m_themeParts);
170     }
171     ++mozGtkRefCount;
172
173 #if ENABLE(VIDEO)
174     initMediaColors();
175     initMediaButtons();
176 #endif
177 }
178
179 RenderThemeGtk::~RenderThemeGtk()
180 {
181     --mozGtkRefCount;
182
183     if (!mozGtkRefCount)
184         moz_gtk_shutdown();
185
186     gtk_widget_destroy(m_gtkWindow);
187 }
188
189 void RenderThemeGtk::getIndicatorMetrics(ControlPart part, int& indicatorSize, int& indicatorSpacing) const
190 {
191     ASSERT(part == CheckboxPart || part == RadioPart);
192     if (part == CheckboxPart) {
193         moz_gtk_checkbox_get_metrics(&indicatorSize, &indicatorSpacing);
194         return;
195     }
196
197     // RadioPart
198     moz_gtk_radio_get_metrics(&indicatorSize, &indicatorSpacing);
199 }
200
201 static bool supportsFocus(ControlPart appearance)
202 {
203     switch (appearance) {
204     case PushButtonPart:
205     case ButtonPart:
206     case TextFieldPart:
207     case TextAreaPart:
208     case SearchFieldPart:
209     case MenulistPart:
210     case RadioPart:
211     case CheckboxPart:
212     case SliderHorizontalPart:
213     case SliderVerticalPart:
214         return true;
215     default:
216         return false;
217     }
218 }
219
220 bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const
221 {
222     return supportsFocus(style->appearance());
223 }
224
225 bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const
226 {
227     return isEnabled(o);
228 }
229
230 int RenderThemeGtk::baselinePosition(const RenderObject* o) const
231 {
232     if (!o->isBox())
233         return 0;
234
235     // FIXME: This strategy is possibly incorrect for the GTK+ port.
236     if (o->style()->appearance() == CheckboxPart
237         || o->style()->appearance() == RadioPart) {
238         const RenderBox* box = toRenderBox(o);
239         return box->marginTop() + box->height() - 2;
240     }
241
242     return RenderTheme::baselinePosition(o);
243 }
244
245 static GtkTextDirection gtkTextDirection(TextDirection direction)
246 {
247     switch (direction) {
248     case RTL:
249         return GTK_TEXT_DIR_RTL;
250     case LTR:
251         return GTK_TEXT_DIR_LTR;
252     default:
253         return GTK_TEXT_DIR_NONE;
254     }
255 }
256
257 GtkStateType RenderThemeGtk::gtkIconState(RenderObject* renderObject)
258 {
259     if (!isEnabled(renderObject))
260         return GTK_STATE_INSENSITIVE;
261     if (isPressed(renderObject))
262         return GTK_STATE_ACTIVE;
263     if (isHovered(renderObject))
264         return GTK_STATE_PRELIGHT;
265
266     return GTK_STATE_NORMAL;
267 }
268
269 bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, int flags)
270 {
271     // Painting is disabled so just claim to have succeeded
272     if (context->paintingDisabled())
273         return false;
274
275     GtkWidgetState widgetState;
276     widgetState.active = isPressed(renderObject);
277     widgetState.focused = isFocused(renderObject);
278
279     // https://bugs.webkit.org/show_bug.cgi?id=18364
280     // The Mozilla theme drawing code, only paints a button as pressed when it's pressed 
281     // while hovered. Until we move away from the Mozila code, work-around the issue by
282     // forcing a pressed button into the hovered state. This ensures that buttons activated
283     // via the keyboard have the proper rendering.
284     widgetState.inHover = isHovered(renderObject) || (type == MOZ_GTK_BUTTON && isPressed(renderObject));
285
286     // FIXME: Disabled does not always give the correct appearance for ReadOnly
287     widgetState.disabled = !isEnabled(renderObject) || isReadOnlyControl(renderObject);
288     widgetState.isDefault = false;
289     widgetState.canDefault = false;
290
291     // FIXME: The depressed value should probably apply for other theme parts too.
292     // It must be used for range thumbs, because otherwise when the thumb is pressed,
293     // the rendering is incorrect.
294     if (type == MOZ_GTK_SCALE_THUMB_HORIZONTAL || type == MOZ_GTK_SCALE_THUMB_VERTICAL)
295         widgetState.depressed = isPressed(renderObject);
296     else
297         widgetState.depressed = false;
298
299     WidgetRenderingContext widgetContext(context, rect);
300     return !widgetContext.paintMozillaWidget(type, &widgetState, flags, gtkTextDirection(renderObject->style()->direction()));
301 }
302
303 static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, ControlPart appearance)
304 {
305     // The width and height are both specified, so we shouldn't change them.
306     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
307         return;
308
309     // FIXME: This is probably not correct use of indicatorSize and indicatorSpacing.
310     gint indicatorSize, indicatorSpacing;
311     theme->getIndicatorMetrics(appearance, indicatorSize, indicatorSpacing);
312
313     // Other ports hard-code this to 13, but GTK+ users tend to demand the native look.
314     // It could be made a configuration option values other than 13 actually break site compatibility.
315     int length = indicatorSize + indicatorSpacing;
316     if (style->width().isIntrinsicOrAuto())
317         style->setWidth(Length(length, Fixed));
318
319     if (style->height().isAuto())
320         style->setHeight(Length(length, Fixed));
321 }
322
323 void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
324 {
325     setToggleSize(this, style, RadioPart);
326 }
327
328 bool RenderThemeGtk::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& rect)
329 {
330     return paintRenderObject(MOZ_GTK_CHECKBUTTON, o, i.context, rect, isChecked(o));
331 }
332
333 void RenderThemeGtk::setRadioSize(RenderStyle* style) const
334 {
335     setToggleSize(this, style, RadioPart);
336 }
337
338 bool RenderThemeGtk::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& rect)
339 {
340     return paintRenderObject(MOZ_GTK_RADIOBUTTON, o, i.context, rect, isChecked(o));
341 }
342
343 void RenderThemeGtk::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const
344 {
345     // Some layout tests check explicitly that buttons ignore line-height.
346     if (style->appearance() == PushButtonPart)
347         style->setLineHeight(RenderStyle::initialLineHeight());
348 }
349
350 bool RenderThemeGtk::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
351 {
352     return paintRenderObject(MOZ_GTK_BUTTON, o, i.context, rect, GTK_RELIEF_NORMAL);
353 }
354
355 static void getComboBoxPadding(RenderStyle* style, int& left, int& top, int& right, int& bottom)
356 {
357     // If this menu list button isn't drawn using the native theme, we
358     // don't add any extra padding beyond what WebCore already uses.
359     if (style->appearance() == NoControlPart)
360         return;
361     moz_gtk_get_widget_border(MOZ_GTK_DROPDOWN, &left, &top, &right, &bottom,
362                               gtkTextDirection(style->direction()), TRUE);
363 }
364
365 int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const
366 {
367     int left = 0, top = 0, right = 0, bottom = 0;
368     getComboBoxPadding(style, left, top, right, bottom);
369     return left;
370 }
371
372 int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const
373 {
374     int left = 0, top = 0, right = 0, bottom = 0;
375     getComboBoxPadding(style, left, top, right, bottom);
376     return right;
377 }
378
379 int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const
380 {
381     int left = 0, top = 0, right = 0, bottom = 0;
382     getComboBoxPadding(style, left, top, right, bottom);
383     return top;
384 }
385
386 int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const
387 {
388     int left = 0, top = 0, right = 0, bottom = 0;
389     getComboBoxPadding(style, left, top, right, bottom);
390     return bottom;
391 }
392
393 void RenderThemeGtk::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
394 {
395     // The tests check explicitly that select menu buttons ignore line height.
396     style->setLineHeight(RenderStyle::initialLineHeight());
397
398     // We cannot give a proper rendering when border radius is active, unfortunately.
399     style->resetBorderRadius();
400 }
401
402 void RenderThemeGtk::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
403 {
404     adjustMenuListStyle(selector, style, e);
405 }
406
407 bool RenderThemeGtk::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& rect)
408 {
409     return paintRenderObject(MOZ_GTK_DROPDOWN, o, i.context, rect);
410 }
411
412 bool RenderThemeGtk::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
413 {
414     return paintMenuList(object, info, rect);
415 }
416
417 static void setTextInputBorders(RenderStyle* style)
418 {
419     // If this control isn't drawn using the native theme, we don't touch the borders.
420     if (style->appearance() == NoControlPart)
421         return;
422
423     // We cannot give a proper rendering when border radius is active, unfortunately.
424     style->resetBorderRadius();
425
426     int left = 0, top = 0, right = 0, bottom = 0;
427     moz_gtk_get_widget_border(MOZ_GTK_ENTRY, &left, &top, &right, &bottom,
428                               gtkTextDirection(style->direction()), TRUE);
429     style->setBorderLeftWidth(left);
430     style->setBorderTopWidth(top);
431     style->setBorderRightWidth(right);
432     style->setBorderBottomWidth(bottom);
433 }
434
435 void RenderThemeGtk::adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
436 {
437     setTextInputBorders(style);
438 }
439
440 bool RenderThemeGtk::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
441 {
442     return paintRenderObject(MOZ_GTK_ENTRY, o, i.context, rect);
443 }
444
445 void RenderThemeGtk::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
446 {
447     setTextInputBorders(style);
448 }
449
450 bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
451 {
452     return paintTextField(o, i, r);
453 }
454
455 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
456 {
457     adjustSearchFieldCancelButtonStyle(selector, style, e);
458 }
459
460 bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
461 {
462     return paintSearchFieldResultsDecoration(o, i, rect);
463 }
464
465 void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
466 {
467     style->resetBorder();
468     style->resetPadding();
469
470     gint width = 0, height = 0;
471     gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
472     style->setWidth(Length(width, Fixed));
473     style->setHeight(Length(height, Fixed));
474 }
475
476 static IntPoint centerRectVerticallyInParentInputElement(RenderObject* object, const IntRect& rect)
477 {
478     Node* input = object->node()->shadowAncestorNode(); // Get the renderer of <input> element.
479     if (!input->renderer()->isBox())
480         return rect.topLeft();
481
482     // If possible center the y-coordinate of the rect vertically in the parent input element.
483     // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
484     // that are even, which looks in relation to the box text.
485     IntRect inputContentBox = toRenderBox(input->renderer())->absoluteContentBox();
486
487     return IntPoint(rect.x(), inputContentBox.y() + (inputContentBox.height() - rect.height() + 1) / 2);
488 }
489
490 bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
491 {
492     GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkEntry()));
493     IntPoint iconPoint(centerRectVerticallyInParentInputElement(renderObject, rect));
494     paintStockIcon(paintInfo.context, iconPoint, style, GTK_STOCK_FIND,
495                    gtkTextDirection(renderObject->style()->direction()),
496                    gtkIconState(renderObject), GTK_ICON_SIZE_MENU);
497     return false;
498 }
499
500 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
501 {
502     style->resetBorder();
503     style->resetPadding();
504
505     gint width = 0, height = 0;
506     gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
507     style->setWidth(Length(width, Fixed));
508     style->setHeight(Length(height, Fixed));
509 }
510
511 bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
512 {
513     GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkEntry()));
514     IntPoint iconPoint(centerRectVerticallyInParentInputElement(renderObject, rect));
515     paintStockIcon(paintInfo.context, iconPoint, style, GTK_STOCK_CLEAR,
516                    gtkTextDirection(renderObject->style()->direction()),
517                    gtkIconState(renderObject), GTK_ICON_SIZE_MENU);
518     return false;
519 }
520
521 void RenderThemeGtk::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
522 {
523     style->setLineHeight(RenderStyle::initialLineHeight());
524     setTextInputBorders(style);
525 }
526
527 bool RenderThemeGtk::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
528 {
529     return paintTextField(o, i, rect);
530 }
531
532 bool RenderThemeGtk::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
533 {
534     ControlPart part = object->style()->appearance();
535     ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
536
537     GtkThemeWidgetType gtkPart = MOZ_GTK_SCALE_HORIZONTAL;
538     if (part == SliderVerticalPart)
539         gtkPart = MOZ_GTK_SCALE_VERTICAL;
540
541     return paintRenderObject(gtkPart, object, info.context, rect);
542 }
543
544 void RenderThemeGtk::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
545 {
546     style->setBoxShadow(0);
547 }
548
549 bool RenderThemeGtk::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
550 {
551     ControlPart part = object->style()->appearance();
552     ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
553
554     GtkThemeWidgetType gtkPart = MOZ_GTK_SCALE_THUMB_HORIZONTAL;
555     if (part == SliderThumbVerticalPart)
556         gtkPart = MOZ_GTK_SCALE_THUMB_VERTICAL;
557
558     return paintRenderObject(gtkPart, object, info.context, rect);
559 }
560
561 void RenderThemeGtk::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
562 {
563     style->setBoxShadow(0);
564 }
565
566 void RenderThemeGtk::adjustSliderThumbSize(RenderObject* o) const
567 {
568     ControlPart part = o->style()->appearance();
569 #if ENABLE(VIDEO)
570     if (part == MediaSliderThumbPart) {
571         o->style()->setWidth(Length(m_mediaSliderThumbWidth, Fixed));
572         o->style()->setHeight(Length(m_mediaSliderThumbHeight, Fixed));
573     } else
574 #endif
575     if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
576         gint width, height;
577         moz_gtk_get_scalethumb_metrics(part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, &width, &height);
578         o->style()->setWidth(Length(width, Fixed));
579         o->style()->setHeight(Length(height, Fixed));
580     }
581 }
582
583 Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const
584 {
585     GtkWidget* widget = gtkEntry();
586     return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED];
587 }
588
589 Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const
590 {
591     GtkWidget* widget = gtkEntry();
592     return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE];
593 }
594
595 Color RenderThemeGtk::platformActiveSelectionForegroundColor() const
596 {
597     GtkWidget* widget = gtkEntry();
598     return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED];
599 }
600
601 Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const
602 {
603     GtkWidget* widget = gtkEntry();
604     return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE];
605 }
606
607 Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const
608 {
609     GtkWidget* widget = gtkTreeView();
610     return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED];
611 }
612
613 Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const
614 {
615     GtkWidget* widget = gtkTreeView();
616     return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE];
617 }
618
619 Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const
620 {
621     GtkWidget* widget = gtkTreeView();
622     return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED];
623 }
624
625 Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const
626 {
627     GtkWidget* widget = gtkTreeView();
628     return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE];
629 }
630
631 double RenderThemeGtk::caretBlinkInterval() const
632 {
633     GtkSettings* settings = gtk_settings_get_default();
634
635     gboolean shouldBlink;
636     gint time;
637
638     g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL);
639
640     if (!shouldBlink)
641         return 0;
642
643     return time / 2000.;
644 }
645
646 static double getScreenDPI()
647 {
648     // FIXME: Really this should be the widget's screen.
649     GdkScreen* screen = gdk_screen_get_default();
650     if (!screen)
651         return 96; // Default to 96 DPI.
652
653     float dpi = gdk_screen_get_resolution(screen);
654     if (dpi <= 0)
655         return 96;
656     return dpi;
657 }
658
659 void RenderThemeGtk::systemFont(int, FontDescription& fontDescription) const
660 {
661     GtkSettings* settings = gtk_settings_get_default();
662     if (!settings)
663         return;
664
665     // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
666     GOwnPtr<gchar> fontName;
667     g_object_get(settings, "gtk-font-name", &fontName.outPtr(), NULL);
668
669     PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
670     if (!pangoDescription)
671         return;
672
673     fontDescription.firstFamily().setFamily(pango_font_description_get_family(pangoDescription));
674
675     int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE;
676     // If the size of the font is in points, we need to convert it to pixels.
677     if (!pango_font_description_get_size_is_absolute(pangoDescription))
678         size = size * (getScreenDPI() / 72.0);
679
680     fontDescription.setSpecifiedSize(size);
681     fontDescription.setIsAbsoluteSize(true);
682     fontDescription.setGenericFamily(FontDescription::NoFamily);
683     fontDescription.setWeight(FontWeightNormal);
684     fontDescription.setItalic(false);
685     pango_font_description_free(pangoDescription);
686 }
687
688 Color RenderThemeGtk::systemColor(int cssValueId) const
689 {
690     switch (cssValueId) {
691     case CSSValueButtontext:
692         return Color(gtk_widget_get_style(gtkButton())->fg[GTK_STATE_NORMAL]);
693     case CSSValueCaptiontext:
694         return Color(gtk_widget_get_style(gtkEntry())->fg[GTK_STATE_NORMAL]);
695     default:
696         return RenderTheme::systemColor(cssValueId);
697     }
698 }
699
700 static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, RenderTheme* renderTheme)
701 {
702     // FIXME: Make sure this function doesn't get called many times for a single GTK+ style change signal.
703     renderTheme->platformColorsDidChange();
704 }
705
706 GtkContainer* RenderThemeGtk::gtkContainer() const
707 {
708     if (m_gtkContainer)
709         return m_gtkContainer;
710
711     m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP);
712     m_gtkContainer = GTK_CONTAINER(gtk_fixed_new());
713     g_signal_connect(m_gtkWindow, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
714     gtk_container_add(GTK_CONTAINER(m_gtkWindow), GTK_WIDGET(m_gtkContainer));
715     gtk_widget_realize(m_gtkWindow);
716
717     return m_gtkContainer;
718 }
719
720 GtkWidget* RenderThemeGtk::gtkButton() const
721 {
722     if (m_gtkButton)
723         return m_gtkButton;
724
725     m_gtkButton = gtk_button_new();
726     g_signal_connect(m_gtkButton, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
727     gtk_container_add(gtkContainer(), m_gtkButton);
728     gtk_widget_realize(m_gtkButton);
729
730     return m_gtkButton;
731 }
732
733 GtkWidget* RenderThemeGtk::gtkEntry() const
734 {
735     if (m_gtkEntry)
736         return m_gtkEntry;
737
738     m_gtkEntry = gtk_entry_new();
739     g_signal_connect(m_gtkEntry, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
740     gtk_container_add(gtkContainer(), m_gtkEntry);
741     gtk_widget_realize(m_gtkEntry);
742
743     return m_gtkEntry;
744 }
745
746 GtkWidget* RenderThemeGtk::gtkTreeView() const
747 {
748     if (m_gtkTreeView)
749         return m_gtkTreeView;
750
751     m_gtkTreeView = gtk_tree_view_new();
752     g_signal_connect(m_gtkTreeView, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
753     gtk_container_add(gtkContainer(), m_gtkTreeView);
754     gtk_widget_realize(m_gtkTreeView);
755
756     return m_gtkTreeView;
757 }
758
759 GtkWidget* RenderThemeGtk::gtkScrollbar()
760 {
761     return moz_gtk_get_scrollbar_widget();
762 }
763
764 void RenderThemeGtk::platformColorsDidChange()
765 {
766 #if ENABLE(VIDEO)
767     initMediaColors();
768 #endif
769     RenderTheme::platformColorsDidChange();
770 }
771
772 #if ENABLE(VIDEO)
773 String RenderThemeGtk::extraMediaControlsStyleSheet()
774 {
775     return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
776 }
777
778 bool RenderThemeGtk::paintMediaButton(RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, const char* iconName)
779 {
780     GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkContainer()));
781     IntPoint iconPoint(rect.x() + (rect.width() - m_mediaIconSize) / 2,
782                        rect.y() + (rect.height() - m_mediaIconSize) / 2);
783     context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB);
784     paintStockIcon(context, iconPoint, style, iconName, gtkTextDirection(renderObject->style()->direction()),
785                    gtkIconState(renderObject), getMediaButtonIconSize(m_mediaIconSize));
786
787     return false;
788 }
789
790 bool RenderThemeGtk::paintMediaFullscreenButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
791 {
792     return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_FULLSCREEN);
793 }
794
795 bool RenderThemeGtk::paintMediaMuteButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
796 {
797     HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(renderObject);
798     if (!mediaElement)
799         return false;
800
801     return paintMediaButton(renderObject, paintInfo.context, rect, mediaElement->muted() ? "audio-volume-muted" : "audio-volume-high");
802 }
803
804 bool RenderThemeGtk::paintMediaPlayButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
805 {
806     Node* node = renderObject->node();
807     if (!node)
808         return false;
809
810     MediaControlPlayButtonElement* button = static_cast<MediaControlPlayButtonElement*>(node);
811     return paintMediaButton(renderObject, paintInfo.context, rect, button->displayType() == MediaPlayButton ? GTK_STOCK_MEDIA_PLAY : GTK_STOCK_MEDIA_PAUSE);
812 }
813
814 bool RenderThemeGtk::paintMediaSeekBackButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
815 {
816     return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_REWIND);
817 }
818
819 bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
820 {
821     return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_FORWARD);
822 }
823
824 bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
825 {
826     GraphicsContext* context = paintInfo.context;
827
828     context->fillRect(FloatRect(r), m_panelColor, ColorSpaceDeviceRGB);
829     context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2,
830                                         r.width(), m_mediaSliderHeight)), m_sliderColor, ColorSpaceDeviceRGB);
831
832     RenderStyle* style = o->style();
833     HTMLMediaElement* mediaElement = toParentMediaElement(o);
834
835     if (!mediaElement)
836         return false;
837
838     // Draw the buffered ranges. This code is highly inspired from
839     // Chrome for the gradient code.
840     float mediaDuration = mediaElement->duration();
841     RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
842     IntRect trackRect = r;
843     int totalWidth = trackRect.width();
844
845     trackRect.inflate(-style->borderLeftWidth());
846     context->save();
847     context->setStrokeStyle(NoStroke);
848
849     for (unsigned index = 0; index < timeRanges->length(); ++index) {
850         ExceptionCode ignoredException;
851         float start = timeRanges->start(index, ignoredException);
852         float end = timeRanges->end(index, ignoredException);
853         int width = ((end - start) * totalWidth) / mediaDuration;
854         IntRect rangeRect;
855         if (!index) {
856             rangeRect = trackRect;
857             rangeRect.setWidth(width);
858         } else {
859             rangeRect.setLocation(IntPoint(trackRect.x() + start / mediaDuration* totalWidth, trackRect.y()));
860             rangeRect.setSize(IntSize(width, trackRect.height()));
861         }
862
863         // Don't bother drawing empty range.
864         if (rangeRect.isEmpty())
865             continue;
866
867         IntPoint sliderTopLeft = rangeRect.location();
868         IntPoint sliderTopRight = sliderTopLeft;
869         sliderTopRight.move(0, rangeRect.height());
870
871         RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight);
872         Color startColor = m_panelColor;
873         gradient->addColorStop(0.0, startColor);
874         gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha()));
875
876         context->setFillGradient(gradient);
877         context->fillRect(rangeRect);
878     }
879
880     context->restore();
881     return false;
882 }
883
884 bool RenderThemeGtk::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
885 {
886     // Make the thumb nicer with rounded corners.
887     paintInfo.context->fillRoundedRect(r, IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), m_sliderThumbColor, ColorSpaceDeviceRGB);
888     return false;
889 }
890 #endif
891
892 #if ENABLE(PROGRESS_TAG)
893 double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const
894 {
895     // FIXME: It doesn't look like there is a good way yet to support animated
896     // progress bars with the Mozilla theme drawing code.
897     return 0;
898 }
899
900 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const
901 {
902     // FIXME: It doesn't look like there is a good way yet to support animated
903     // progress bars with the Mozilla theme drawing code.
904     return 0;
905 }
906
907 void RenderThemeGtk::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
908 {
909     style->setBoxShadow(0);
910 }
911
912 bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
913 {
914     if (!renderObject->isProgress())
915         return true;
916
917     GtkWidget* progressBarWidget = moz_gtk_get_progress_widget();
918     if (!progressBarWidget)
919         return true;
920
921     if (paintRenderObject(MOZ_GTK_PROGRESSBAR, renderObject, paintInfo.context, rect))
922         return true;
923
924     IntRect chunkRect(rect);
925     RenderProgress* renderProgress = toRenderProgress(renderObject);
926
927     GtkStyle* style = gtk_widget_get_style(progressBarWidget);
928     chunkRect.setHeight(chunkRect.height() - (2 * style->ythickness));
929     chunkRect.setY(chunkRect.y() + style->ythickness);
930     chunkRect.setWidth((chunkRect.width() - (2 * style->xthickness)) * renderProgress->position());
931     if (renderObject->style()->direction() == RTL)
932         chunkRect.setX(rect.x() + rect.width() - chunkRect.width() - style->xthickness);
933     else
934         chunkRect.setX(chunkRect.x() + style->xthickness);
935
936     return paintRenderObject(MOZ_GTK_PROGRESS_CHUNK, renderObject, paintInfo.context, chunkRect);
937 }
938 #endif
939
940 }