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
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
gtkStyleChangedCallback(GObject *,GParamSpec *)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
styleContextMap()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
getStyleContext(GType widgetType)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
gtkScrollbarStyle()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
platformInit()103 void RenderThemeGtk::platformInit()
104 {
105 }
106
~RenderThemeGtk()107 RenderThemeGtk::~RenderThemeGtk()
108 {
109 }
110
111 #if ENABLE(VIDEO)
initMediaColors()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
adjustRectForFocus(GtkStyleContext * context,IntRect & rect)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
adjustRepaintRect(const RenderObject * renderObject,IntRect & rect)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
setToggleSize(GtkStyleContext * context,RenderStyle * style)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
paintToggle(const RenderThemeGtk * theme,GType widgetType,RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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
setCheckboxSize(RenderStyle * style) const236 void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const
237 {
238 setToggleSize(getStyleContext(GTK_TYPE_CHECK_BUTTON), style);
239 }
240
paintCheckbox(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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
setRadioSize(RenderStyle * style) const247 void RenderThemeGtk::setRadioSize(RenderStyle* style) const
248 {
249 setToggleSize(getStyleContext(GTK_TYPE_RADIO_BUTTON), style);
250 }
251
paintRadio(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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
renderButton(RenderTheme * theme,GtkStyleContext * context,RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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 }
paintButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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
getComboBoxMetrics(RenderStyle * style,GtkBorder & border,int & focus,int & separator)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
popupInternalPaddingLeft(RenderStyle * style) const387 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
popupInternalPaddingRight(RenderStyle * style) const398 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
popupInternalPaddingTop(RenderStyle * style) const409 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
popupInternalPaddingBottom(RenderStyle * style) const417 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
paintMenuList(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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
paintTextField(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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
paintSliderTrack(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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
paintSliderThumb(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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
adjustSliderThumbSize(RenderObject * renderObject) const666 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)
paintProgressBar(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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
spinButtonArrowSize(GtkStyleContext * context)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
adjustInnerSpinButtonStyle(CSSStyleSelector *,RenderStyle * style,Element *) const735 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
paintSpinArrowButton(RenderTheme * theme,GtkStyleContext * context,RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect,GtkArrowType arrowType)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
paintInnerSpinButton(RenderObject * renderObject,const PaintInfo & paintInfo,const IntRect & rect)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
getStockIcon(GType widgetType,const char * iconName,gint direction,gint state,gint iconSize)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
platformActiveSelectionBackgroundColor() const859 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
platformInactiveSelectionBackgroundColor() const866 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
platformActiveSelectionForegroundColor() const873 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
platformInactiveSelectionForegroundColor() const880 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
activeListBoxSelectionBackgroundColor() const887 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
inactiveListBoxSelectionBackgroundColor() const894 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
activeListBoxSelectionForegroundColor() const901 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
inactiveListBoxSelectionForegroundColor() const908 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
systemColor(int cssValueId) const915 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