| /* |
| * Copyright (C) 2007 Apple Inc. |
| * Copyright (C) 2007 Alp Toker <alp@atoker.com> |
| * Copyright (C) 2008 Collabora Ltd. |
| * Copyright (C) 2009 Kenneth Rohde Christiansen |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "config.h" |
| #include "RenderThemeGtk.h" |
| |
| #include "AffineTransform.h" |
| #include "CSSValueKeywords.h" |
| #include "GOwnPtr.h" |
| #include "Gradient.h" |
| #include "GraphicsContext.h" |
| #include "GtkVersioning.h" |
| #include "HTMLMediaElement.h" |
| #include "HTMLNames.h" |
| #include "MediaControlElements.h" |
| #include "NotImplemented.h" |
| #include "RenderBox.h" |
| #include "RenderObject.h" |
| #include "UserAgentStyleSheets.h" |
| #include "gtkdrawing.h" |
| #include <gdk/gdk.h> |
| #include <gtk/gtk.h> |
| #include <wtf/text/CString.h> |
| |
| #if ENABLE(PROGRESS_TAG) |
| #include "RenderProgress.h" |
| #endif |
| |
| namespace WebCore { |
| |
| using namespace HTMLNames; |
| |
| #if ENABLE(VIDEO) |
| static HTMLMediaElement* getMediaElementFromRenderObject(RenderObject* o) |
| { |
| Node* node = o->node(); |
| Node* mediaNode = node ? node->shadowAncestorNode() : 0; |
| if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) |
| return 0; |
| |
| return static_cast<HTMLMediaElement*>(mediaNode); |
| } |
| |
| static gchar* getIconNameForTextDirection(const char* baseName) |
| { |
| GString* nameWithDirection = g_string_new(baseName); |
| GtkTextDirection textDirection = gtk_widget_get_default_direction(); |
| |
| if (textDirection == GTK_TEXT_DIR_RTL) |
| g_string_append(nameWithDirection, "-rtl"); |
| else if (textDirection == GTK_TEXT_DIR_LTR) |
| g_string_append(nameWithDirection, "-ltr"); |
| |
| return g_string_free(nameWithDirection, FALSE); |
| } |
| |
| void RenderThemeGtk::initMediaStyling(GtkStyle* style, bool force) |
| { |
| static bool stylingInitialized = false; |
| |
| if (!stylingInitialized || force) { |
| m_panelColor = style->bg[GTK_STATE_NORMAL]; |
| m_sliderColor = style->bg[GTK_STATE_ACTIVE]; |
| m_sliderThumbColor = style->bg[GTK_STATE_SELECTED]; |
| |
| // Names of these icons can vary because of text direction. |
| gchar* playButtonIconName = getIconNameForTextDirection("gtk-media-play"); |
| gchar* seekBackButtonIconName = getIconNameForTextDirection("gtk-media-rewind"); |
| gchar* seekForwardButtonIconName = getIconNameForTextDirection("gtk-media-forward"); |
| |
| m_fullscreenButton.clear(); |
| m_muteButton.clear(); |
| m_unmuteButton.clear(); |
| m_playButton.clear(); |
| m_pauseButton.clear(); |
| m_seekBackButton.clear(); |
| m_seekForwardButton.clear(); |
| |
| m_fullscreenButton = Image::loadPlatformThemeIcon("gtk-fullscreen", m_mediaIconSize); |
| // Note that the muteButton and unmuteButton take icons reflecting |
| // the *current* state. Hence, the unmuteButton represents the *muted* |
| // status, the muteButton represents the then current *unmuted* status. |
| m_muteButton = Image::loadPlatformThemeIcon("audio-volume-high", m_mediaIconSize); |
| m_unmuteButton = Image::loadPlatformThemeIcon("audio-volume-muted", m_mediaIconSize); |
| m_playButton = Image::loadPlatformThemeIcon(reinterpret_cast<const char*>(playButtonIconName), m_mediaIconSize); |
| m_pauseButton = Image::loadPlatformThemeIcon("gtk-media-pause", m_mediaIconSize); |
| m_seekBackButton = Image::loadPlatformThemeIcon(reinterpret_cast<const char*>(seekBackButtonIconName), m_mediaIconSize); |
| m_seekForwardButton = Image::loadPlatformThemeIcon(reinterpret_cast<const char*>(seekForwardButtonIconName), m_mediaIconSize); |
| |
| g_free(playButtonIconName); |
| g_free(seekBackButtonIconName); |
| g_free(seekForwardButtonIconName); |
| stylingInitialized = true; |
| } |
| } |
| #endif |
| |
| PassRefPtr<RenderTheme> RenderThemeGtk::create() |
| { |
| return adoptRef(new RenderThemeGtk()); |
| } |
| |
| PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page) |
| { |
| static RenderTheme* rt = RenderThemeGtk::create().releaseRef(); |
| return rt; |
| } |
| |
| static int mozGtkRefCount = 0; |
| |
| RenderThemeGtk::RenderThemeGtk() |
| : m_gtkWindow(0) |
| , m_gtkContainer(0) |
| , m_gtkButton(0) |
| , m_gtkEntry(0) |
| , m_gtkTreeView(0) |
| , m_panelColor(Color::white) |
| , m_sliderColor(Color::white) |
| , m_sliderThumbColor(Color::white) |
| , m_mediaIconSize(16) |
| , m_mediaSliderHeight(14) |
| , m_mediaSliderThumbWidth(12) |
| , m_mediaSliderThumbHeight(12) |
| , m_fullscreenButton(0) |
| , m_muteButton(0) |
| , m_unmuteButton(0) |
| , m_playButton(0) |
| , m_pauseButton(0) |
| , m_seekBackButton(0) |
| , m_seekForwardButton(0) |
| , m_partsTable(adoptGRef(g_hash_table_new_full(0, 0, 0, g_free))) |
| { |
| if (!mozGtkRefCount) { |
| moz_gtk_init(); |
| |
| // Use the theme parts for the default drawable. |
| moz_gtk_use_theme_parts(partsForDrawable(0)); |
| } |
| |
| ++mozGtkRefCount; |
| |
| #if ENABLE(VIDEO) |
| initMediaStyling(gtk_rc_get_style(GTK_WIDGET(gtkContainer())), false); |
| #endif |
| } |
| |
| RenderThemeGtk::~RenderThemeGtk() |
| { |
| --mozGtkRefCount; |
| |
| if (!mozGtkRefCount) |
| moz_gtk_shutdown(); |
| |
| m_fullscreenButton.clear(); |
| m_muteButton.clear(); |
| m_unmuteButton.clear(); |
| m_playButton.clear(); |
| m_pauseButton.clear(); |
| m_seekBackButton.clear(); |
| m_seekForwardButton.clear(); |
| |
| GList* values = g_hash_table_get_values(m_partsTable.get()); |
| for (guint i = 0; i < g_list_length(values); i++) |
| moz_gtk_destroy_theme_parts_widgets( |
| static_cast<GtkThemeParts*>(g_list_nth_data(values, i))); |
| |
| gtk_widget_destroy(m_gtkWindow); |
| } |
| |
| GtkThemeParts* RenderThemeGtk::partsForDrawable(GdkDrawable* drawable) const |
| { |
| // A null drawable represents the default screen colormap. |
| GdkColormap* colormap = 0; |
| if (!drawable) |
| colormap = gdk_screen_get_default_colormap(gdk_screen_get_default()); |
| else |
| colormap = gdk_drawable_get_colormap(drawable); |
| |
| GtkThemeParts* parts = static_cast<GtkThemeParts*>(g_hash_table_lookup(m_partsTable.get(), colormap)); |
| if (!parts) { |
| parts = g_new0(GtkThemeParts, 1); |
| parts->colormap = colormap; |
| g_hash_table_insert(m_partsTable.get(), colormap, parts); |
| } |
| |
| return parts; |
| } |
| |
| static bool supportsFocus(ControlPart appearance) |
| { |
| switch (appearance) { |
| case PushButtonPart: |
| case ButtonPart: |
| case TextFieldPart: |
| case TextAreaPart: |
| case SearchFieldPart: |
| case MenulistPart: |
| case RadioPart: |
| case CheckboxPart: |
| case SliderHorizontalPart: |
| case SliderVerticalPart: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| bool RenderThemeGtk::supportsFocusRing(const RenderStyle* style) const |
| { |
| return supportsFocus(style->appearance()); |
| } |
| |
| bool RenderThemeGtk::controlSupportsTints(const RenderObject* o) const |
| { |
| return isEnabled(o); |
| } |
| |
| int RenderThemeGtk::baselinePosition(const RenderObject* o) const |
| { |
| if (!o->isBox()) |
| return 0; |
| |
| // FIXME: This strategy is possibly incorrect for the GTK+ port. |
| if (o->style()->appearance() == CheckboxPart |
| || o->style()->appearance() == RadioPart) { |
| const RenderBox* box = toRenderBox(o); |
| return box->marginTop() + box->height() - 2; |
| } |
| |
| return RenderTheme::baselinePosition(o); |
| } |
| |
| static GtkTextDirection gtkTextDirection(TextDirection direction) |
| { |
| switch (direction) { |
| case RTL: |
| return GTK_TEXT_DIR_RTL; |
| case LTR: |
| return GTK_TEXT_DIR_LTR; |
| default: |
| return GTK_TEXT_DIR_NONE; |
| } |
| } |
| |
| static void adjustMozillaStyle(const RenderThemeGtk* theme, RenderStyle* style, GtkThemeWidgetType type) |
| { |
| gint left, top, right, bottom; |
| GtkTextDirection direction = gtkTextDirection(style->direction()); |
| gboolean inhtml = true; |
| |
| if (moz_gtk_get_widget_border(type, &left, &top, &right, &bottom, direction, inhtml) != MOZ_GTK_SUCCESS) |
| return; |
| |
| // FIXME: This approach is likely to be incorrect. See other ports and layout tests to see the problem. |
| const int xpadding = 1; |
| const int ypadding = 1; |
| |
| style->setPaddingLeft(Length(xpadding + left, Fixed)); |
| style->setPaddingTop(Length(ypadding + top, Fixed)); |
| style->setPaddingRight(Length(xpadding + right, Fixed)); |
| style->setPaddingBottom(Length(ypadding + bottom, Fixed)); |
| } |
| |
| static void setMozillaState(const RenderTheme* theme, GtkThemeWidgetType type, RenderObject* o, GtkWidgetState* state) |
| { |
| state->active = theme->isPressed(o); |
| state->focused = theme->isFocused(o); |
| state->inHover = theme->isHovered(o); |
| // FIXME: Disabled does not always give the correct appearance for ReadOnly |
| state->disabled = !theme->isEnabled(o) || theme->isReadOnlyControl(o); |
| state->isDefault = false; |
| state->canDefault = false; |
| |
| |
| // FIXME: The depressed value should probably apply for other theme parts too. |
| // It must be used for range thumbs, because otherwise when the thumb is pressed, |
| // the rendering is incorrect. |
| if (type == MOZ_GTK_SCALE_THUMB_HORIZONTAL || type == MOZ_GTK_SCALE_THUMB_VERTICAL) |
| state->depressed = theme->isPressed(o); |
| else |
| state->depressed = false; |
| } |
| |
| static bool paintMozillaGtkWidget(const RenderThemeGtk* theme, GtkThemeWidgetType type, RenderObject* o, const PaintInfo& i, const IntRect& rect) |
| { |
| // Painting is disabled so just claim to have succeeded |
| if (i.context->paintingDisabled()) |
| return false; |
| |
| GtkWidgetState widgetState; |
| setMozillaState(theme, type, o, &widgetState); |
| |
| // We might want to make setting flags the caller's job at some point rather than doing it here. |
| int flags = 0; |
| if (type == MOZ_GTK_BUTTON) |
| flags = GTK_RELIEF_NORMAL; |
| else if (type == MOZ_GTK_CHECKBUTTON || type == MOZ_GTK_RADIOBUTTON) |
| flags = theme->isChecked(o); |
| |
| GRefPtr<GdkDrawable> drawable(i.context->gdkDrawable()); |
| GdkRectangle paintRect, clipRect; |
| if (drawable) { |
| AffineTransform ctm = i.context->getCTM(); |
| IntPoint pos = ctm.mapPoint(rect.location()); |
| paintRect = IntRect(pos.x(), pos.y(), rect.width(), rect.height()); |
| |
| // Intersect the cairo rectangle with the target widget region. This will |
| // prevent the theme drawing code from drawing into regions that cairo will |
| // clip anyway. |
| cairo_t* cr = i.context->platformContext(); |
| double clipX1, clipX2, clipY1, clipY2; |
| cairo_clip_extents(cr, &clipX1, &clipY1, &clipX2, &clipY2); |
| IntPoint clipPos = ctm.mapPoint(IntPoint(clipX1, clipY1)); |
| |
| clipRect.width = clipX2 - clipX1; |
| clipRect.height = clipY2 - clipY1; |
| clipRect.x = clipPos.x(); |
| clipRect.y = clipPos.y(); |
| gdk_rectangle_intersect(&paintRect, &clipRect, &clipRect); |
| |
| } else { |
| // In some situations, like during print previews, this GraphicsContext is not |
| // backed by a GdkDrawable. In those situations, we render onto a pixmap and then |
| // copy the rendered data back to the GraphicsContext via Cairo. |
| drawable = adoptGRef(gdk_pixmap_new(0, rect.width(), rect.height(), gdk_visual_get_depth(gdk_visual_get_system()))); |
| paintRect = clipRect = IntRect(0, 0, rect.width(), rect.height()); |
| } |
| |
| moz_gtk_use_theme_parts(theme->partsForDrawable(drawable.get())); |
| bool success = moz_gtk_widget_paint(type, drawable.get(), &paintRect, &clipRect, &widgetState, flags, gtkTextDirection(o->style()->direction())) == MOZ_GTK_SUCCESS; |
| |
| // If the drawing was successful and we rendered onto a pixmap, copy the |
| // results back to the original GraphicsContext. |
| if (success && !i.context->gdkDrawable()) { |
| cairo_t* cairoContext = i.context->platformContext(); |
| cairo_save(cairoContext); |
| gdk_cairo_set_source_pixmap(cairoContext, drawable.get(), rect.x(), rect.y()); |
| cairo_paint(cairoContext); |
| cairo_restore(cairoContext); |
| } |
| |
| return !success; |
| } |
| |
| static void setButtonPadding(RenderStyle* style) |
| { |
| // FIXME: This looks incorrect. |
| const int padding = 8; |
| style->setPaddingLeft(Length(padding, Fixed)); |
| style->setPaddingRight(Length(padding, Fixed)); |
| style->setPaddingTop(Length(padding / 2, Fixed)); |
| style->setPaddingBottom(Length(padding / 2, Fixed)); |
| } |
| |
| static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, ControlPart appearance) |
| { |
| // The width and height are both specified, so we shouldn't change them. |
| if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) |
| return; |
| |
| // FIXME: This is probably not correct use of indicatorSize and indicatorSpacing. |
| gint indicatorSize, indicatorSpacing; |
| |
| switch (appearance) { |
| case CheckboxPart: |
| if (moz_gtk_checkbox_get_metrics(&indicatorSize, &indicatorSpacing) != MOZ_GTK_SUCCESS) |
| return; |
| break; |
| case RadioPart: |
| if (moz_gtk_radio_get_metrics(&indicatorSize, &indicatorSpacing) != MOZ_GTK_SUCCESS) |
| return; |
| break; |
| default: |
| return; |
| } |
| |
| // Other ports hard-code this to 13, but GTK+ users tend to demand the native look. |
| // It could be made a configuration option values other than 13 actually break site compatibility. |
| int length = indicatorSize + indicatorSpacing; |
| if (style->width().isIntrinsicOrAuto()) |
| style->setWidth(Length(length, Fixed)); |
| |
| if (style->height().isAuto()) |
| style->setHeight(Length(length, Fixed)); |
| } |
| |
| void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const |
| { |
| setToggleSize(this, style, RadioPart); |
| } |
| |
| bool RenderThemeGtk::paintCheckbox(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
| { |
| return paintMozillaGtkWidget(this, MOZ_GTK_CHECKBUTTON, o, i, rect); |
| } |
| |
| void RenderThemeGtk::setRadioSize(RenderStyle* style) const |
| { |
| setToggleSize(this, style, RadioPart); |
| } |
| |
| bool RenderThemeGtk::paintRadio(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
| { |
| return paintMozillaGtkWidget(this, MOZ_GTK_RADIOBUTTON, o, i, rect); |
| } |
| |
| void RenderThemeGtk::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const |
| { |
| // FIXME: Is this condition necessary? |
| if (style->appearance() == PushButtonPart) { |
| style->resetBorder(); |
| style->setHeight(Length(Auto)); |
| style->setWhiteSpace(PRE); |
| setButtonPadding(style); |
| } else { |
| // FIXME: This should not be hard-coded. |
| style->setMinHeight(Length(14, Fixed)); |
| style->resetBorderTop(); |
| style->resetBorderBottom(); |
| } |
| } |
| |
| bool RenderThemeGtk::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
| { |
| return paintMozillaGtkWidget(this, MOZ_GTK_BUTTON, o, i, rect); |
| } |
| |
| void RenderThemeGtk::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const |
| { |
| style->resetBorder(); |
| style->resetPadding(); |
| style->setHeight(Length(Auto)); |
| style->setWhiteSpace(PRE); |
| adjustMozillaStyle(this, style, MOZ_GTK_DROPDOWN); |
| } |
| |
| bool RenderThemeGtk::paintMenuList(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
| { |
| return paintMozillaGtkWidget(this, MOZ_GTK_DROPDOWN, o, i, rect); |
| } |
| |
| void RenderThemeGtk::adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
| { |
| style->resetBorder(); |
| style->resetPadding(); |
| style->setHeight(Length(Auto)); |
| style->setWhiteSpace(PRE); |
| adjustMozillaStyle(this, style, MOZ_GTK_ENTRY); |
| } |
| |
| bool RenderThemeGtk::paintTextField(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
| { |
| return paintMozillaGtkWidget(this, MOZ_GTK_ENTRY, o, i, rect); |
| } |
| |
| bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const IntRect& r) |
| { |
| return paintTextField(o, i, r); |
| } |
| |
| void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
| { |
| adjustSearchFieldCancelButtonStyle(selector, style, e); |
| } |
| |
| bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
| { |
| return paintSearchFieldResultsDecoration(o, i, rect); |
| } |
| |
| void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
| { |
| style->resetBorder(); |
| style->resetPadding(); |
| |
| // FIXME: This should not be hard-coded. |
| IntSize size = IntSize(14, 14); |
| style->setWidth(Length(size.width(), Fixed)); |
| style->setHeight(Length(size.height(), Fixed)); |
| } |
| |
| bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
| { |
| GraphicsContext* context = i.context; |
| |
| static Image* searchImage = Image::loadPlatformThemeIcon(GTK_STOCK_FIND, rect.width()).releaseRef(); |
| context->drawImage(searchImage, DeviceColorSpace, rect); |
| |
| return false; |
| } |
| |
| void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
| { |
| style->resetBorder(); |
| style->resetPadding(); |
| |
| // FIXME: This should not be hard-coded. |
| IntSize size = IntSize(14, 14); |
| style->setWidth(Length(size.width(), Fixed)); |
| style->setHeight(Length(size.height(), Fixed)); |
| } |
| |
| bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
| { |
| GraphicsContext* context = i.context; |
| |
| // TODO: Brightening up the image on hover is desirable here, I believe. |
| static Image* cancelImage = Image::loadPlatformThemeIcon(GTK_STOCK_CLEAR, rect.width()).releaseRef(); |
| context->drawImage(cancelImage, DeviceColorSpace, rect); |
| |
| return false; |
| } |
| |
| void RenderThemeGtk::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const |
| { |
| adjustTextFieldStyle(selector, style, e); |
| } |
| |
| bool RenderThemeGtk::paintSearchField(RenderObject* o, const PaintInfo& i, const IntRect& rect) |
| { |
| return paintTextField(o, i, rect); |
| } |
| |
| bool RenderThemeGtk::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect) |
| { |
| ControlPart part = object->style()->appearance(); |
| ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart); |
| |
| GtkThemeWidgetType gtkPart = MOZ_GTK_SCALE_HORIZONTAL; |
| if (part == SliderVerticalPart) |
| gtkPart = MOZ_GTK_SCALE_VERTICAL; |
| |
| return paintMozillaGtkWidget(this, gtkPart, object, info, rect); |
| } |
| |
| void RenderThemeGtk::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const |
| { |
| style->setBoxShadow(0); |
| } |
| |
| bool RenderThemeGtk::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect) |
| { |
| ControlPart part = object->style()->appearance(); |
| ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart); |
| |
| GtkThemeWidgetType gtkPart = MOZ_GTK_SCALE_THUMB_HORIZONTAL; |
| if (part == SliderThumbVerticalPart) |
| gtkPart = MOZ_GTK_SCALE_THUMB_VERTICAL; |
| |
| return paintMozillaGtkWidget(this, gtkPart, object, info, rect); |
| } |
| |
| void RenderThemeGtk::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle* style, Element*) const |
| { |
| style->setBoxShadow(0); |
| } |
| |
| void RenderThemeGtk::adjustSliderThumbSize(RenderObject* o) const |
| { |
| ControlPart part = o->style()->appearance(); |
| #if ENABLE(VIDEO) |
| if (part == MediaSliderThumbPart) { |
| o->style()->setWidth(Length(m_mediaSliderThumbWidth, Fixed)); |
| o->style()->setHeight(Length(m_mediaSliderThumbHeight, Fixed)); |
| } else |
| #endif |
| if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) { |
| gint width, height; |
| moz_gtk_get_scalethumb_metrics(part == SliderThumbHorizontalPart ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, &width, &height); |
| o->style()->setWidth(Length(width, Fixed)); |
| o->style()->setHeight(Length(height, Fixed)); |
| } |
| } |
| |
| Color RenderThemeGtk::platformActiveSelectionBackgroundColor() const |
| { |
| GtkWidget* widget = gtkEntry(); |
| return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED]; |
| } |
| |
| Color RenderThemeGtk::platformInactiveSelectionBackgroundColor() const |
| { |
| GtkWidget* widget = gtkEntry(); |
| return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE]; |
| } |
| |
| Color RenderThemeGtk::platformActiveSelectionForegroundColor() const |
| { |
| GtkWidget* widget = gtkEntry(); |
| return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED]; |
| } |
| |
| Color RenderThemeGtk::platformInactiveSelectionForegroundColor() const |
| { |
| GtkWidget* widget = gtkEntry(); |
| return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE]; |
| } |
| |
| Color RenderThemeGtk::activeListBoxSelectionBackgroundColor() const |
| { |
| GtkWidget* widget = gtkTreeView(); |
| return gtk_widget_get_style(widget)->base[GTK_STATE_SELECTED]; |
| } |
| |
| Color RenderThemeGtk::inactiveListBoxSelectionBackgroundColor() const |
| { |
| GtkWidget* widget = gtkTreeView(); |
| return gtk_widget_get_style(widget)->base[GTK_STATE_ACTIVE]; |
| } |
| |
| Color RenderThemeGtk::activeListBoxSelectionForegroundColor() const |
| { |
| GtkWidget* widget = gtkTreeView(); |
| return gtk_widget_get_style(widget)->text[GTK_STATE_SELECTED]; |
| } |
| |
| Color RenderThemeGtk::inactiveListBoxSelectionForegroundColor() const |
| { |
| GtkWidget* widget = gtkTreeView(); |
| return gtk_widget_get_style(widget)->text[GTK_STATE_ACTIVE]; |
| } |
| |
| double RenderThemeGtk::caretBlinkInterval() const |
| { |
| GtkSettings* settings = gtk_settings_get_default(); |
| |
| gboolean shouldBlink; |
| gint time; |
| |
| g_object_get(settings, "gtk-cursor-blink", &shouldBlink, "gtk-cursor-blink-time", &time, NULL); |
| |
| if (!shouldBlink) |
| return 0; |
| |
| return time / 2000.; |
| } |
| |
| void RenderThemeGtk::systemFont(int, FontDescription&) const |
| { |
| // If you remove this notImplemented(), replace it with an comment that explains why. |
| notImplemented(); |
| } |
| |
| Color RenderThemeGtk::systemColor(int cssValueId) const |
| { |
| switch (cssValueId) { |
| case CSSValueButtontext: |
| return Color(gtk_widget_get_style(gtkButton())->fg[GTK_STATE_NORMAL]); |
| case CSSValueCaptiontext: |
| return Color(gtk_widget_get_style(gtkEntry())->fg[GTK_STATE_NORMAL]); |
| default: |
| return RenderTheme::systemColor(cssValueId); |
| } |
| } |
| |
| static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, RenderTheme* renderTheme) |
| { |
| // FIXME: Make sure this function doesn't get called many times for a single GTK+ style change signal. |
| renderTheme->platformColorsDidChange(); |
| } |
| |
| GtkContainer* RenderThemeGtk::gtkContainer() const |
| { |
| if (m_gtkContainer) |
| return m_gtkContainer; |
| |
| m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP); |
| m_gtkContainer = GTK_CONTAINER(gtk_fixed_new()); |
| g_signal_connect(m_gtkWindow, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this)); |
| gtk_container_add(GTK_CONTAINER(m_gtkWindow), GTK_WIDGET(m_gtkContainer)); |
| gtk_widget_realize(m_gtkWindow); |
| |
| return m_gtkContainer; |
| } |
| |
| GtkWidget* RenderThemeGtk::gtkButton() const |
| { |
| if (m_gtkButton) |
| return m_gtkButton; |
| |
| m_gtkButton = gtk_button_new(); |
| g_signal_connect(m_gtkButton, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this)); |
| gtk_container_add(gtkContainer(), m_gtkButton); |
| gtk_widget_realize(m_gtkButton); |
| |
| return m_gtkButton; |
| } |
| |
| GtkWidget* RenderThemeGtk::gtkEntry() const |
| { |
| if (m_gtkEntry) |
| return m_gtkEntry; |
| |
| m_gtkEntry = gtk_entry_new(); |
| g_signal_connect(m_gtkEntry, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this)); |
| gtk_container_add(gtkContainer(), m_gtkEntry); |
| gtk_widget_realize(m_gtkEntry); |
| |
| return m_gtkEntry; |
| } |
| |
| GtkWidget* RenderThemeGtk::gtkTreeView() const |
| { |
| if (m_gtkTreeView) |
| return m_gtkTreeView; |
| |
| m_gtkTreeView = gtk_tree_view_new(); |
| g_signal_connect(m_gtkTreeView, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this)); |
| gtk_container_add(gtkContainer(), m_gtkTreeView); |
| gtk_widget_realize(m_gtkTreeView); |
| |
| return m_gtkTreeView; |
| } |
| |
| void RenderThemeGtk::platformColorsDidChange() |
| { |
| #if ENABLE(VIDEO) |
| initMediaStyling(gtk_rc_get_style(GTK_WIDGET(gtkContainer())), true); |
| #endif |
| RenderTheme::platformColorsDidChange(); |
| } |
| |
| #if ENABLE(VIDEO) |
| String RenderThemeGtk::extraMediaControlsStyleSheet() |
| { |
| return String(mediaControlsGtkUserAgentStyleSheet, sizeof(mediaControlsGtkUserAgentStyleSheet)); |
| } |
| |
| static inline bool paintMediaButton(GraphicsContext* context, const IntRect& r, Image* image, Color panelColor, int mediaIconSize) |
| { |
| context->fillRect(FloatRect(r), panelColor, DeviceColorSpace); |
| context->drawImage(image, DeviceColorSpace, |
| IntRect(r.x() + (r.width() - mediaIconSize) / 2, |
| r.y() + (r.height() - mediaIconSize) / 2, |
| mediaIconSize, mediaIconSize)); |
| |
| return false; |
| } |
| |
| bool RenderThemeGtk::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) |
| { |
| return paintMediaButton(paintInfo.context, r, m_fullscreenButton.get(), m_panelColor, m_mediaIconSize); |
| } |
| |
| bool RenderThemeGtk::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) |
| { |
| HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o); |
| if (!mediaElement) |
| return false; |
| |
| return paintMediaButton(paintInfo.context, r, mediaElement->muted() ? m_unmuteButton.get() : m_muteButton.get(), m_panelColor, m_mediaIconSize); |
| } |
| |
| bool RenderThemeGtk::paintMediaPlayButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) |
| { |
| Node* node = o->node(); |
| if (!node) |
| return false; |
| |
| MediaControlPlayButtonElement* button = static_cast<MediaControlPlayButtonElement*>(node); |
| return paintMediaButton(paintInfo.context, r, button->displayType() == MediaPlayButton ? m_playButton.get() : m_pauseButton.get(), m_panelColor, m_mediaIconSize); |
| } |
| |
| bool RenderThemeGtk::paintMediaSeekBackButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) |
| { |
| return paintMediaButton(paintInfo.context, r, m_seekBackButton.get(), m_panelColor, m_mediaIconSize); |
| } |
| |
| bool RenderThemeGtk::paintMediaSeekForwardButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) |
| { |
| return paintMediaButton(paintInfo.context, r, m_seekForwardButton.get(), m_panelColor, m_mediaIconSize); |
| } |
| |
| bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) |
| { |
| GraphicsContext* context = paintInfo.context; |
| |
| context->fillRect(FloatRect(r), m_panelColor, DeviceColorSpace); |
| context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2, |
| r.width(), m_mediaSliderHeight)), m_sliderColor, DeviceColorSpace); |
| |
| RenderStyle* style = o->style(); |
| HTMLMediaElement* mediaElement = toParentMediaElement(o); |
| |
| if (!mediaElement) |
| return false; |
| |
| // Draw the buffered ranges. This code is highly inspired from |
| // Chrome. |
| // FIXME: Draw multiple ranges if there are multiple buffered |
| // ranges. The current implementation of the player is always |
| // buffering a single range anyway. |
| IntRect bufferedRect = r; |
| bufferedRect.inflate(-style->borderLeftWidth()); |
| bufferedRect.setWidth((bufferedRect.width() * mediaElement->percentLoaded())); |
| |
| // Don't bother drawing an empty area. |
| if (bufferedRect.isEmpty()) |
| return false; |
| |
| IntPoint sliderTopLeft = bufferedRect.location(); |
| IntPoint sliderTopRight = sliderTopLeft; |
| sliderTopRight.move(0, bufferedRect.height()); |
| |
| RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight); |
| Color startColor = m_panelColor; |
| gradient->addColorStop(0.0, startColor); |
| gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha())); |
| |
| context->save(); |
| context->setStrokeStyle(NoStroke); |
| context->setFillGradient(gradient); |
| context->fillRect(bufferedRect); |
| context->restore(); |
| |
| return false; |
| } |
| |
| bool RenderThemeGtk::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) |
| { |
| // Make the thumb nicer with rounded corners. |
| paintInfo.context->fillRoundedRect(r, IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), m_sliderThumbColor, DeviceColorSpace); |
| return false; |
| } |
| #endif |
| |
| #if ENABLE(PROGRESS_TAG) |
| double RenderThemeGtk::animationRepeatIntervalForProgressBar(RenderProgress*) const |
| { |
| // FIXME: It doesn't look like there is a good way yet to support animated |
| // progress bars with the Mozilla theme drawing code. |
| return 0; |
| } |
| |
| double RenderThemeGtk::animationDurationForProgressBar(RenderProgress*) const |
| { |
| // FIXME: It doesn't look like there is a good way yet to support animated |
| // progress bars with the Mozilla theme drawing code. |
| return 0; |
| } |
| |
| void RenderThemeGtk::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const |
| { |
| style->setBoxShadow(0); |
| } |
| |
| bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) |
| { |
| if (!renderObject->isProgress()) |
| return true; |
| |
| GtkWidget* progressBarWidget = moz_gtk_get_progress_widget(); |
| if (!progressBarWidget) |
| return true; |
| |
| if (paintMozillaGtkWidget(this, MOZ_GTK_PROGRESSBAR, renderObject, paintInfo, rect)) |
| return true; |
| |
| IntRect chunkRect(rect); |
| RenderProgress* renderProgress = toRenderProgress(renderObject); |
| |
| GtkStyle* style = gtk_widget_get_style(progressBarWidget); |
| chunkRect.setHeight(chunkRect.height() - (2 * style->ythickness)); |
| chunkRect.setY(chunkRect.y() + style->ythickness); |
| chunkRect.setWidth((chunkRect.width() - (2 * style->xthickness)) * renderProgress->position()); |
| if (renderObject->style()->direction() == RTL) |
| chunkRect.setX(rect.x() + rect.width() - chunkRect.width() - style->xthickness); |
| else |
| chunkRect.setX(chunkRect.x() + style->xthickness); |
| |
| return paintMozillaGtkWidget(this, MOZ_GTK_PROGRESS_CHUNK, renderObject, paintInfo, chunkRect); |
| } |
| #endif |
| |
| } |