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