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#ifndef GTK_API_VERSION_2 29 30#include "CSSValueKeywords.h" 31#include "GraphicsContext.h" 32#include "GtkVersioning.h" 33#include "HTMLNames.h" 34#include "MediaControlElements.h" 35#include "Page.h" 36#include "PaintInfo.h" 37#include "PlatformContextCairo.h" 38#include "RenderObject.h" 39#include "TextDirection.h" 40#include "UserAgentStyleSheets.h" 41#include <cmath> 42#include <gdk/gdk.h> 43#include <gtk/gtk.h> 44 45namespace WebCore { 46 47// This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c. 48static const int minArrowSize = 15; 49// This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c. 50static const int minSpinButtonArrowSize = 6; 51 52typedef HashMap<GType, GRefPtr<GtkStyleContext> > StyleContextMap; 53static StyleContextMap& styleContextMap(); 54 55static void gtkStyleChangedCallback(GObject*, GParamSpec*) 56{ 57 StyleContextMap::const_iterator end = styleContextMap().end(); 58 for (StyleContextMap::const_iterator iter = styleContextMap().begin(); iter != end; ++iter) 59 gtk_style_context_invalidate(iter->value.get()); 60 61 Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment(); 62} 63 64static StyleContextMap& styleContextMap() 65{ 66 DEFINE_STATIC_LOCAL(StyleContextMap, map, ()); 67 68 static bool initialized = false; 69 if (!initialized) { 70 GtkSettings* settings = gtk_settings_get_default(); 71 g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(gtkStyleChangedCallback), 0); 72 g_signal_connect(settings, "notify::gtk-color-scheme", G_CALLBACK(gtkStyleChangedCallback), 0); 73 initialized = true; 74 } 75 return map; 76} 77 78static GtkStyleContext* getStyleContext(GType widgetType) 79{ 80 StyleContextMap::AddResult result = styleContextMap().add(widgetType, 0); 81 if (!result.isNewEntry) 82 return result.iterator->value.get(); 83 84 GtkWidgetPath* path = gtk_widget_path_new(); 85 gtk_widget_path_append_type(path, widgetType); 86 87 if (widgetType == GTK_TYPE_SCROLLBAR) 88 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCROLLBAR); 89 else if (widgetType == GTK_TYPE_ENTRY) 90 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_ENTRY); 91 else if (widgetType == GTK_TYPE_ARROW) 92 gtk_widget_path_iter_add_class(path, 0, "arrow"); 93 else if (widgetType == GTK_TYPE_BUTTON) 94 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_BUTTON); 95 else if (widgetType == GTK_TYPE_SCALE) 96 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SCALE); 97 else if (widgetType == GTK_TYPE_SEPARATOR) 98 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SEPARATOR); 99 else if (widgetType == GTK_TYPE_PROGRESS_BAR) 100 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_PROGRESSBAR); 101 else if (widgetType == GTK_TYPE_SPIN_BUTTON) 102 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_SPINBUTTON); 103 else if (widgetType == GTK_TYPE_TREE_VIEW) 104 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_VIEW); 105 else if (widgetType == GTK_TYPE_CHECK_BUTTON) 106 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_CHECK); 107 else if (widgetType == GTK_TYPE_RADIO_BUTTON) 108 gtk_widget_path_iter_add_class(path, 0, GTK_STYLE_CLASS_RADIO); 109 110 GRefPtr<GtkStyleContext> context = adoptGRef(gtk_style_context_new()); 111 gtk_style_context_set_path(context.get(), path); 112 gtk_widget_path_free(path); 113 114 result.iterator->value = context; 115 return context.get(); 116} 117 118GtkStyleContext* RenderThemeGtk::gtkScrollbarStyle() 119{ 120 return getStyleContext(GTK_TYPE_SCROLLBAR); 121} 122 123// This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h. 124extern GtkTextDirection gtkTextDirection(TextDirection); 125 126void RenderThemeGtk::platformInit() 127{ 128} 129 130RenderThemeGtk::~RenderThemeGtk() 131{ 132} 133 134#if ENABLE(VIDEO) 135void RenderThemeGtk::initMediaColors() 136{ 137 GdkRGBA color; 138 GtkStyleContext* containerContext = getStyleContext(GTK_TYPE_CONTAINER); 139 140 gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_NORMAL, &color); 141 m_panelColor = color; 142 gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_ACTIVE, &color); 143 m_sliderColor = color; 144 gtk_style_context_get_background_color(containerContext, GTK_STATE_FLAG_SELECTED, &color); 145 m_sliderThumbColor = color; 146} 147#endif 148 149static void adjustRectForFocus(GtkStyleContext* context, IntRect& rect) 150{ 151 gint focusWidth, focusPad; 152 gtk_style_context_get_style(context, 153 "focus-line-width", &focusWidth, 154 "focus-padding", &focusPad, NULL); 155 rect.inflate(focusWidth + focusPad); 156} 157 158void RenderThemeGtk::adjustRepaintRect(const RenderObject* renderObject, IntRect& rect) 159{ 160 GtkStyleContext* context = 0; 161 bool checkInteriorFocus = false; 162 ControlPart part = renderObject->style()->appearance(); 163 switch (part) { 164 case CheckboxPart: 165 case RadioPart: 166 context = getStyleContext(part == CheckboxPart ? GTK_TYPE_CHECK_BUTTON : GTK_TYPE_RADIO_BUTTON); 167 168 gint indicatorSpacing; 169 gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL); 170 rect.inflate(indicatorSpacing); 171 172 return; 173 case SliderVerticalPart: 174 case SliderHorizontalPart: 175 context = getStyleContext(GTK_TYPE_SCALE); 176 break; 177 case ButtonPart: 178 case MenulistButtonPart: 179 case MenulistPart: 180 context = getStyleContext(GTK_TYPE_BUTTON); 181 checkInteriorFocus = true; 182 break; 183 case TextFieldPart: 184 case TextAreaPart: 185 context = getStyleContext(GTK_TYPE_ENTRY); 186 checkInteriorFocus = true; 187 break; 188 default: 189 return; 190 } 191 192 ASSERT(context); 193 if (checkInteriorFocus) { 194 gboolean interiorFocus; 195 gtk_style_context_get_style(context, "interior-focus", &interiorFocus, NULL); 196 if (interiorFocus) 197 return; 198 } 199 adjustRectForFocus(context, rect); 200} 201 202static void setToggleSize(GtkStyleContext* context, RenderStyle* style) 203{ 204 // The width and height are both specified, so we shouldn't change them. 205 if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) 206 return; 207 208 // Other ports hard-code this to 13 which is also the default value defined by GTK+. 209 // GTK+ users tend to demand the native look. 210 // It could be made a configuration option values other than 13 actually break site compatibility. 211 gint indicatorSize; 212 gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL); 213 214 if (style->width().isIntrinsicOrAuto()) 215 style->setWidth(Length(indicatorSize, Fixed)); 216 217 if (style->height().isAuto()) 218 style->setHeight(Length(indicatorSize, Fixed)); 219} 220 221static void paintToggle(const RenderThemeGtk* theme, GType widgetType, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& fullRect) 222{ 223 GtkStyleContext* context = getStyleContext(widgetType); 224 gtk_style_context_save(context); 225 226 // Some themes do not render large toggle buttons properly, so we simply 227 // shrink the rectangle back down to the default size and then center it 228 // in the full toggle button region. The reason for not simply forcing toggle 229 // buttons to be a smaller size is that we don't want to break site layouts. 230 gint indicatorSize; 231 gtk_style_context_get_style(context, "indicator-size", &indicatorSize, NULL); 232 IntRect rect(fullRect); 233 if (rect.width() > indicatorSize) { 234 rect.inflateX(-(rect.width() - indicatorSize) / 2); 235 rect.setWidth(indicatorSize); // In case rect.width() was equal to indicatorSize + 1. 236 } 237 238 if (rect.height() > indicatorSize) { 239 rect.inflateY(-(rect.height() - indicatorSize) / 2); 240 rect.setHeight(indicatorSize); // In case rect.height() was equal to indicatorSize + 1. 241 } 242 243 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()))); 244 gtk_style_context_add_class(context, widgetType == GTK_TYPE_CHECK_BUTTON ? GTK_STYLE_CLASS_CHECK : GTK_STYLE_CLASS_RADIO); 245 246 guint flags = 0; 247 if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject)) 248 flags |= GTK_STATE_FLAG_INSENSITIVE; 249 else if (theme->isHovered(renderObject)) 250 flags |= GTK_STATE_FLAG_PRELIGHT; 251 if (theme->isIndeterminate(renderObject)) 252 flags |= GTK_STATE_FLAG_INCONSISTENT; 253 else if (theme->isChecked(renderObject)) 254 flags |= GTK_STATE_FLAG_ACTIVE; 255 if (theme->isPressed(renderObject)) 256 flags |= GTK_STATE_FLAG_SELECTED; 257 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 258 259 if (widgetType == GTK_TYPE_CHECK_BUTTON) 260 gtk_render_check(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 261 else 262 gtk_render_option(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 263 264 if (theme->isFocused(renderObject)) { 265 IntRect indicatorRect(rect); 266 gint indicatorSpacing; 267 gtk_style_context_get_style(context, "indicator-spacing", &indicatorSpacing, NULL); 268 indicatorRect.inflate(indicatorSpacing); 269 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), indicatorRect.x(), indicatorRect.y(), 270 indicatorRect.width(), indicatorRect.height()); 271 } 272 273 gtk_style_context_restore(context); 274} 275 276void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const 277{ 278 setToggleSize(getStyleContext(GTK_TYPE_CHECK_BUTTON), style); 279} 280 281bool RenderThemeGtk::paintCheckbox(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 282{ 283 paintToggle(this, GTK_TYPE_CHECK_BUTTON, renderObject, paintInfo, rect); 284 return false; 285} 286 287void RenderThemeGtk::setRadioSize(RenderStyle* style) const 288{ 289 setToggleSize(getStyleContext(GTK_TYPE_RADIO_BUTTON), style); 290} 291 292bool RenderThemeGtk::paintRadio(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 293{ 294 paintToggle(this, GTK_TYPE_RADIO_BUTTON, renderObject, paintInfo, rect); 295 return false; 296} 297 298static void renderButton(RenderTheme* theme, GtkStyleContext* context, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 299{ 300 IntRect buttonRect(rect); 301 302 guint flags = 0; 303 if (!theme->isEnabled(renderObject) || theme->isReadOnlyControl(renderObject)) 304 flags |= GTK_STATE_FLAG_INSENSITIVE; 305 else if (theme->isHovered(renderObject)) 306 flags |= GTK_STATE_FLAG_PRELIGHT; 307 if (theme->isPressed(renderObject)) 308 flags |= GTK_STATE_FLAG_ACTIVE; 309 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 310 311 if (theme->isDefault(renderObject)) { 312 GtkBorder* borderPtr = 0; 313 GtkBorder border = { 1, 1, 1, 1 }; 314 315 gtk_style_context_get_style(context, "default-border", &borderPtr, NULL); 316 if (borderPtr) { 317 border = *borderPtr; 318 gtk_border_free(borderPtr); 319 } 320 321 buttonRect.move(border.left, border.top); 322 buttonRect.setWidth(buttonRect.width() - (border.left + border.right)); 323 buttonRect.setHeight(buttonRect.height() - (border.top + border.bottom)); 324 325 gtk_style_context_add_class(context, GTK_STYLE_CLASS_DEFAULT); 326 } 327 328 gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); 329 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); 330 331 if (theme->isFocused(renderObject)) { 332 gint focusWidth, focusPad; 333 gboolean displaceFocus, interiorFocus; 334 gtk_style_context_get_style(context, 335 "focus-line-width", &focusWidth, 336 "focus-padding", &focusPad, 337 "interior-focus", &interiorFocus, 338 "displace-focus", &displaceFocus, 339 NULL); 340 341 if (interiorFocus) { 342 GtkBorder borderWidth; 343 gtk_style_context_get_border(context, static_cast<GtkStateFlags>(flags), &borderWidth); 344 345 buttonRect = IntRect(buttonRect.x() + borderWidth.left + focusPad, buttonRect.y() + borderWidth.top + focusPad, 346 buttonRect.width() - (2 * focusPad + borderWidth.left + borderWidth.right), 347 buttonRect.height() - (2 * focusPad + borderWidth.top + borderWidth.bottom)); 348 } else 349 buttonRect.inflate(focusWidth + focusPad); 350 351 if (displaceFocus && theme->isPressed(renderObject)) { 352 gint childDisplacementX; 353 gint childDisplacementY; 354 gtk_style_context_get_style(context, 355 "child-displacement-x", &childDisplacementX, 356 "child-displacement-y", &childDisplacementY, 357 NULL); 358 buttonRect.move(childDisplacementX, childDisplacementY); 359 } 360 361 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); 362 } 363} 364bool RenderThemeGtk::paintButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 365{ 366 GtkStyleContext* context = getStyleContext(GTK_TYPE_BUTTON); 367 gtk_style_context_save(context); 368 369 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()))); 370 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); 371 372 renderButton(this, context, renderObject, paintInfo, rect); 373 374 gtk_style_context_restore(context); 375 376 return false; 377} 378 379static void getComboBoxMetrics(RenderStyle* style, GtkBorder& border, int& focus, int& separator) 380{ 381 // If this menu list button isn't drawn using the native theme, we 382 // don't add any extra padding beyond what WebCore already uses. 383 if (style->appearance() == NoControlPart) 384 return; 385 386 GtkStyleContext* context = getStyleContext(GTK_TYPE_COMBO_BOX); 387 gtk_style_context_save(context); 388 389 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); 390 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(style->direction()))); 391 392 gtk_style_context_get_border(context, static_cast<GtkStateFlags>(0), &border); 393 394 gboolean interiorFocus; 395 gint focusWidth, focusPad; 396 gtk_style_context_get_style(context, 397 "interior-focus", &interiorFocus, 398 "focus-line-width", &focusWidth, 399 "focus-padding", &focusPad, NULL); 400 focus = interiorFocus ? focusWidth + focusPad : 0; 401 402 gtk_style_context_restore(context); 403 404 context = getStyleContext(GTK_TYPE_SEPARATOR); 405 gtk_style_context_save(context); 406 407 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(style->direction())); 408 gtk_style_context_set_direction(context, direction); 409 gtk_style_context_add_class(context, "separator"); 410 411 gboolean wideSeparators; 412 gint separatorWidth; 413 gtk_style_context_get_style(context, 414 "wide-separators", &wideSeparators, 415 "separator-width", &separatorWidth, 416 NULL); 417 418 // GTK+ always uses border.left, regardless of text direction. See gtkseperator.c. 419 if (!wideSeparators) 420 separatorWidth = border.left; 421 422 separator = separatorWidth; 423 424 gtk_style_context_restore(context); 425} 426 427int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const 428{ 429 GtkBorder borderWidth = { 0, 0, 0, 0 }; 430 int focusWidth = 0, separatorWidth = 0; 431 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); 432 int left = borderWidth.left + focusWidth; 433 if (style->direction() == RTL) 434 left += separatorWidth + minArrowSize; 435 return left; 436} 437 438int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const 439{ 440 GtkBorder borderWidth = { 0, 0, 0, 0 }; 441 int focusWidth = 0, separatorWidth = 0; 442 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); 443 int right = borderWidth.right + focusWidth; 444 if (style->direction() == LTR) 445 right += separatorWidth + minArrowSize; 446 return right; 447} 448 449int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const 450{ 451 GtkBorder borderWidth = { 0, 0, 0, 0 }; 452 int focusWidth = 0, separatorWidth = 0; 453 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); 454 return borderWidth.top + focusWidth; 455} 456 457int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const 458{ 459 GtkBorder borderWidth = { 0, 0, 0, 0 }; 460 int focusWidth = 0, separatorWidth = 0; 461 getComboBoxMetrics(style, borderWidth, focusWidth, separatorWidth); 462 return borderWidth.bottom + focusWidth; 463} 464 465bool RenderThemeGtk::paintMenuList(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 466{ 467 cairo_t* cairoContext = paintInfo.context->platformContext()->cr(); 468 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())); 469 470 // Paint the button. 471 GtkStyleContext* buttonStyleContext = getStyleContext(GTK_TYPE_BUTTON); 472 gtk_style_context_save(buttonStyleContext); 473 gtk_style_context_set_direction(buttonStyleContext, direction); 474 gtk_style_context_add_class(buttonStyleContext, GTK_STYLE_CLASS_BUTTON); 475 renderButton(this, buttonStyleContext, renderObject, paintInfo, rect); 476 477 // Get the inner rectangle. 478 gint focusWidth, focusPad; 479 GtkBorder* innerBorderPtr = 0; 480 GtkBorder innerBorder = { 1, 1, 1, 1 }; 481 gtk_style_context_get_style(buttonStyleContext, 482 "inner-border", &innerBorderPtr, 483 "focus-line-width", &focusWidth, 484 "focus-padding", &focusPad, 485 NULL); 486 if (innerBorderPtr) { 487 innerBorder = *innerBorderPtr; 488 gtk_border_free(innerBorderPtr); 489 } 490 491 GtkBorder borderWidth; 492 GtkStateFlags state = gtk_style_context_get_state(buttonStyleContext); 493 gtk_style_context_get_border(buttonStyleContext, state, &borderWidth); 494 495 focusWidth += focusPad; 496 IntRect innerRect(rect.x() + innerBorder.left + borderWidth.left + focusWidth, 497 rect.y() + innerBorder.top + borderWidth.top + focusWidth, 498 rect.width() - borderWidth.left - borderWidth.right - innerBorder.left - innerBorder.right - (2 * focusWidth), 499 rect.height() - borderWidth.top - borderWidth.bottom - innerBorder.top - innerBorder.bottom - (2 * focusWidth)); 500 501 if (isPressed(renderObject)) { 502 gint childDisplacementX; 503 gint childDisplacementY; 504 gtk_style_context_get_style(buttonStyleContext, 505 "child-displacement-x", &childDisplacementX, 506 "child-displacement-y", &childDisplacementY, 507 NULL); 508 innerRect.move(childDisplacementX, childDisplacementY); 509 } 510 innerRect.setWidth(max(1, innerRect.width())); 511 innerRect.setHeight(max(1, innerRect.height())); 512 513 gtk_style_context_restore(buttonStyleContext); 514 515 // Paint the arrow. 516 GtkStyleContext* arrowStyleContext = getStyleContext(GTK_TYPE_ARROW); 517 gtk_style_context_save(arrowStyleContext); 518 519 gtk_style_context_set_direction(arrowStyleContext, direction); 520 gtk_style_context_add_class(arrowStyleContext, "arrow"); 521 gtk_style_context_add_class(arrowStyleContext, GTK_STYLE_CLASS_BUTTON); 522 523 gfloat arrowScaling; 524 gtk_style_context_get_style(arrowStyleContext, "arrow-scaling", &arrowScaling, NULL); 525 526 IntSize arrowSize(minArrowSize, innerRect.height()); 527 FloatPoint arrowPosition(innerRect.location()); 528 if (direction == GTK_TEXT_DIR_LTR) 529 arrowPosition.move(innerRect.width() - arrowSize.width(), 0); 530 531 // GTK+ actually fetches the xalign and valign values from the widget, but since we 532 // don't have a widget here, we are just using the default xalign and valign values of 0.5. 533 gint extent = std::min(arrowSize.width(), arrowSize.height()) * arrowScaling; 534 arrowPosition.move((arrowSize.width() - extent) / 2, (arrowSize.height() - extent) / 2); 535 536 gtk_style_context_set_state(arrowStyleContext, state); 537 gtk_render_arrow(arrowStyleContext, cairoContext, G_PI, arrowPosition.x(), arrowPosition.y(), extent); 538 539 gtk_style_context_restore(arrowStyleContext); 540 541 // Paint the separator if needed. 542 GtkStyleContext* separatorStyleContext = getStyleContext(GTK_TYPE_COMBO_BOX); 543 gtk_style_context_save(separatorStyleContext); 544 545 gtk_style_context_set_direction(separatorStyleContext, direction); 546 gtk_style_context_add_class(separatorStyleContext, "separator"); 547 548 gboolean wideSeparators; 549 gint separatorWidth; 550 gtk_style_context_get_style(separatorStyleContext, 551 "wide-separators", &wideSeparators, 552 "separator-width", &separatorWidth, 553 NULL); 554 if (wideSeparators && !separatorWidth) { 555 gtk_style_context_restore(separatorStyleContext); 556 return false; 557 } 558 559 gtk_style_context_set_state(separatorStyleContext, state); 560 IntPoint separatorPosition(arrowPosition.x(), innerRect.y()); 561 if (wideSeparators) { 562 if (direction == GTK_TEXT_DIR_LTR) 563 separatorPosition.move(-separatorWidth, 0); 564 else 565 separatorPosition.move(arrowSize.width(), 0); 566 567 gtk_render_frame(separatorStyleContext, cairoContext, 568 separatorPosition.x(), separatorPosition.y(), 569 separatorWidth, innerRect.height()); 570 } else { 571 GtkBorder padding; 572 gtk_style_context_get_padding(separatorStyleContext, state, &padding); 573 GtkBorder border; 574 gtk_style_context_get_border(separatorStyleContext, state, &border); 575 576 if (direction == GTK_TEXT_DIR_LTR) 577 separatorPosition.move(-(padding.left + border.left), 0); 578 else 579 separatorPosition.move(arrowSize.width(), 0); 580 581 cairo_save(cairoContext); 582 583 // An extra clip prevents the separator bleeding outside of the specified rectangle because of subpixel positioning. 584 cairo_rectangle(cairoContext, separatorPosition.x(), separatorPosition.y(), border.left, innerRect.height()); 585 cairo_clip(cairoContext); 586 gtk_render_line(separatorStyleContext, cairoContext, 587 separatorPosition.x(), separatorPosition.y(), 588 separatorPosition.x(), innerRect.maxY()); 589 cairo_restore(cairoContext); 590 } 591 592 gtk_style_context_restore(separatorStyleContext); 593 return false; 594} 595 596bool RenderThemeGtk::paintTextField(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 597{ 598 GtkStyleContext* context = getStyleContext(GTK_TYPE_ENTRY); 599 gtk_style_context_save(context); 600 601 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction()))); 602 gtk_style_context_add_class(context, GTK_STYLE_CLASS_ENTRY); 603 604 guint flags = 0; 605 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) 606 flags |= GTK_STATE_FLAG_INSENSITIVE; 607 else if (isFocused(renderObject)) 608 flags |= GTK_STATE_FLAG_FOCUSED; 609 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 610 611 gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 612 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 613 614 if (isFocused(renderObject) && isEnabled(renderObject)) { 615 gboolean interiorFocus; 616 gint focusWidth, focusPad; 617 gtk_style_context_get_style(context, 618 "interior-focus", &interiorFocus, 619 "focus-line-width", &focusWidth, 620 "focus-padding", &focusPad, 621 NULL); 622 if (!interiorFocus) { 623 IntRect focusRect(rect); 624 focusRect.inflate(focusWidth + focusPad); 625 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), 626 focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height()); 627 } 628 } 629 630 gtk_style_context_restore(context); 631 632 return false; 633} 634 635static void applySliderStyleContextClasses(GtkStyleContext* context, ControlPart part) 636{ 637 gtk_style_context_add_class(context, GTK_STYLE_CLASS_SCALE); 638 if (part == SliderHorizontalPart || part == SliderThumbHorizontalPart) 639 gtk_style_context_add_class(context, GTK_STYLE_CLASS_HORIZONTAL); 640 else if (part == SliderVerticalPart || part == SliderThumbVerticalPart) 641 gtk_style_context_add_class(context, GTK_STYLE_CLASS_VERTICAL); 642} 643 644bool RenderThemeGtk::paintSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 645{ 646 ControlPart part = renderObject->style()->appearance(); 647 ASSERT_UNUSED(part, part == SliderHorizontalPart || part == SliderVerticalPart || part == MediaVolumeSliderPart); 648 649 GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE); 650 gtk_style_context_save(context); 651 652 gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction())); 653 applySliderStyleContextClasses(context, part); 654 gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH); 655 656 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) 657 gtk_style_context_set_state(context, GTK_STATE_FLAG_INSENSITIVE); 658 659 gtk_render_background(context, paintInfo.context->platformContext()->cr(), 660 rect.x(), rect.y(), rect.width(), rect.height()); 661 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), 662 rect.x(), rect.y(), rect.width(), rect.height()); 663 664 if (isFocused(renderObject)) { 665 gint focusWidth, focusPad; 666 gtk_style_context_get_style(context, 667 "focus-line-width", &focusWidth, 668 "focus-padding", &focusPad, NULL); 669 IntRect focusRect(rect); 670 focusRect.inflate(focusWidth + focusPad); 671 gtk_render_focus(context, paintInfo.context->platformContext()->cr(), 672 focusRect.x(), focusRect.y(), focusRect.width(), focusRect.height()); 673 } 674 675 gtk_style_context_restore(context); 676 return false; 677} 678 679bool RenderThemeGtk::paintSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 680{ 681 ControlPart part = renderObject->style()->appearance(); 682 ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart); 683 684 GtkStyleContext* context = getStyleContext(GTK_TYPE_SCALE); 685 gtk_style_context_save(context); 686 687 gtk_style_context_set_direction(context, gtkTextDirection(renderObject->style()->direction())); 688 applySliderStyleContextClasses(context, part); 689 gtk_style_context_add_class(context, GTK_STYLE_CLASS_SLIDER); 690 691 guint flags = 0; 692 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) 693 flags |= GTK_STATE_FLAG_INSENSITIVE; 694 else if (isHovered(renderObject)) 695 flags |= GTK_STATE_FLAG_PRELIGHT; 696 if (isPressed(renderObject)) 697 flags |= GTK_STATE_FLAG_ACTIVE; 698 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 699 700 gtk_render_slider(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height(), 701 part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); 702 703 gtk_style_context_restore(context); 704 705 return false; 706} 707 708void RenderThemeGtk::adjustSliderThumbSize(RenderStyle* style, Element*) const 709{ 710 ControlPart part = style->appearance(); 711 if (part != SliderThumbHorizontalPart && part != SliderThumbVerticalPart) 712 return; 713 714 gint sliderWidth, sliderLength; 715 gtk_style_context_get_style(getStyleContext(GTK_TYPE_SCALE), 716 "slider-width", &sliderWidth, 717 "slider-length", &sliderLength, 718 NULL); 719 if (part == SliderThumbHorizontalPart) { 720 style->setWidth(Length(sliderLength, Fixed)); 721 style->setHeight(Length(sliderWidth, Fixed)); 722 return; 723 } 724 ASSERT(part == SliderThumbVerticalPart || part == MediaVolumeSliderThumbPart); 725 style->setWidth(Length(sliderWidth, Fixed)); 726 style->setHeight(Length(sliderLength, Fixed)); 727} 728 729#if ENABLE(PROGRESS_ELEMENT) 730bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 731{ 732 if (!renderObject->isProgress()) 733 return true; 734 735 GtkStyleContext* context = getStyleContext(GTK_TYPE_PROGRESS_BAR); 736 gtk_style_context_save(context); 737 738 gtk_style_context_add_class(context, GTK_STYLE_CLASS_TROUGH); 739 740 gtk_render_background(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 741 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), rect.x(), rect.y(), rect.width(), rect.height()); 742 743 gtk_style_context_restore(context); 744 745 gtk_style_context_save(context); 746 gtk_style_context_add_class(context, GTK_STYLE_CLASS_PROGRESSBAR); 747 748 749 GtkBorder padding; 750 gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding); 751 IntRect progressRect(rect.x() + padding.left, rect.y() + padding.top, 752 rect.width() - (padding.left + padding.right), 753 rect.height() - (padding.top + padding.bottom)); 754 progressRect = RenderThemeGtk::calculateProgressRect(renderObject, progressRect); 755 756 if (!progressRect.isEmpty()) 757 gtk_render_activity(context, paintInfo.context->platformContext()->cr(), progressRect.x(), progressRect.y(), progressRect.width(), progressRect.height()); 758 759 gtk_style_context_restore(context); 760 return false; 761} 762#endif 763 764static gint spinButtonArrowSize(GtkStyleContext* context) 765{ 766 const PangoFontDescription* fontDescription; 767 gtk_style_context_get(context, static_cast<GtkStateFlags>(0), "font", &fontDescription, NULL); 768 gint fontSize = pango_font_description_get_size(fontDescription); 769 gint arrowSize = max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize); 770 771 return arrowSize - arrowSize % 2; // Force even. 772} 773 774void RenderThemeGtk::adjustInnerSpinButtonStyle(StyleResolver*, RenderStyle* style, Element*) const 775{ 776 GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON); 777 778 GtkBorder padding; 779 gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding); 780 781 int width = spinButtonArrowSize(context) + padding.left + padding.right; 782 style->setWidth(Length(width, Fixed)); 783 style->setMinWidth(Length(width, Fixed)); 784} 785 786static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType) 787{ 788 ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN); 789 790 gtk_style_context_save(context); 791 gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); 792 793 GtkTextDirection direction = gtk_style_context_get_direction(context); 794 guint state = static_cast<guint>(gtk_style_context_get_state(context)); 795 if (!(state & GTK_STATE_FLAG_INSENSITIVE)) { 796 if (theme->isPressed(renderObject)) { 797 if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject)) 798 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject))) 799 state |= GTK_STATE_FLAG_ACTIVE; 800 } else if (theme->isHovered(renderObject)) { 801 if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject)) 802 || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject))) 803 state |= GTK_STATE_FLAG_PRELIGHT; 804 } 805 } 806 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state)); 807 808 // Paint button. 809 IntRect buttonRect(rect); 810 guint junction = gtk_style_context_get_junction_sides(context); 811 if (arrowType == GTK_ARROW_UP) 812 junction |= GTK_JUNCTION_BOTTOM; 813 else { 814 junction |= GTK_JUNCTION_TOP; 815 buttonRect.move(0, rect.height() / 2); 816 } 817 buttonRect.setHeight(rect.height() / 2); 818 gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction)); 819 820 gtk_render_background(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); 821 gtk_render_frame(context, paintInfo.context->platformContext()->cr(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); 822 823 // Paint arrow centered inside button. 824 // This code is based on gtkspinbutton.c code. 825 IntRect arrowRect; 826 gdouble angle; 827 if (arrowType == GTK_ARROW_UP) { 828 angle = 0; 829 arrowRect.setY(rect.y()); 830 arrowRect.setHeight(rect.height() / 2 - 2); 831 } else { 832 angle = G_PI; 833 arrowRect.setY(rect.y() + buttonRect.y()); 834 arrowRect.setHeight(rect.height() - arrowRect.y() - 2); 835 } 836 arrowRect.setWidth(rect.width() - 3); 837 if (direction == GTK_TEXT_DIR_LTR) 838 arrowRect.setX(rect.x() + 1); 839 else 840 arrowRect.setX(rect.x() + 2); 841 842 gint width = arrowRect.width() / 2; 843 width -= width % 2 - 1; // Force odd. 844 gint height = (width + 1) / 2; 845 846 arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2); 847 gtk_render_arrow(context, paintInfo.context->platformContext()->cr(), angle, arrowRect.x(), arrowRect.y(), width); 848 849 gtk_style_context_restore(context); 850} 851 852bool RenderThemeGtk::paintInnerSpinButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) 853{ 854 GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON); 855 gtk_style_context_save(context); 856 857 GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())); 858 gtk_style_context_set_direction(context, direction); 859 860 guint flags = 0; 861 if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) 862 flags |= GTK_STATE_FLAG_INSENSITIVE; 863 else if (isFocused(renderObject)) 864 flags |= GTK_STATE_FLAG_FOCUSED; 865 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 866 gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ENTRY); 867 868 paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP); 869 paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN); 870 871 gtk_style_context_restore(context); 872 873 return false; 874} 875 876GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize) 877{ 878 GtkStyleContext* context = getStyleContext(widgetType); 879 GtkIconSet* iconSet = gtk_style_context_lookup_icon_set(context, iconName); 880 881 gtk_style_context_save(context); 882 883 guint flags = 0; 884 if (state == GTK_STATE_PRELIGHT) 885 flags |= GTK_STATE_FLAG_PRELIGHT; 886 else if (state == GTK_STATE_INSENSITIVE) 887 flags |= GTK_STATE_FLAG_INSENSITIVE; 888 889 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 890 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction)); 891 GdkPixbuf* icon = gtk_icon_set_render_icon_pixbuf(iconSet, context, static_cast<GtkIconSize>(iconSize)); 892 893 gtk_style_context_restore(context); 894 895 return adoptGRef(icon); 896} 897 898GRefPtr<GdkPixbuf> getStockSymbolicIconForWidgetType(GType widgetType, const char* symbolicIconName, const char *fallbackStockIconName, gint direction, gint state, gint iconSize) 899{ 900 GtkStyleContext* context = getStyleContext(widgetType); 901 902 gtk_style_context_save(context); 903 904 guint flags = 0; 905 if (state == GTK_STATE_PRELIGHT) 906 flags |= GTK_STATE_FLAG_PRELIGHT; 907 else if (state == GTK_STATE_INSENSITIVE) 908 flags |= GTK_STATE_FLAG_INSENSITIVE; 909 910 gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); 911 gtk_style_context_set_direction(context, static_cast<GtkTextDirection>(direction)); 912 GtkIconInfo* info = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), symbolicIconName, iconSize, 913 static_cast<GtkIconLookupFlags>(GTK_ICON_LOOKUP_FORCE_SVG | GTK_ICON_LOOKUP_FORCE_SIZE)); 914 GdkPixbuf* icon = 0; 915 if (info) { 916 icon = gtk_icon_info_load_symbolic_for_context(info, context, 0, 0); 917 gtk_icon_info_free(info); 918 } 919 920 gtk_style_context_restore(context); 921 922 if (!icon) 923 return getStockIconForWidgetType(widgetType, fallbackStockIconName, direction, state, iconSize); 924 925 return adoptGRef(icon); 926} 927 928Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const 929{ 930 GdkRGBA gdkRGBAColor; 931 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); 932 return gdkRGBAColor; 933} 934 935Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const 936{ 937 GdkRGBA gdkRGBAColor; 938 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); 939 return gdkRGBAColor; 940} 941 942Color RenderThemeGtk::platformActiveSelectionForegroundColor() const 943{ 944 GdkRGBA gdkRGBAColor; 945 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); 946 return gdkRGBAColor; 947} 948 949Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const 950{ 951 GdkRGBA gdkRGBAColor; 952 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); 953 return gdkRGBAColor; 954} 955 956Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const 957{ 958 GdkRGBA gdkRGBAColor; 959 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); 960 return gdkRGBAColor; 961} 962 963Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const 964{ 965 GdkRGBA gdkRGBAColor; 966 gtk_style_context_get_background_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); 967 return gdkRGBAColor; 968} 969 970Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const 971{ 972 GdkRGBA gdkRGBAColor; 973 gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_SELECTED, &gdkRGBAColor); 974 return gdkRGBAColor; 975} 976 977Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const 978{ 979 GdkRGBA gdkRGBAColor; 980 gtk_style_context_get_color(getStyleContext(GTK_TYPE_TREE_VIEW), GTK_STATE_FLAG_ACTIVE, &gdkRGBAColor); 981 return gdkRGBAColor; 982} 983 984Color RenderThemeGtk::systemColor(int cssValueId) const 985{ 986 GdkRGBA gdkRGBAColor; 987 988 switch (cssValueId) { 989 case CSSValueButtontext: 990 gtk_style_context_get_color(getStyleContext(GTK_TYPE_BUTTON), static_cast<GtkStateFlags>(0), &gdkRGBAColor); 991 return gdkRGBAColor; 992 case CSSValueCaptiontext: 993 gtk_style_context_get_color(getStyleContext(GTK_TYPE_ENTRY), static_cast<GtkStateFlags>(0), &gdkRGBAColor); 994 return gdkRGBAColor; 995 default: 996 return RenderTheme::systemColor(cssValueId); 997 } 998} 999 1000} // namespace WebCore 1001 1002#endif // !GTK_API_VERSION_2 1003