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 "CSSValueKeywords.h"
29 #include "GOwnPtr.h"
30 #include "Gradient.h"
31 #include "GraphicsContext.h"
32 #include "GtkVersioning.h"
33 #include "HTMLMediaElement.h"
34 #include "HTMLNames.h"
35 #include "MediaControlElements.h"
36 #include "PaintInfo.h"
37 #include "PlatformContextCairo.h"
38 #include "RenderBox.h"
39 #include "RenderObject.h"
40 #include "TimeRanges.h"
41 #include "UserAgentStyleSheets.h"
42 #include <gdk/gdk.h>
43 #include <gtk/gtk.h>
44
45 #if ENABLE(PROGRESS_TAG)
46 #include "RenderProgress.h"
47 #endif
48
49 namespace WebCore {
50
51 using namespace HTMLNames;
52
53 #if ENABLE(VIDEO)
getMediaElementFromRenderObject(RenderObject * o)54 static HTMLMediaElement* getMediaElementFromRenderObject(RenderObject* o)
55 {
56 Node* node = o->node();
57 Node* mediaNode = node ? node->shadowAncestorNode() : 0;
58 if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
59 return 0;
60
61 return static_cast<HTMLMediaElement*>(mediaNode);
62 }
63
getMediaButtonIconSize(int mediaIconSize)64 static GtkIconSize getMediaButtonIconSize(int mediaIconSize)
65 {
66 GtkIconSize iconSize = gtk_icon_size_from_name("webkit-media-button-size");
67 if (!iconSize)
68 iconSize = gtk_icon_size_register("webkit-media-button-size", mediaIconSize, mediaIconSize);
69 return iconSize;
70 }
71
initMediaButtons()72 void RenderThemeGtk::initMediaButtons()
73 {
74 static bool iconsInitialized = false;
75
76 if (iconsInitialized)
77 return;
78
79 GRefPtr<GtkIconFactory> iconFactory = adoptGRef(gtk_icon_factory_new());
80 GtkIconSource* iconSource = gtk_icon_source_new();
81 const char* icons[] = { "audio-volume-high", "audio-volume-muted" };
82
83 gtk_icon_factory_add_default(iconFactory.get());
84
85 for (size_t i = 0; i < G_N_ELEMENTS(icons); ++i) {
86 gtk_icon_source_set_icon_name(iconSource, icons[i]);
87 GtkIconSet* iconSet = gtk_icon_set_new();
88 gtk_icon_set_add_source(iconSet, iconSource);
89 gtk_icon_factory_add(iconFactory.get(), icons[i], iconSet);
90 gtk_icon_set_unref(iconSet);
91 }
92
93 gtk_icon_source_free(iconSource);
94
95 iconsInitialized = true;
96 }
97 #endif
98
create()99 PassRefPtr<RenderTheme> RenderThemeGtk::create()
100 {
101 return adoptRef(new RenderThemeGtk());
102 }
103
themeForPage(Page * page)104 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
105 {
106 static RenderTheme* rt = RenderThemeGtk::create().releaseRef();
107 return rt;
108 }
109
RenderThemeGtk()110 RenderThemeGtk::RenderThemeGtk()
111 : m_panelColor(Color::white)
112 , m_sliderColor(Color::white)
113 , m_sliderThumbColor(Color::white)
114 , m_mediaIconSize(16)
115 , m_mediaSliderHeight(14)
116 , m_mediaSliderThumbWidth(12)
117 , m_mediaSliderThumbHeight(12)
118 {
119 platformInit();
120 #if ENABLE(VIDEO)
121 initMediaColors();
122 initMediaButtons();
123 #endif
124 }
125
supportsFocus(ControlPart appearance)126 static bool supportsFocus(ControlPart appearance)
127 {
128 switch (appearance) {
129 case PushButtonPart:
130 case ButtonPart:
131 case TextFieldPart:
132 case TextAreaPart:
133 case SearchFieldPart:
134 case MenulistPart:
135 case RadioPart:
136 case CheckboxPart:
137 case SliderHorizontalPart:
138 case SliderVerticalPart:
139 return true;
140 default:
141 return false;
142 }
143 }
144
supportsFocusRing(const RenderStyle * style) const145 bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const
146 {
147 return supportsFocus(style->appearance());
148 }
149
controlSupportsTints(const RenderObject * o) const150 bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const
151 {
152 return isEnabled(o);
153 }
154
baselinePosition(const RenderObject * o) const155 int RenderThemeGtk::baselinePosition(const RenderObject* o) const
156 {
157 if (!o->isBox())
158 return 0;
159
160 // FIXME: This strategy is possibly incorrect for the GTK+ port.
161 if (o->style()->appearance() == CheckboxPart
162 || o->style()->appearance() == RadioPart) {
163 const RenderBox* box = toRenderBox(o);
164 return box->marginTop() + box->height() - 2;
165 }
166
167 return RenderTheme::baselinePosition(o);
168 }
169
170 // This is used in RenderThemeGtk2 and RenderThemeGtk3. Normally, it would be in
171 // the RenderThemeGtk header (perhaps as a static method), but we want to avoid
172 // having to include GTK+ headers only for the GtkTextDirection enum.
gtkTextDirection(TextDirection direction)173 GtkTextDirection gtkTextDirection(TextDirection direction)
174 {
175 switch (direction) {
176 case RTL:
177 return GTK_TEXT_DIR_RTL;
178 case LTR:
179 return GTK_TEXT_DIR_LTR;
180 default:
181 return GTK_TEXT_DIR_NONE;
182 }
183 }
184
gtkIconState(RenderTheme * theme,RenderObject * renderObject)185 static GtkStateType gtkIconState(RenderTheme* theme, RenderObject* renderObject)
186 {
187 if (!theme->isEnabled(renderObject))
188 return GTK_STATE_INSENSITIVE;
189 if (theme->isPressed(renderObject))
190 return GTK_STATE_ACTIVE;
191 if (theme->isHovered(renderObject))
192 return GTK_STATE_PRELIGHT;
193
194 return GTK_STATE_NORMAL;
195 }
196
adjustButtonStyle(CSSStyleSelector * selector,RenderStyle * style,WebCore::Element * e) const197 void RenderThemeGtk::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const
198 {
199 // Some layout tests check explicitly that buttons ignore line-height.
200 if (style->appearance() == PushButtonPart)
201 style->setLineHeight(RenderStyle::initialLineHeight());
202 }
203
adjustMenuListStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const204 void RenderThemeGtk::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
205 {
206 // The tests check explicitly that select menu buttons ignore line height.
207 style->setLineHeight(RenderStyle::initialLineHeight());
208
209 // We cannot give a proper rendering when border radius is active, unfortunately.
210 style->resetBorderRadius();
211 }
212
adjustMenuListButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const213 void RenderThemeGtk::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
214 {
215 adjustMenuListStyle(selector, style, e);
216 }
217
paintMenuListButton(RenderObject * object,const PaintInfo & info,const IntRect & rect)218 bool RenderThemeGtk::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
219 {
220 return paintMenuList(object, info, rect);
221 }
222
paintTextArea(RenderObject * o,const PaintInfo & i,const IntRect & r)223 bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r)
224 {
225 return paintTextField(o, i, r);
226 }
227
paintGdkPixbuf(GraphicsContext * context,const GdkPixbuf * icon,const IntRect & iconRect)228 static void paintGdkPixbuf(GraphicsContext* context, const GdkPixbuf* icon, const IntRect& iconRect)
229 {
230 IntSize iconSize(gdk_pixbuf_get_width(icon), gdk_pixbuf_get_height(icon));
231 if (iconRect.size() != iconSize) {
232 // We could use cairo_scale() here but cairo/pixman downscale quality is quite bad.
233 GRefPtr<GdkPixbuf> scaledIcon = gdk_pixbuf_scale_simple(icon, iconRect.width(), iconRect.height(),
234 GDK_INTERP_BILINEAR);
235 icon = scaledIcon.get();
236 }
237
238 cairo_t* cr = context->platformContext()->cr();
239 cairo_save(cr);
240 gdk_cairo_set_source_pixbuf(cr, icon, iconRect.x(), iconRect.y());
241 cairo_paint(cr);
242 cairo_restore(cr);
243 }
244
245 // Defined in GTK+ (gtk/gtkiconfactory.c)
246 static const gint gtkIconSizeMenu = 16;
247 static const gint gtkIconSizeSmallToolbar = 18;
248 static const gint gtkIconSizeButton = 20;
249 static const gint gtkIconSizeLargeToolbar = 24;
250 static const gint gtkIconSizeDnd = 32;
251 static const gint gtkIconSizeDialog = 48;
252
getIconSizeForPixelSize(gint pixelSize)253 static GtkIconSize getIconSizeForPixelSize(gint pixelSize)
254 {
255 if (pixelSize < gtkIconSizeSmallToolbar)
256 return GTK_ICON_SIZE_MENU;
257 if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton)
258 return GTK_ICON_SIZE_SMALL_TOOLBAR;
259 if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar)
260 return GTK_ICON_SIZE_BUTTON;
261 if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd)
262 return GTK_ICON_SIZE_LARGE_TOOLBAR;
263 if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog)
264 return GTK_ICON_SIZE_DND;
265
266 return GTK_ICON_SIZE_DIALOG;
267 }
268
adjustSearchFieldResultsButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const269 void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
270 {
271 adjustSearchFieldCancelButtonStyle(selector, style, e);
272 }
273
paintSearchFieldResultsButton(RenderObject * o,const PaintInfo & i,const IntRect & rect)274 bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& i, const IntRect& rect)
275 {
276 return paintSearchFieldResultsDecoration(o, i, rect);
277 }
278
adjustSearchFieldIconStyle(RenderStyle * style)279 static void adjustSearchFieldIconStyle(RenderStyle* style)
280 {
281 style->resetBorder();
282 style->resetPadding();
283
284 // Get the icon size based on the font size.
285 int fontSize = style->fontSize();
286 if (fontSize < gtkIconSizeMenu) {
287 style->setWidth(Length(fontSize, Fixed));
288 style->setHeight(Length(fontSize, Fixed));
289 return;
290 }
291 gint width = 0, height = 0;
292 gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height);
293 style->setWidth(Length(width, Fixed));
294 style->setHeight(Length(height, Fixed));
295 }
296
adjustSearchFieldResultsDecorationStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const297 void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
298 {
299 adjustSearchFieldIconStyle(style);
300 }
301
centerRectVerticallyInParentInputElement(RenderObject * renderObject,const IntRect & rect)302 static IntRect centerRectVerticallyInParentInputElement(RenderObject* renderObject, const IntRect& rect)
303 {
304 // Get the renderer of <input> element.
305 Node* input = renderObject->node()->shadowAncestorNode();
306 if (!input->renderer()->isBox())
307 return IntRect();
308
309 // If possible center the y-coordinate of the rect vertically in the parent input element.
310 // We also add one pixel here to ensure that the y coordinate is rounded up for box heights
311 // that are even, which looks in relation to the box text.
312 IntRect inputContentBox = toRenderBox(input->renderer())->absoluteContentBox();
313
314 // Make sure the scaled decoration stays square and will fit in its parent's box.
315 int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height()));
316 IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize);
317 return scaledRect;
318 }
319
paintSearchFieldResultsDecoration(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)320 bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
321 {
322 IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
323 if (iconRect.isEmpty())
324 return false;
325
326 GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_FIND,
327 gtkTextDirection(renderObject->style()->direction()),
328 gtkIconState(this, renderObject),
329 getIconSizeForPixelSize(rect.height()));
330 paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
331 return false;
332 }
333
adjustSearchFieldCancelButtonStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const334 void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
335 {
336 adjustSearchFieldIconStyle(style);
337 }
338
paintSearchFieldCancelButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)339 bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
340 {
341 IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect);
342 if (iconRect.isEmpty())
343 return false;
344
345 GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_CLEAR,
346 gtkTextDirection(renderObject->style()->direction()),
347 gtkIconState(this, renderObject),
348 getIconSizeForPixelSize(rect.height()));
349 paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
350 return false;
351 }
352
adjustSearchFieldStyle(CSSStyleSelector * selector,RenderStyle * style,Element * e) const353 void RenderThemeGtk::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
354 {
355 // We cannot give a proper rendering when border radius is active, unfortunately.
356 style->resetBorderRadius();
357 style->setLineHeight(RenderStyle::initialLineHeight());
358 }
359
paintSearchField(RenderObject * o,const PaintInfo & i,const IntRect & rect)360 bool RenderThemeGtk::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& rect)
361 {
362 return paintTextField(o, i, rect);
363 }
364
paintCapsLockIndicator(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)365 bool RenderThemeGtk::paintCapsLockIndicator(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
366 {
367 // The other paint methods don't need to check whether painting is disabled because RenderTheme already checks it
368 // before calling them, but paintCapsLockIndicator() is called by RenderTextControlSingleLine which doesn't check it.
369 if (paintInfo.context->paintingDisabled())
370 return true;
371
372 int iconSize = std::min(rect.width(), rect.height());
373 GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_CAPS_LOCK_WARNING,
374 gtkTextDirection(renderObject->style()->direction()),
375 0, getIconSizeForPixelSize(iconSize));
376
377 // Only re-scale the icon when it's smaller than the minimum icon size.
378 if (iconSize >= gtkIconSizeMenu)
379 iconSize = gdk_pixbuf_get_height(icon.get());
380
381 // GTK+ locates the icon right aligned in the entry. The given rectangle is already
382 // centered vertically by RenderTextControlSingleLine.
383 IntRect iconRect(rect.x() + rect.width() - iconSize,
384 rect.y() + (rect.height() - iconSize) / 2,
385 iconSize, iconSize);
386 paintGdkPixbuf(paintInfo.context, icon.get(), iconRect);
387 return true;
388 }
389
adjustSliderTrackStyle(CSSStyleSelector *,RenderStyle * style,Element *) const390 void RenderThemeGtk::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
391 {
392 style->setBoxShadow(0);
393 }
394
adjustSliderThumbStyle(CSSStyleSelector *,RenderStyle * style,Element *) const395 void RenderThemeGtk::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
396 {
397 style->setBoxShadow(0);
398 }
399
caretBlinkInterval() const400 double RenderThemeGtk::caretBlinkInterval() const
401 {
402 GtkSettings* settings = gtk_settings_get_default();
403
404 gboolean shouldBlink;
405 gint time;
406
407 g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL);
408
409 if (!shouldBlink)
410 return 0;
411
412 return time / 2000.;
413 }
414
getScreenDPI()415 double RenderThemeGtk::getScreenDPI()
416 {
417 // FIXME: Really this should be the widget's screen.
418 GdkScreen* screen = gdk_screen_get_default();
419 if (!screen)
420 return 96; // Default to 96 DPI.
421
422 float dpi = gdk_screen_get_resolution(screen);
423 if (dpi <= 0)
424 return 96;
425 return dpi;
426 }
427
systemFont(int,FontDescription & fontDescription) const428 void RenderThemeGtk::systemFont(int, FontDescription& fontDescription) const
429 {
430 GtkSettings* settings = gtk_settings_get_default();
431 if (!settings)
432 return;
433
434 // This will be a font selection string like "Sans 10" so we cannot use it as the family name.
435 GOwnPtr<gchar> fontName;
436 g_object_get(settings, "gtk-font-name", &fontName.outPtr(), NULL);
437
438 PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get());
439 if (!pangoDescription)
440 return;
441
442 fontDescription.firstFamily().setFamily(pango_font_description_get_family(pangoDescription));
443
444 int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE;
445 // If the size of the font is in points, we need to convert it to pixels.
446 if (!pango_font_description_get_size_is_absolute(pangoDescription))
447 size = size * (getScreenDPI() / 72.0);
448
449 fontDescription.setSpecifiedSize(size);
450 fontDescription.setIsAbsoluteSize(true);
451 fontDescription.setGenericFamily(FontDescription::NoFamily);
452 fontDescription.setWeight(FontWeightNormal);
453 fontDescription.setItalic(false);
454 pango_font_description_free(pangoDescription);
455 }
456
platformColorsDidChange()457 void RenderThemeGtk::platformColorsDidChange()
458 {
459 #if ENABLE(VIDEO)
460 initMediaColors();
461 #endif
462 RenderTheme::platformColorsDidChange();
463 }
464
465 #if ENABLE(VIDEO)
extraMediaControlsStyleSheet()466 String RenderThemeGtk::extraMediaControlsStyleSheet()
467 {
468 return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet));
469 }
470
adjustMediaSliderThumbSize(RenderObject * renderObject) const471 void RenderThemeGtk::adjustMediaSliderThumbSize(RenderObject* renderObject) const
472 {
473 ASSERT(renderObject->style()->appearance() == MediaSliderThumbPart);
474 renderObject->style()->setWidth(Length(m_mediaSliderThumbWidth, Fixed));
475 renderObject->style()->setHeight(Length(m_mediaSliderThumbHeight, Fixed));
476 }
477
paintMediaButton(RenderObject * renderObject,GraphicsContext * context,const IntRect & rect,const char * iconName)478 bool RenderThemeGtk::paintMediaButton(RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, const char* iconName)
479 {
480 GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_CONTAINER, iconName,
481 gtkTextDirection(renderObject->style()->direction()),
482 gtkIconState(this, renderObject),
483 getMediaButtonIconSize(m_mediaIconSize));
484 IntRect iconRect(rect.x() + (rect.width() - m_mediaIconSize) / 2,
485 rect.y() + (rect.height() - m_mediaIconSize) / 2,
486 m_mediaIconSize, m_mediaIconSize);
487 context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB);
488 paintGdkPixbuf(context, icon.get(), iconRect);
489 return false;
490 }
491
paintMediaFullscreenButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)492 bool RenderThemeGtk::paintMediaFullscreenButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
493 {
494 return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_FULLSCREEN);
495 }
496
paintMediaMuteButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)497 bool RenderThemeGtk::paintMediaMuteButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
498 {
499 HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(renderObject);
500 if (!mediaElement)
501 return false;
502
503 return paintMediaButton(renderObject, paintInfo.context, rect, mediaElement->muted() ? "audio-volume-muted" : "audio-volume-high");
504 }
505
paintMediaPlayButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)506 bool RenderThemeGtk::paintMediaPlayButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
507 {
508 Node* node = renderObject->node();
509 if (!node)
510 return false;
511
512 MediaControlPlayButtonElement* button = static_cast<MediaControlPlayButtonElement*>(node);
513 return paintMediaButton(renderObject, paintInfo.context, rect, button->displayType() == MediaPlayButton ? GTK_STOCK_MEDIA_PLAY : GTK_STOCK_MEDIA_PAUSE);
514 }
515
paintMediaSeekBackButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)516 bool RenderThemeGtk::paintMediaSeekBackButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
517 {
518 return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_REWIND);
519 }
520
paintMediaSeekForwardButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)521 bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
522 {
523 return paintMediaButton(renderObject, paintInfo.context, rect, GTK_STOCK_MEDIA_FORWARD);
524 }
525
paintMediaSliderTrack(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)526 bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
527 {
528 GraphicsContext* context = paintInfo.context;
529
530 context->fillRect(FloatRect(r), m_panelColor, ColorSpaceDeviceRGB);
531 context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2,
532 r.width(), m_mediaSliderHeight)), m_sliderColor, ColorSpaceDeviceRGB);
533
534 RenderStyle* style = o->style();
535 HTMLMediaElement* mediaElement = toParentMediaElement(o);
536
537 if (!mediaElement)
538 return false;
539
540 // Draw the buffered ranges. This code is highly inspired from
541 // Chrome for the gradient code.
542 float mediaDuration = mediaElement->duration();
543 RefPtr<TimeRanges> timeRanges = mediaElement->buffered();
544 IntRect trackRect = r;
545 int totalWidth = trackRect.width();
546
547 trackRect.inflate(-style->borderLeftWidth());
548 context->save();
549 context->setStrokeStyle(NoStroke);
550
551 for (unsigned index = 0; index < timeRanges->length(); ++index) {
552 ExceptionCode ignoredException;
553 float start = timeRanges->start(index, ignoredException);
554 float end = timeRanges->end(index, ignoredException);
555 int width = ((end - start) * totalWidth) / mediaDuration;
556 IntRect rangeRect;
557 if (!index) {
558 rangeRect = trackRect;
559 rangeRect.setWidth(width);
560 } else {
561 rangeRect.setLocation(IntPoint(trackRect.x() + start / mediaDuration* totalWidth, trackRect.y()));
562 rangeRect.setSize(IntSize(width, trackRect.height()));
563 }
564
565 // Don't bother drawing empty range.
566 if (rangeRect.isEmpty())
567 continue;
568
569 IntPoint sliderTopLeft = rangeRect.location();
570 IntPoint sliderTopRight = sliderTopLeft;
571 sliderTopRight.move(0, rangeRect.height());
572
573 RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight);
574 Color startColor = m_panelColor;
575 gradient->addColorStop(0.0, startColor);
576 gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha()));
577
578 context->setFillGradient(gradient);
579 context->fillRect(rangeRect);
580 }
581
582 context->restore();
583 return false;
584 }
585
paintMediaSliderThumb(RenderObject * o,const PaintInfo & paintInfo,const IntRect & r)586 bool RenderThemeGtk::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r)
587 {
588 // Make the thumb nicer with rounded corners.
589 paintInfo.context->fillRoundedRect(r, IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), m_sliderThumbColor, ColorSpaceDeviceRGB);
590 return false;
591 }
592
paintMediaVolumeSliderContainer(RenderObject *,const PaintInfo & paintInfo,const IntRect & rect)593 bool RenderThemeGtk::paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo& paintInfo, const IntRect& rect)
594 {
595 GraphicsContext* context = paintInfo.context;
596 context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB);
597 return false;
598 }
599
paintMediaVolumeSliderTrack(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)600 bool RenderThemeGtk::paintMediaVolumeSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
601 {
602 return paintSliderTrack(renderObject, paintInfo, rect);
603 }
604
paintMediaVolumeSliderThumb(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)605 bool RenderThemeGtk::paintMediaVolumeSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
606 {
607 return paintSliderThumb(renderObject, paintInfo, rect);
608 }
609
formatMediaControlsCurrentTime(float currentTime,float duration) const610 String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const
611 {
612 return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration);
613 }
614
paintMediaCurrentTime(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)615 bool RenderThemeGtk::paintMediaCurrentTime(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect)
616 {
617 GraphicsContext* context = paintInfo.context;
618
619 context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB);
620 return false;
621 }
622 #endif
623
624 #if ENABLE(PROGRESS_TAG)
adjustProgressBarStyle(CSSStyleSelector *,RenderStyle * style,Element *) const625 void RenderThemeGtk::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
626 {
627 style->setBoxShadow(0);
628 }
629
630 // These values have been copied from RenderThemeChromiumSkia.cpp
631 static const int progressActivityBlocks = 5;
632 static const int progressAnimationFrames = 10;
633 static const double progressAnimationInterval = 0.125;
animationRepeatIntervalForProgressBar(RenderProgress *) const634 double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const
635 {
636 return progressAnimationInterval;
637 }
638
animationDurationForProgressBar(RenderProgress *) const639 double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const
640 {
641 return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth;
642 }
643
calculateProgressRect(RenderObject * renderObject,const IntRect & fullBarRect)644 IntRect RenderThemeGtk::calculateProgressRect(RenderObject* renderObject, const IntRect& fullBarRect)
645 {
646 IntRect progressRect(fullBarRect);
647 RenderProgress* renderProgress = toRenderProgress(renderObject);
648 if (renderProgress->isDeterminate()) {
649 int progressWidth = progressRect.width() * renderProgress->position();
650 if (renderObject->style()->direction() == RTL)
651 progressRect.setX(progressRect.x() + progressRect.width() - progressWidth);
652 progressRect.setWidth(progressWidth);
653 return progressRect;
654 }
655
656 double animationProgress = renderProgress->animationProgress();
657
658 // Never let the progress rect shrink smaller than 2 pixels.
659 int newWidth = max(2, progressRect.width() / progressActivityBlocks);
660 int movableWidth = progressRect.width() - newWidth;
661 progressRect.setWidth(newWidth);
662
663 // We want the first 0.5 units of the animation progress to represent the
664 // forward motion and the second 0.5 units to represent the backward motion,
665 // thus we multiply by two here to get the full sweep of the progress bar with
666 // each direction.
667 if (animationProgress < 0.5)
668 progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth));
669 else
670 progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth));
671 return progressRect;
672 }
673 #endif
674
675 }
676