| 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 | #include "CSSValueKeywords.h" |
|---|
| 29 | #include "ExceptionCodePlaceholder.h" |
|---|
| 30 | #include "FileList.h" |
|---|
| 31 | #include "FileSystem.h" |
|---|
| 32 | #include "FontDescription.h" |
|---|
| 33 | #include <wtf/gobject/GOwnPtr.h> |
|---|
| 34 | #include "Gradient.h" |
|---|
| 35 | #include "GraphicsContext.h" |
|---|
| 36 | #include "GtkVersioning.h" |
|---|
| 37 | #include "HTMLMediaElement.h" |
|---|
| 38 | #include "HTMLNames.h" |
|---|
| 39 | #include "LocalizedStrings.h" |
|---|
| 40 | #include "MediaControlElements.h" |
|---|
| 41 | #include "PaintInfo.h" |
|---|
| 42 | #include "PlatformContextCairo.h" |
|---|
| 43 | #include "RenderBox.h" |
|---|
| 44 | #include "RenderObject.h" |
|---|
| 45 | #include "StringTruncator.h" |
|---|
| 46 | #include "TimeRanges.h" |
|---|
| 47 | #include "UserAgentStyleSheets.h" |
|---|
| 48 | #include <cmath> |
|---|
| 49 | #include <gdk/gdk.h> |
|---|
| 50 | #include <glib.h> |
|---|
| 51 | #include <gtk/gtk.h> |
|---|
| 52 | #include <wtf/text/CString.h> |
|---|
| 53 | |
|---|
| 54 | #if ENABLE(PROGRESS_ELEMENT) |
|---|
| 55 | #include "RenderProgress.h" |
|---|
| 56 | #endif |
|---|
| 57 | |
|---|
| 58 | namespace WebCore { |
|---|
| 59 | |
|---|
| 60 | // This would be a static method, except that forward declaring GType is tricky, since its |
|---|
| 61 | // definition depends on including glib.h, negating the benefit of using a forward declaration. |
|---|
| 62 | extern GRefPtr<GdkPixbuf> getStockIconForWidgetType(GType, const char* iconName, gint direction, gint state, gint iconSize); |
|---|
| 63 | extern GRefPtr<GdkPixbuf> getStockSymbolicIconForWidgetType(GType widgetType, const char* symbolicIconName, const char *fallbackStockIconName, gint direction, gint state, gint iconSize); |
|---|
| 64 | |
|---|
| 65 | using namespace HTMLNames; |
|---|
| 66 | |
|---|
| 67 | #if ENABLE(VIDEO) |
|---|
| 68 | static HTMLMediaElement* getMediaElementFromRenderObject(RenderObject* o) |
|---|
| 69 | { |
|---|
| 70 | Node* node = o->node(); |
|---|
| 71 | Node* mediaNode = node ? node->shadowHost() : 0; |
|---|
| 72 | if (!mediaNode) |
|---|
| 73 | mediaNode = node; |
|---|
| 74 | if (!mediaNode || !mediaNode->isElementNode() || !static_cast<Element*>(mediaNode)->isMediaElement()) |
|---|
| 75 | return 0; |
|---|
| 76 | |
|---|
| 77 | return static_cast<HTMLMediaElement*>(mediaNode); |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | void RenderThemeGtk::initMediaButtons() |
|---|
| 81 | { |
|---|
| 82 | static bool iconsInitialized = false; |
|---|
| 83 | |
|---|
| 84 | if (iconsInitialized) |
|---|
| 85 | return; |
|---|
| 86 | |
|---|
| 87 | GRefPtr<GtkIconFactory> iconFactory = adoptGRef(gtk_icon_factory_new()); |
|---|
| 88 | GtkIconSource* iconSource = gtk_icon_source_new(); |
|---|
| 89 | const char* icons[] = { "audio-volume-high", "audio-volume-muted" }; |
|---|
| 90 | |
|---|
| 91 | gtk_icon_factory_add_default(iconFactory.get()); |
|---|
| 92 | |
|---|
| 93 | for (size_t i = 0; i < G_N_ELEMENTS(icons); ++i) { |
|---|
| 94 | gtk_icon_source_set_icon_name(iconSource, icons[i]); |
|---|
| 95 | GtkIconSet* iconSet = gtk_icon_set_new(); |
|---|
| 96 | gtk_icon_set_add_source(iconSet, iconSource); |
|---|
| 97 | gtk_icon_factory_add(iconFactory.get(), icons[i], iconSet); |
|---|
| 98 | gtk_icon_set_unref(iconSet); |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | gtk_icon_source_free(iconSource); |
|---|
| 102 | |
|---|
| 103 | iconsInitialized = true; |
|---|
| 104 | } |
|---|
| 105 | #endif |
|---|
| 106 | |
|---|
| 107 | PassRefPtr<RenderTheme> RenderThemeGtk::create() |
|---|
| 108 | { |
|---|
| 109 | return adoptRef(new RenderThemeGtk()); |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) |
|---|
| 113 | { |
|---|
| 114 | static RenderTheme* rt = RenderThemeGtk::create().leakRef(); |
|---|
| 115 | return rt; |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | RenderThemeGtk::RenderThemeGtk() |
|---|
| 119 | : m_panelColor(Color::white) |
|---|
| 120 | , m_sliderColor(Color::white) |
|---|
| 121 | , m_sliderThumbColor(Color::white) |
|---|
| 122 | , m_mediaIconSize(16) |
|---|
| 123 | , m_mediaSliderHeight(14) |
|---|
| 124 | { |
|---|
| 125 | platformInit(); |
|---|
| 126 | #if ENABLE(VIDEO) |
|---|
| 127 | initMediaColors(); |
|---|
| 128 | initMediaButtons(); |
|---|
| 129 | #endif |
|---|
| 130 | } |
|---|
| 131 | |
|---|
| 132 | static bool supportsFocus(ControlPart appearance) |
|---|
| 133 | { |
|---|
| 134 | switch (appearance) { |
|---|
| 135 | case PushButtonPart: |
|---|
| 136 | case ButtonPart: |
|---|
| 137 | case TextFieldPart: |
|---|
| 138 | case TextAreaPart: |
|---|
| 139 | case SearchFieldPart: |
|---|
| 140 | case MenulistPart: |
|---|
| 141 | case RadioPart: |
|---|
| 142 | case CheckboxPart: |
|---|
| 143 | case SliderHorizontalPart: |
|---|
| 144 | case SliderVerticalPart: |
|---|
| 145 | case MediaPlayButtonPart: |
|---|
| 146 | case MediaVolumeSliderPart: |
|---|
| 147 | case MediaMuteButtonPart: |
|---|
| 148 | case MediaEnterFullscreenButtonPart: |
|---|
| 149 | case MediaSliderPart: |
|---|
| 150 | return true; |
|---|
| 151 | default: |
|---|
| 152 | return false; |
|---|
| 153 | } |
|---|
| 154 | } |
|---|
| 155 | |
|---|
| 156 | bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const |
|---|
| 157 | { |
|---|
| 158 | return supportsFocus(style->appearance()); |
|---|
| 159 | } |
|---|
| 160 | |
|---|
| 161 | bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const |
|---|
| 162 | { |
|---|
| 163 | return isEnabled(o); |
|---|
| 164 | } |
|---|
| 165 | |
|---|
| 166 | int RenderThemeGtk::baselinePosition(const RenderObject* o) const |
|---|
| 167 | { |
|---|
| 168 | if (!o->isBox()) |
|---|
| 169 | return 0; |
|---|
| 170 | |
|---|
| 171 | // FIXME: This strategy is possibly incorrect for the GTK+ port. |
|---|
| 172 | if (o->style()->appearance() == CheckboxPart |
|---|
| 173 | || o->style()->appearance() == RadioPart) { |
|---|
| 174 | const RenderBox* box = toRenderBox(o); |
|---|
| 175 | return box->marginTop() + box->height() - 2; |
|---|
| 176 | } |
|---|
| 177 | |
|---|
| 178 | return RenderTheme::baselinePosition(o); |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | // This is used in RenderThemeGtk2 and RenderThemeGtk3. Normally, it would be in |
|---|
| 182 | // the RenderThemeGtk header (perhaps as a static method), but we want to avoid |
|---|
| 183 | // having to include GTK+ headers only for the GtkTextDirection enum. |
|---|
| 184 | GtkTextDirection gtkTextDirection(TextDirection direction) |
|---|
| 185 | { |
|---|
| 186 | switch (direction) { |
|---|
| 187 | case RTL: |
|---|
| 188 | return GTK_TEXT_DIR_RTL; |
|---|
| 189 | case LTR: |
|---|
| 190 | return GTK_TEXT_DIR_LTR; |
|---|
| 191 | default: |
|---|
| 192 | return GTK_TEXT_DIR_NONE; |
|---|
| 193 | } |
|---|
| 194 | } |
|---|
| 195 | |
|---|
| 196 | static GtkStateType gtkIconState(RenderTheme* theme, RenderObject* renderObject) |
|---|
| 197 | { |
|---|
| 198 | if (!theme->isEnabled(renderObject)) |
|---|
| 199 | return GTK_STATE_INSENSITIVE; |
|---|
| 200 | if (theme->isPressed(renderObject)) |
|---|
| 201 | return GTK_STATE_ACTIVE; |
|---|
| 202 | if (theme->isHovered(renderObject)) |
|---|
| 203 | return GTK_STATE_PRELIGHT; |
|---|
| 204 | |
|---|
| 205 | return GTK_STATE_NORMAL; |
|---|
| 206 | } |
|---|
| 207 | |
|---|
| 208 | void RenderThemeGtk::adjustButtonStyle(StyleResolver*, RenderStyle* style, WebCore::Element*) const |
|---|
| 209 | { |
|---|
| 210 | // Some layout tests check explicitly that buttons ignore line-height. |
|---|
| 211 | if (style->appearance() == PushButtonPart) |
|---|
| 212 | style->setLineHeight(RenderStyle::initialLineHeight()); |
|---|
| 213 | } |
|---|
| 214 | |
|---|
| 215 | void RenderThemeGtk::adjustMenuListStyle(StyleResolver*, RenderStyle* style, Element*) const |
|---|
| 216 | { |
|---|
| 217 | // The tests check explicitly that select menu buttons ignore line height. |
|---|
| 218 | style->setLineHeight(RenderStyle::initialLineHeight()); |
|---|
| 219 | |
|---|
| 220 | // We cannot give a proper rendering when border radius is active, unfortunately. |
|---|
| 221 | style->resetBorderRadius(); |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | void RenderThemeGtk::adjustMenuListButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const |
|---|
| 225 | { |
|---|
| 226 | adjustMenuListStyle(styleResolver, style, e); |
|---|
| 227 | } |
|---|
| 228 | |
|---|
| 229 | bool RenderThemeGtk::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect) |
|---|
| 230 | { |
|---|
| 231 | return paintMenuList(object, info, rect); |
|---|
| 232 | } |
|---|
| 233 | |
|---|
| 234 | bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) |
|---|
| 235 | { |
|---|
| 236 | return paintTextField(o, i, r); |
|---|
| 237 | } |
|---|
| 238 | |
|---|
| 239 | static void paintGdkPixbuf(GraphicsContext* context, const GdkPixbuf* icon, const IntRect& iconRect) |
|---|
| 240 | { |
|---|
| 241 | IntSize iconSize(gdk_pixbuf_get_width(icon), gdk_pixbuf_get_height(icon)); |
|---|
| 242 | GRefPtr<GdkPixbuf> scaledIcon; |
|---|
| 243 | if (iconRect.size() != iconSize) { |
|---|
| 244 | // We could use cairo_scale() here but cairo/pixman downscale quality is quite bad. |
|---|
| 245 | scaledIcon = adoptGRef(gdk_pixbuf_scale_simple(icon, iconRect.width(), iconRect.height(), |
|---|
| 246 | GDK_INTERP_BILINEAR)); |
|---|
| 247 | icon = scaledIcon.get(); |
|---|
| 248 | } |
|---|
| 249 | |
|---|
| 250 | cairo_t* cr = context->platformContext()->cr(); |
|---|
| 251 | cairo_save(cr); |
|---|
| 252 | gdk_cairo_set_source_pixbuf(cr, icon, iconRect.x(), iconRect.y()); |
|---|
| 253 | cairo_paint(cr); |
|---|
| 254 | cairo_restore(cr); |
|---|
| 255 | } |
|---|
| 256 | |
|---|
| 257 | // Defined in GTK+ (gtk/gtkiconfactory.c) |
|---|
| 258 | static const gint gtkIconSizeMenu = 16; |
|---|
| 259 | static const gint gtkIconSizeSmallToolbar = 18; |
|---|
| 260 | static const gint gtkIconSizeButton = 20; |
|---|
| 261 | static const gint gtkIconSizeLargeToolbar = 24; |
|---|
| 262 | static const gint gtkIconSizeDnd = 32; |
|---|
| 263 | static const gint gtkIconSizeDialog = 48; |
|---|
| 264 | |
|---|
| 265 | static GtkIconSize getIconSizeForPixelSize(gint pixelSize) |
|---|
| 266 | { |
|---|
| 267 | if (pixelSize < gtkIconSizeSmallToolbar) |
|---|
| 268 | return GTK_ICON_SIZE_MENU; |
|---|
| 269 | if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton) |
|---|
| 270 | return GTK_ICON_SIZE_SMALL_TOOLBAR; |
|---|
| 271 | if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar) |
|---|
| 272 | return GTK_ICON_SIZE_BUTTON; |
|---|
| 273 | if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd) |
|---|
| 274 | return GTK_ICON_SIZE_LARGE_TOOLBAR; |
|---|
| 275 | if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog) |
|---|
| 276 | return GTK_ICON_SIZE_DND; |
|---|
| 277 | |
|---|
| 278 | return GTK_ICON_SIZE_DIALOG; |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(StyleResolver* styleResolver, RenderStyle* style, Element* e) const |
|---|
| 282 | { |
|---|
| 283 | adjustSearchFieldCancelButtonStyle(styleResolver, style, e); |
|---|
| 284 | } |
|---|
| 285 | |
|---|
| 286 | bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
|---|
| 287 | { |
|---|
| 288 | return paintSearchFieldResultsDecoration(o, i, rect); |
|---|
| 289 | } |
|---|
| 290 | |
|---|
| 291 | static void adjustSearchFieldIconStyle(RenderStyle* style) |
|---|
| 292 | { |
|---|
| 293 | style->resetBorder(); |
|---|
| 294 | style->resetPadding(); |
|---|
| 295 | |
|---|
| 296 | // Get the icon size based on the font size. |
|---|
| 297 | int fontSize = style->fontSize(); |
|---|
| 298 | if (fontSize < gtkIconSizeMenu) { |
|---|
| 299 | style->setWidth(Length(fontSize, Fixed)); |
|---|
| 300 | style->setHeight(Length(fontSize, Fixed)); |
|---|
| 301 | return; |
|---|
| 302 | } |
|---|
| 303 | gint width = 0, height = 0; |
|---|
| 304 | gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height); |
|---|
| 305 | style->setWidth(Length(width, Fixed)); |
|---|
| 306 | style->setHeight(Length(height, Fixed)); |
|---|
| 307 | } |
|---|
| 308 | |
|---|
| 309 | void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(StyleResolver*, RenderStyle* style, Element*) const |
|---|
| 310 | { |
|---|
| 311 | adjustSearchFieldIconStyle(style); |
|---|
| 312 | } |
|---|
| 313 | |
|---|
| 314 | static IntRect centerRectVerticallyInParentInputElement(RenderObject* renderObject, const IntRect& rect) |
|---|
| 315 | { |
|---|
| 316 | // Get the renderer of <input> element. |
|---|
| 317 | Node* input = renderObject->node()->shadowHost(); |
|---|
| 318 | if (!input) |
|---|
| 319 | input = renderObject->node(); |
|---|
| 320 | if (!input->renderer()->isBox()) |
|---|
| 321 | return IntRect(); |
|---|
| 322 | |
|---|
| 323 | // If possible center the y-coordinate of the rect vertically in the parent input element. |
|---|
| 324 | // We also add one pixel here to ensure that the y coordinate is rounded up for box heights |
|---|
| 325 | // that are even, which looks in relation to the box text. |
|---|
| 326 | IntRect inputContentBox = toRenderBox(input->renderer())->absoluteContentBox(); |
|---|
| 327 | |
|---|
| 328 | // Make sure the scaled decoration stays square and will fit in its parent's box. |
|---|
| 329 | int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height())); |
|---|
| 330 | IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize); |
|---|
| 331 | return scaledRect; |
|---|
| 332 | } |
|---|
| 333 | |
|---|
| 334 | bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 335 | { |
|---|
| 336 | IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect); |
|---|
| 337 | if (iconRect.isEmpty()) |
|---|
| 338 | return false; |
|---|
| 339 | |
|---|
| 340 | GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_FIND, |
|---|
| 341 | gtkTextDirection(renderObject->style()->direction()), |
|---|
| 342 | gtkIconState(this, renderObject), |
|---|
| 343 | getIconSizeForPixelSize(rect.height())); |
|---|
| 344 | paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); |
|---|
| 345 | return false; |
|---|
| 346 | } |
|---|
| 347 | |
|---|
| 348 | void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const |
|---|
| 349 | { |
|---|
| 350 | adjustSearchFieldIconStyle(style); |
|---|
| 351 | } |
|---|
| 352 | |
|---|
| 353 | bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 354 | { |
|---|
| 355 | IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect); |
|---|
| 356 | if (iconRect.isEmpty()) |
|---|
| 357 | return false; |
|---|
| 358 | |
|---|
| 359 | GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_CLEAR, |
|---|
| 360 | gtkTextDirection(renderObject->style()->direction()), |
|---|
| 361 | gtkIconState(this, renderObject), |
|---|
| 362 | getIconSizeForPixelSize(rect.height())); |
|---|
| 363 | paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); |
|---|
| 364 | return false; |
|---|
| 365 | } |
|---|
| 366 | |
|---|
| 367 | void RenderThemeGtk::adjustSearchFieldStyle(StyleResolver*, RenderStyle* style, Element*) const |
|---|
| 368 | { |
|---|
| 369 | // We cannot give a proper rendering when border radius is active, unfortunately. |
|---|
| 370 | style->resetBorderRadius(); |
|---|
| 371 | style->setLineHeight(RenderStyle::initialLineHeight()); |
|---|
| 372 | } |
|---|
| 373 | |
|---|
| 374 | bool RenderThemeGtk::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
|---|
| 375 | { |
|---|
| 376 | return paintTextField(o, i, rect); |
|---|
| 377 | } |
|---|
| 378 | |
|---|
| 379 | bool RenderThemeGtk::paintCapsLockIndicator(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 380 | { |
|---|
| 381 | // The other paint methods don't need to check whether painting is disabled because RenderTheme already checks it |
|---|
| 382 | // before calling them, but paintCapsLockIndicator() is called by RenderTextControlSingleLine which doesn't check it. |
|---|
| 383 | if (paintInfo.context->paintingDisabled()) |
|---|
| 384 | return true; |
|---|
| 385 | |
|---|
| 386 | int iconSize = std::min(rect.width(), rect.height()); |
|---|
| 387 | GRefPtr<GdkPixbuf> icon = getStockIconForWidgetType(GTK_TYPE_ENTRY, GTK_STOCK_CAPS_LOCK_WARNING, |
|---|
| 388 | gtkTextDirection(renderObject->style()->direction()), |
|---|
| 389 | 0, getIconSizeForPixelSize(iconSize)); |
|---|
| 390 | |
|---|
| 391 | // Only re-scale the icon when it's smaller than the minimum icon size. |
|---|
| 392 | if (iconSize >= gtkIconSizeMenu) |
|---|
| 393 | iconSize = gdk_pixbuf_get_height(icon.get()); |
|---|
| 394 | |
|---|
| 395 | // GTK+ locates the icon right aligned in the entry. The given rectangle is already |
|---|
| 396 | // centered vertically by RenderTextControlSingleLine. |
|---|
| 397 | IntRect iconRect(rect.x() + rect.width() - iconSize, |
|---|
| 398 | rect.y() + (rect.height() - iconSize) / 2, |
|---|
| 399 | iconSize, iconSize); |
|---|
| 400 | paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); |
|---|
| 401 | return true; |
|---|
| 402 | } |
|---|
| 403 | |
|---|
| 404 | void RenderThemeGtk::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element*) const |
|---|
| 405 | { |
|---|
| 406 | style->setBoxShadow(nullptr); |
|---|
| 407 | } |
|---|
| 408 | |
|---|
| 409 | void RenderThemeGtk::adjustSliderThumbStyle(StyleResolver* styleResolver, RenderStyle* style, Element* element) const |
|---|
| 410 | { |
|---|
| 411 | RenderTheme::adjustSliderThumbStyle(styleResolver, style, element); |
|---|
| 412 | style->setBoxShadow(nullptr); |
|---|
| 413 | } |
|---|
| 414 | |
|---|
| 415 | double RenderThemeGtk::caretBlinkInterval() const |
|---|
| 416 | { |
|---|
| 417 | GtkSettings* settings = gtk_settings_get_default(); |
|---|
| 418 | |
|---|
| 419 | gboolean shouldBlink; |
|---|
| 420 | gint time; |
|---|
| 421 | |
|---|
| 422 | g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL); |
|---|
| 423 | |
|---|
| 424 | if (!shouldBlink) |
|---|
| 425 | return 0; |
|---|
| 426 | |
|---|
| 427 | return time / 2000.; |
|---|
| 428 | } |
|---|
| 429 | |
|---|
| 430 | double RenderThemeGtk::getScreenDPI() |
|---|
| 431 | { |
|---|
| 432 | // FIXME: Really this should be the widget's screen. |
|---|
| 433 | GdkScreen* screen = gdk_screen_get_default(); |
|---|
| 434 | if (!screen) |
|---|
| 435 | return 96; // Default to 96 DPI. |
|---|
| 436 | |
|---|
| 437 | float dpi = gdk_screen_get_resolution(screen); |
|---|
| 438 | if (dpi <= 0) |
|---|
| 439 | return 96; |
|---|
| 440 | return dpi; |
|---|
| 441 | } |
|---|
| 442 | |
|---|
| 443 | void RenderThemeGtk::systemFont(int, FontDescription& fontDescription) const |
|---|
| 444 | { |
|---|
| 445 | GtkSettings* settings = gtk_settings_get_default(); |
|---|
| 446 | if (!settings) |
|---|
| 447 | return; |
|---|
| 448 | |
|---|
| 449 | // This will be a font selection string like "Sans 10" so we cannot use it as the family name. |
|---|
| 450 | GOwnPtr<gchar> fontName; |
|---|
| 451 | g_object_get(settings, "gtk-font-name", &fontName.outPtr(), NULL); |
|---|
| 452 | |
|---|
| 453 | PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get()); |
|---|
| 454 | if (!pangoDescription) |
|---|
| 455 | return; |
|---|
| 456 | |
|---|
| 457 | fontDescription.firstFamily().setFamily(pango_font_description_get_family(pangoDescription)); |
|---|
| 458 | |
|---|
| 459 | int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE; |
|---|
| 460 | // If the size of the font is in points, we need to convert it to pixels. |
|---|
| 461 | if (!pango_font_description_get_size_is_absolute(pangoDescription)) |
|---|
| 462 | size = size * (getScreenDPI() / 72.0); |
|---|
| 463 | |
|---|
| 464 | fontDescription.setSpecifiedSize(size); |
|---|
| 465 | fontDescription.setIsAbsoluteSize(true); |
|---|
| 466 | fontDescription.setGenericFamily(FontDescription::NoFamily); |
|---|
| 467 | fontDescription.setWeight(FontWeightNormal); |
|---|
| 468 | fontDescription.setItalic(false); |
|---|
| 469 | pango_font_description_free(pangoDescription); |
|---|
| 470 | } |
|---|
| 471 | |
|---|
| 472 | void RenderThemeGtk::platformColorsDidChange() |
|---|
| 473 | { |
|---|
| 474 | #if ENABLE(VIDEO) |
|---|
| 475 | initMediaColors(); |
|---|
| 476 | #endif |
|---|
| 477 | RenderTheme::platformColorsDidChange(); |
|---|
| 478 | } |
|---|
| 479 | |
|---|
| 480 | #if ENABLE(VIDEO) |
|---|
| 481 | String RenderThemeGtk::extraMediaControlsStyleSheet() |
|---|
| 482 | { |
|---|
| 483 | return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet)); |
|---|
| 484 | } |
|---|
| 485 | |
|---|
| 486 | #if ENABLE(FULLSCREEN_API) |
|---|
| 487 | String RenderThemeGtk::extraFullScreenStyleSheet() |
|---|
| 488 | { |
|---|
| 489 | return String(); |
|---|
| 490 | } |
|---|
| 491 | #endif |
|---|
| 492 | |
|---|
| 493 | bool RenderThemeGtk::paintMediaButton(RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, const char* symbolicIconName, const char* fallbackStockIconName) |
|---|
| 494 | { |
|---|
| 495 | IntRect iconRect(rect.x() + (rect.width() - m_mediaIconSize) / 2, |
|---|
| 496 | rect.y() + (rect.height() - m_mediaIconSize) / 2, |
|---|
| 497 | m_mediaIconSize, m_mediaIconSize); |
|---|
| 498 | GRefPtr<GdkPixbuf> icon = getStockSymbolicIconForWidgetType(GTK_TYPE_CONTAINER, symbolicIconName, fallbackStockIconName, |
|---|
| 499 | gtkTextDirection(renderObject->style()->direction()), gtkIconState(this, renderObject), iconRect.width()); |
|---|
| 500 | paintGdkPixbuf(context, icon.get(), iconRect); |
|---|
| 501 | return false; |
|---|
| 502 | } |
|---|
| 503 | |
|---|
| 504 | bool RenderThemeGtk::hasOwnDisabledStateHandlingFor(ControlPart part) const |
|---|
| 505 | { |
|---|
| 506 | return (part != MediaMuteButtonPart); |
|---|
| 507 | } |
|---|
| 508 | |
|---|
| 509 | bool RenderThemeGtk::paintMediaFullscreenButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 510 | { |
|---|
| 511 | return paintMediaButton(renderObject, paintInfo.context, rect, "view-fullscreen-symbolic", GTK_STOCK_FULLSCREEN); |
|---|
| 512 | } |
|---|
| 513 | |
|---|
| 514 | bool RenderThemeGtk::paintMediaMuteButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 515 | { |
|---|
| 516 | HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(renderObject); |
|---|
| 517 | if (!mediaElement) |
|---|
| 518 | return false; |
|---|
| 519 | |
|---|
| 520 | bool muted = mediaElement->muted(); |
|---|
| 521 | return paintMediaButton(renderObject, paintInfo.context, rect, |
|---|
| 522 | muted ? "audio-volume-muted-symbolic" : "audio-volume-high-symbolic", |
|---|
| 523 | muted ? "audio-volume-muted" : "audio-volume-high"); |
|---|
| 524 | } |
|---|
| 525 | |
|---|
| 526 | bool RenderThemeGtk::paintMediaPlayButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 527 | { |
|---|
| 528 | Node* node = renderObject->node(); |
|---|
| 529 | if (!node) |
|---|
| 530 | return false; |
|---|
| 531 | if (!node->isMediaControlElement()) |
|---|
| 532 | return false; |
|---|
| 533 | |
|---|
| 534 | bool play = mediaControlElementType(node) == MediaPlayButton; |
|---|
| 535 | return paintMediaButton(renderObject, paintInfo.context, rect, |
|---|
| 536 | play ? "media-playback-start-symbolic" : "media-playback-pause-symbolic", |
|---|
| 537 | play ? GTK_STOCK_MEDIA_PLAY : GTK_STOCK_MEDIA_PAUSE); |
|---|
| 538 | } |
|---|
| 539 | |
|---|
| 540 | bool RenderThemeGtk::paintMediaSeekBackButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 541 | { |
|---|
| 542 | return paintMediaButton(renderObject, paintInfo.context, rect, "media-seek-backward-symbolic", GTK_STOCK_MEDIA_REWIND); |
|---|
| 543 | } |
|---|
| 544 | |
|---|
| 545 | bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 546 | { |
|---|
| 547 | return paintMediaButton(renderObject, paintInfo.context, rect, "media-seek-forward-symbolic", GTK_STOCK_MEDIA_FORWARD); |
|---|
| 548 | } |
|---|
| 549 | |
|---|
| 550 | static RoundedRect::Radii borderRadiiFromStyle(RenderStyle* style) |
|---|
| 551 | { |
|---|
| 552 | return RoundedRect::Radii( |
|---|
| 553 | IntSize(style->borderTopLeftRadius().width().intValue(), style->borderTopLeftRadius().height().intValue()), |
|---|
| 554 | IntSize(style->borderTopRightRadius().width().intValue(), style->borderTopRightRadius().height().intValue()), |
|---|
| 555 | IntSize(style->borderBottomLeftRadius().width().intValue(), style->borderBottomLeftRadius().height().intValue()), |
|---|
| 556 | IntSize(style->borderBottomRightRadius().width().intValue(), style->borderBottomRightRadius().height().intValue())); |
|---|
| 557 | } |
|---|
| 558 | |
|---|
| 559 | bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) |
|---|
| 560 | { |
|---|
| 561 | HTMLMediaElement* mediaElement = toParentMediaElement(o); |
|---|
| 562 | if (!mediaElement) |
|---|
| 563 | return false; |
|---|
| 564 | |
|---|
| 565 | GraphicsContext* context = paintInfo.context; |
|---|
| 566 | context->save(); |
|---|
| 567 | context->setStrokeStyle(NoStroke); |
|---|
| 568 | |
|---|
| 569 | float mediaDuration = mediaElement->duration(); |
|---|
| 570 | float totalTrackWidth = r.width(); |
|---|
| 571 | RenderStyle* style = o->style(); |
|---|
| 572 | RefPtr<TimeRanges> timeRanges = mediaElement->buffered(); |
|---|
| 573 | for (unsigned index = 0; index < timeRanges->length(); ++index) { |
|---|
| 574 | float start = timeRanges->start(index, IGNORE_EXCEPTION); |
|---|
| 575 | float end = timeRanges->end(index, IGNORE_EXCEPTION); |
|---|
| 576 | float startRatio = start / mediaDuration; |
|---|
| 577 | float lengthRatio = (end - start) / mediaDuration; |
|---|
| 578 | if (!lengthRatio) |
|---|
| 579 | continue; |
|---|
| 580 | |
|---|
| 581 | IntRect rangeRect(r); |
|---|
| 582 | rangeRect.setWidth(lengthRatio * totalTrackWidth); |
|---|
| 583 | if (index) |
|---|
| 584 | rangeRect.move(startRatio * totalTrackWidth, 0); |
|---|
| 585 | context->fillRoundedRect(RoundedRect(rangeRect, borderRadiiFromStyle(style)), style->visitedDependentColor(CSSPropertyColor), style->colorSpace()); |
|---|
| 586 | } |
|---|
| 587 | |
|---|
| 588 | context->restore(); |
|---|
| 589 | return false; |
|---|
| 590 | } |
|---|
| 591 | |
|---|
| 592 | bool RenderThemeGtk::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) |
|---|
| 593 | { |
|---|
| 594 | RenderStyle* style = o->style(); |
|---|
| 595 | paintInfo.context->fillRoundedRect(RoundedRect(r, borderRadiiFromStyle(style)), style->visitedDependentColor(CSSPropertyColor), style->colorSpace()); |
|---|
| 596 | return false; |
|---|
| 597 | } |
|---|
| 598 | |
|---|
| 599 | bool RenderThemeGtk::paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 600 | { |
|---|
| 601 | return true; |
|---|
| 602 | } |
|---|
| 603 | |
|---|
| 604 | bool RenderThemeGtk::paintMediaVolumeSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 605 | { |
|---|
| 606 | HTMLMediaElement* mediaElement = toParentMediaElement(renderObject); |
|---|
| 607 | if (!mediaElement) |
|---|
| 608 | return true; |
|---|
| 609 | |
|---|
| 610 | float volume = mediaElement->volume(); |
|---|
| 611 | if (!volume) |
|---|
| 612 | return true; |
|---|
| 613 | |
|---|
| 614 | GraphicsContext* context = paintInfo.context; |
|---|
| 615 | context->save(); |
|---|
| 616 | context->setStrokeStyle(NoStroke); |
|---|
| 617 | |
|---|
| 618 | int rectHeight = rect.height(); |
|---|
| 619 | float trackHeight = rectHeight * volume; |
|---|
| 620 | RenderStyle* style = renderObject->style(); |
|---|
| 621 | IntRect volumeRect(rect); |
|---|
| 622 | volumeRect.move(0, rectHeight - trackHeight); |
|---|
| 623 | volumeRect.setHeight(ceil(trackHeight)); |
|---|
| 624 | |
|---|
| 625 | context->fillRoundedRect(RoundedRect(volumeRect, borderRadiiFromStyle(style)), |
|---|
| 626 | style->visitedDependentColor(CSSPropertyColor), style->colorSpace()); |
|---|
| 627 | context->restore(); |
|---|
| 628 | |
|---|
| 629 | return false; |
|---|
| 630 | } |
|---|
| 631 | |
|---|
| 632 | bool RenderThemeGtk::paintMediaVolumeSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 633 | { |
|---|
| 634 | return paintMediaSliderThumb(renderObject, paintInfo, rect); |
|---|
| 635 | } |
|---|
| 636 | |
|---|
| 637 | String RenderThemeGtk::formatMediaControlsCurrentTime(float currentTime, float duration) const |
|---|
| 638 | { |
|---|
| 639 | return formatMediaControlsTime(currentTime) + " / " + formatMediaControlsTime(duration); |
|---|
| 640 | } |
|---|
| 641 | |
|---|
| 642 | bool RenderThemeGtk::paintMediaCurrentTime(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
|---|
| 643 | { |
|---|
| 644 | return false; |
|---|
| 645 | } |
|---|
| 646 | #endif |
|---|
| 647 | |
|---|
| 648 | #if ENABLE(PROGRESS_ELEMENT) |
|---|
| 649 | void RenderThemeGtk::adjustProgressBarStyle(StyleResolver*, RenderStyle* style, Element*) const |
|---|
| 650 | { |
|---|
| 651 | style->setBoxShadow(nullptr); |
|---|
| 652 | } |
|---|
| 653 | |
|---|
| 654 | // These values have been copied from RenderThemeChromiumSkia.cpp |
|---|
| 655 | static const int progressActivityBlocks = 5; |
|---|
| 656 | static const int progressAnimationFrames = 10; |
|---|
| 657 | static const double progressAnimationInterval = 0.125; |
|---|
| 658 | double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const |
|---|
| 659 | { |
|---|
| 660 | return progressAnimationInterval; |
|---|
| 661 | } |
|---|
| 662 | |
|---|
| 663 | double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const |
|---|
| 664 | { |
|---|
| 665 | return progressAnimationInterval * progressAnimationFrames * 2; // "2" for back and forth; |
|---|
| 666 | } |
|---|
| 667 | |
|---|
| 668 | IntRect RenderThemeGtk::calculateProgressRect(RenderObject* renderObject, const IntRect& fullBarRect) |
|---|
| 669 | { |
|---|
| 670 | IntRect progressRect(fullBarRect); |
|---|
| 671 | RenderProgress* renderProgress = toRenderProgress(renderObject); |
|---|
| 672 | if (renderProgress->isDeterminate()) { |
|---|
| 673 | int progressWidth = progressRect.width() * renderProgress->position(); |
|---|
| 674 | if (renderObject->style()->direction() == RTL) |
|---|
| 675 | progressRect.setX(progressRect.x() + progressRect.width() - progressWidth); |
|---|
| 676 | progressRect.setWidth(progressWidth); |
|---|
| 677 | return progressRect; |
|---|
| 678 | } |
|---|
| 679 | |
|---|
| 680 | double animationProgress = renderProgress->animationProgress(); |
|---|
| 681 | |
|---|
| 682 | // Never let the progress rect shrink smaller than 2 pixels. |
|---|
| 683 | int newWidth = max(2, progressRect.width() / progressActivityBlocks); |
|---|
| 684 | int movableWidth = progressRect.width() - newWidth; |
|---|
| 685 | progressRect.setWidth(newWidth); |
|---|
| 686 | |
|---|
| 687 | // We want the first 0.5 units of the animation progress to represent the |
|---|
| 688 | // forward motion and the second 0.5 units to represent the backward motion, |
|---|
| 689 | // thus we multiply by two here to get the full sweep of the progress bar with |
|---|
| 690 | // each direction. |
|---|
| 691 | if (animationProgress < 0.5) |
|---|
| 692 | progressRect.setX(progressRect.x() + (animationProgress * 2 * movableWidth)); |
|---|
| 693 | else |
|---|
| 694 | progressRect.setX(progressRect.x() + ((1.0 - animationProgress) * 2 * movableWidth)); |
|---|
| 695 | return progressRect; |
|---|
| 696 | } |
|---|
| 697 | #endif |
|---|
| 698 | |
|---|
| 699 | String RenderThemeGtk::fileListNameForWidth(const FileList* fileList, const Font& font, int width, bool multipleFilesAllowed) const |
|---|
| 700 | { |
|---|
| 701 | if (width <= 0) |
|---|
| 702 | return String(); |
|---|
| 703 | |
|---|
| 704 | if (fileList->length() > 1) |
|---|
| 705 | return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks); |
|---|
| 706 | |
|---|
| 707 | String string; |
|---|
| 708 | if (fileList->length()) |
|---|
| 709 | string = pathGetFileName(fileList->item(0)->path()); |
|---|
| 710 | else if (multipleFilesAllowed) |
|---|
| 711 | string = fileButtonNoFilesSelectedLabel(); |
|---|
| 712 | else |
|---|
| 713 | string = fileButtonNoFileSelectedLabel(); |
|---|
| 714 | |
|---|
| 715 | return StringTruncator::centerTruncate(string, width, font, StringTruncator::EnableRoundingHacks); |
|---|
| 716 | } |
|---|
| 717 | |
|---|
| 718 | #if ENABLE(DATALIST_ELEMENT) |
|---|
| 719 | IntSize RenderThemeGtk::sliderTickSize() const |
|---|
| 720 | { |
|---|
| 721 | // FIXME: We need to set this to the size of one tick mark. |
|---|
| 722 | return IntSize(0, 0); |
|---|
| 723 | } |
|---|
| 724 | |
|---|
| 725 | int RenderThemeGtk::sliderTickOffsetFromTrackCenter() const |
|---|
| 726 | { |
|---|
| 727 | // FIXME: We need to set this to the position of the tick marks. |
|---|
| 728 | return 0; |
|---|
| 729 | } |
|---|
| 730 | #endif |
|---|
| 731 | |
|---|
| 732 | } |
|---|