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