• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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