summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/platform/gtk/RenderThemeGtk2.cpp')
-rw-r--r--Source/WebCore/platform/gtk/RenderThemeGtk2.cpp517
1 files changed, 517 insertions, 0 deletions
diff --git a/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp b/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp
new file mode 100644
index 0000000..fd391b7
--- /dev/null
+++ b/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) 2007 Apple Inc.
+ * Copyright (C) 2007 Alp Toker <alp@atoker.com>
+ * Copyright (C) 2008 Collabora Ltd.
+ * Copyright (C) 2009 Kenneth Rohde Christiansen
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * 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"
+
+#ifdef GTK_API_VERSION_2
+
+#include "CSSValueKeywords.h"
+#include "GraphicsContext.h"
+#include "GtkVersioning.h"
+#include "HTMLNames.h"
+#include "MediaControlElements.h"
+#include "RenderObject.h"
+#include "TextDirection.h"
+#include "UserAgentStyleSheets.h"
+#include "WidgetRenderingContext.h"
+#include "gtkdrawing.h"
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#if ENABLE(PROGRESS_TAG)
+#include "RenderProgress.h"
+#endif
+
+namespace WebCore {
+
+// This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h.
+extern GtkTextDirection gtkTextDirection(TextDirection);
+
+#if ENABLE(VIDEO)
+void RenderThemeGtk::initMediaColors()
+{
+ GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(gtkContainer()));
+ m_panelColor = style->bg[GTK_STATE_NORMAL];
+ m_sliderColor = style->bg[GTK_STATE_ACTIVE];
+ m_sliderThumbColor = style->bg[GTK_STATE_SELECTED];
+}
+#endif
+
+void RenderThemeGtk::adjustRepaintRect(const RenderObject*, IntRect&)
+{
+}
+
+GtkStateType RenderThemeGtk::getGtkStateType(RenderObject* object)
+{
+ if (!isEnabled(object) || isReadOnlyControl(object))
+ return GTK_STATE_INSENSITIVE;
+ if (isPressed(object))
+ return GTK_STATE_ACTIVE;
+ if (isHovered(object))
+ return GTK_STATE_PRELIGHT;
+ return GTK_STATE_NORMAL;
+}
+
+bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, int flags)
+{
+ // Painting is disabled so just claim to have succeeded
+ if (context->paintingDisabled())
+ return false;
+
+ GtkWidgetState widgetState;
+ widgetState.active = isPressed(renderObject);
+ widgetState.focused = isFocused(renderObject);
+
+ // https://bugs.webkit.org/show_bug.cgi?id=18364
+ // The Mozilla theme drawing code, only paints a button as pressed when it's pressed
+ // while hovered. Until we move away from the Mozila code, work-around the issue by
+ // forcing a pressed button into the hovered state. This ensures that buttons activated
+ // via the keyboard have the proper rendering.
+ widgetState.inHover = isHovered(renderObject) || (type == MOZ_GTK_BUTTON && isPressed(renderObject));
+
+ // FIXME: Disabled does not always give the correct appearance for ReadOnly
+ widgetState.disabled = !isEnabled(renderObject) || isReadOnlyControl(renderObject);
+ widgetState.isDefault = false;
+ widgetState.canDefault = false;
+ widgetState.depressed = false;
+
+ WidgetRenderingContext widgetContext(context, rect);
+ return !widgetContext.paintMozillaWidget(type, &widgetState, flags,
+ gtkTextDirection(renderObject->style()->direction()));
+}
+
+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;
+ theme->getIndicatorMetrics(appearance, indicatorSize, indicatorSpacing);
+
+ // 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* object, const PaintInfo& info, const IntRect& rect)
+{
+ return paintRenderObject(MOZ_GTK_CHECKBUTTON, object, info.context, rect, isChecked(object));
+}
+
+void RenderThemeGtk::setRadioSize(RenderStyle* style) const
+{
+ setToggleSize(this, style, RadioPart);
+}
+
+bool RenderThemeGtk::paintRadio(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+{
+ return paintRenderObject(MOZ_GTK_RADIOBUTTON, object, info.context, rect, isChecked(object));
+}
+
+bool RenderThemeGtk::paintButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+{
+ if (info.context->paintingDisabled())
+ return false;
+
+ GtkWidget* widget = gtkButton();
+ IntRect buttonRect(IntPoint(), rect.size());
+ IntRect focusRect(buttonRect);
+
+ GtkStateType state = getGtkStateType(object);
+ gtk_widget_set_state(widget, state);
+ gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
+
+ if (isFocused(object)) {
+ if (isEnabled(object)) {
+#if !GTK_CHECK_VERSION(2, 22, 0)
+ GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS);
+#endif
+ g_object_set(widget, "has-focus", TRUE, NULL);
+ }
+
+ gboolean interiorFocus = 0, focusWidth = 0, focusPadding = 0;
+ gtk_widget_style_get(widget,
+ "interior-focus", &interiorFocus,
+ "focus-line-width", &focusWidth,
+ "focus-padding", &focusPadding, NULL);
+ // If we are using exterior focus, we shrink the button rect down before
+ // drawing. If we are using interior focus we shrink the focus rect. This
+ // approach originates from the Mozilla theme drawing code (gtk2drawing.c).
+ if (interiorFocus) {
+ GtkStyle* style = gtk_widget_get_style(widget);
+ focusRect.inflateX(-style->xthickness - focusPadding);
+ focusRect.inflateY(-style->ythickness - focusPadding);
+ } else {
+ buttonRect.inflateX(-focusWidth - focusPadding);
+ buttonRect.inflateY(-focusPadding - focusPadding);
+ }
+ }
+
+ WidgetRenderingContext widgetContext(info.context, rect);
+ GtkShadowType shadowType = state == GTK_STATE_ACTIVE ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
+ widgetContext.gtkPaintBox(buttonRect, widget, state, shadowType, "button");
+ if (isFocused(object))
+ widgetContext.gtkPaintFocus(focusRect, widget, state, "button");
+
+#if !GTK_CHECK_VERSION(2, 22, 0)
+ GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS);
+#endif
+ g_object_set(widget, "has-focus", FALSE, NULL);
+ return false;
+}
+
+static void getComboBoxPadding(RenderStyle* style, int& left, int& top, int& right, int& bottom)
+{
+ // If this menu list button isn't drawn using the native theme, we
+ // don't add any extra padding beyond what WebCore already uses.
+ if (style->appearance() == NoControlPart)
+ return;
+ moz_gtk_get_widget_border(MOZ_GTK_DROPDOWN, &left, &top, &right, &bottom,
+ gtkTextDirection(style->direction()), TRUE);
+}
+
+int RenderThemeGtk::popupInternalPaddingLeft(RenderStyle* style) const
+{
+ int left = 0, top = 0, right = 0, bottom = 0;
+ getComboBoxPadding(style, left, top, right, bottom);
+ return left;
+}
+
+int RenderThemeGtk::popupInternalPaddingRight(RenderStyle* style) const
+{
+ int left = 0, top = 0, right = 0, bottom = 0;
+ getComboBoxPadding(style, left, top, right, bottom);
+ return right;
+}
+
+int RenderThemeGtk::popupInternalPaddingTop(RenderStyle* style) const
+{
+ int left = 0, top = 0, right = 0, bottom = 0;
+ getComboBoxPadding(style, left, top, right, bottom);
+ return top;
+}
+
+int RenderThemeGtk::popupInternalPaddingBottom(RenderStyle* style) const
+{
+ int left = 0, top = 0, right = 0, bottom = 0;
+ getComboBoxPadding(style, left, top, right, bottom);
+ return bottom;
+}
+
+bool RenderThemeGtk::paintMenuList(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+{
+ return paintRenderObject(MOZ_GTK_DROPDOWN, object, info.context, rect);
+}
+
+bool RenderThemeGtk::paintTextField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+{
+ return paintRenderObject(MOZ_GTK_ENTRY, object, info.context, rect);
+}
+
+bool RenderThemeGtk::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+{
+ if (info.context->paintingDisabled())
+ return false;
+
+ ControlPart part = object->style()->appearance();
+ ASSERT(part == SliderHorizontalPart || part == SliderVerticalPart);
+
+ // We shrink the trough rect slightly to make room for the focus indicator.
+ IntRect troughRect(IntPoint(), rect.size()); // This is relative to rect.
+ GtkWidget* widget = 0;
+ if (part == SliderVerticalPart) {
+ widget = gtkVScale();
+ troughRect.inflateY(-gtk_widget_get_style(widget)->ythickness);
+ } else {
+ widget = gtkHScale();
+ troughRect.inflateX(-gtk_widget_get_style(widget)->xthickness);
+ }
+ gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
+
+ WidgetRenderingContext widgetContext(info.context, rect);
+ widgetContext.gtkPaintBox(troughRect, widget, GTK_STATE_ACTIVE, GTK_SHADOW_OUT, "trough");
+ if (isFocused(object))
+ widgetContext.gtkPaintFocus(IntRect(IntPoint(), rect.size()), widget, getGtkStateType(object), "trough");
+
+ return false;
+}
+
+bool RenderThemeGtk::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
+{
+ if (info.context->paintingDisabled())
+ return false;
+
+ ControlPart part = object->style()->appearance();
+ ASSERT(part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart);
+
+ GtkWidget* widget = 0;
+ const char* detail = 0;
+ GtkOrientation orientation;
+ if (part == SliderThumbVerticalPart) {
+ widget = gtkVScale();
+ detail = "vscale";
+ orientation = GTK_ORIENTATION_VERTICAL;
+ } else {
+ widget = gtkHScale();
+ detail = "hscale";
+ orientation = GTK_ORIENTATION_HORIZONTAL;
+ }
+ gtk_widget_set_direction(widget, gtkTextDirection(object->style()->direction()));
+
+ // Only some themes have slider thumbs respond to clicks and some don't. This information is
+ // gathered via the 'activate-slider' property, but it's deprecated in GTK+ 2.22 and removed in
+ // GTK+ 3.x. The drawback of not honoring it is that slider thumbs change color when you click
+ // on them.
+ IntRect thumbRect(IntPoint(), rect.size());
+ WidgetRenderingContext widgetContext(info.context, rect);
+ widgetContext.gtkPaintSlider(thumbRect, widget, getGtkStateType(object), GTK_SHADOW_OUT, detail, orientation);
+ return false;
+}
+
+void RenderThemeGtk::adjustSliderThumbSize(RenderObject* o) const
+{
+ ControlPart part = o->style()->appearance();
+#if ENABLE(VIDEO)
+ if (part == MediaSliderThumbPart || part == MediaVolumeSliderThumbPart) {
+ adjustMediaSliderThumbSize(o);
+ return;
+ }
+#endif
+
+ GtkWidget* widget = part == SliderThumbHorizontalPart ? gtkHScale() : gtkVScale();
+ int length = 0, width = 0;
+ gtk_widget_style_get(widget,
+ "slider_length", &length,
+ "slider_width", &width,
+ NULL);
+
+ if (part == SliderThumbHorizontalPart) {
+ o->style()->setWidth(Length(length, Fixed));
+ o->style()->setHeight(Length(width, Fixed));
+ return;
+ }
+ ASSERT(part == SliderThumbVerticalPart);
+ o->style()->setWidth(Length(width, Fixed));
+ o->style()->setHeight(Length(length, Fixed));
+}
+
+#if ENABLE(PROGRESS_TAG)
+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 (paintRenderObject(MOZ_GTK_PROGRESSBAR, renderObject, paintInfo.context, 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 paintRenderObject(MOZ_GTK_PROGRESS_CHUNK, renderObject, paintInfo.context, chunkRect);
+}
+#endif
+
+GRefPtr<GdkPixbuf> RenderThemeGtk::getStockIcon(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize)
+{
+ ASSERT(widgetType == GTK_TYPE_CONTAINER || widgetType == GTK_TYPE_ENTRY);
+ GtkWidget* widget = widgetType == GTK_TYPE_CONTAINER ? GTK_WIDGET(gtkContainer()) : gtkEntry();
+ GtkStyle* style = gtk_widget_get_style(widget);
+ GtkIconSet* iconSet = gtk_style_lookup_icon_set(style, iconName);
+ return adoptGRef(gtk_icon_set_render_icon(iconSet, style,
+ static_cast<GtkTextDirection>(direction),
+ static_cast<GtkStateType>(state),
+ static_cast<GtkIconSize>(iconSize), 0, 0));
+}
+
+
+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];
+}
+
+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();
+}
+
+void RenderThemeGtk::setupWidgetAndAddToContainer(GtkWidget* widget, GtkWidget* window) const
+{
+ gtk_container_add(GTK_CONTAINER(window), widget);
+ gtk_widget_realize(widget);
+ g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE));
+
+ // FIXME: Perhaps this should only be called for the containing window or parent container.
+ g_signal_connect(widget, "style-set", G_CALLBACK(gtkStyleSetCallback), const_cast<RenderThemeGtk*>(this));
+}
+
+GtkWidget* RenderThemeGtk::gtkContainer() const
+{
+ if (m_gtkContainer)
+ return m_gtkContainer;
+
+ m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP);
+ gtk_widget_set_colormap(m_gtkWindow, m_themeParts.colormap);
+ gtk_widget_realize(m_gtkWindow);
+ gtk_widget_set_name(m_gtkWindow, "MozillaGtkWidget");
+
+ m_gtkContainer = gtk_fixed_new();
+ setupWidgetAndAddToContainer(m_gtkContainer, m_gtkWindow);
+ return m_gtkContainer;
+}
+
+GtkWidget* RenderThemeGtk::gtkButton() const
+{
+ if (m_gtkButton)
+ return m_gtkButton;
+ m_gtkButton = gtk_button_new();
+ setupWidgetAndAddToContainer(m_gtkButton, gtkContainer());
+ return m_gtkButton;
+}
+
+GtkWidget* RenderThemeGtk::gtkEntry() const
+{
+ if (m_gtkEntry)
+ return m_gtkEntry;
+ m_gtkEntry = gtk_entry_new();
+ setupWidgetAndAddToContainer(m_gtkEntry, gtkContainer());
+ return m_gtkEntry;
+}
+
+GtkWidget* RenderThemeGtk::gtkTreeView() const
+{
+ if (m_gtkTreeView)
+ return m_gtkTreeView;
+ m_gtkTreeView = gtk_tree_view_new();
+ setupWidgetAndAddToContainer(m_gtkTreeView, gtkContainer());
+ return m_gtkTreeView;
+}
+
+GtkWidget* RenderThemeGtk::gtkVScale() const
+{
+ if (m_gtkVScale)
+ return m_gtkVScale;
+ m_gtkVScale = gtk_vscale_new(0);
+ setupWidgetAndAddToContainer(m_gtkVScale, gtkContainer());
+ return m_gtkVScale;
+}
+
+GtkWidget* RenderThemeGtk::gtkHScale() const
+{
+ if (m_gtkHScale)
+ return m_gtkHScale;
+ m_gtkHScale = gtk_hscale_new(0);
+ setupWidgetAndAddToContainer(m_gtkHScale, gtkContainer());
+ return m_gtkHScale;
+}
+
+GtkWidget* RenderThemeGtk::gtkScrollbar()
+{
+ return moz_gtk_get_scrollbar_widget();
+}
+
+} // namespace WebCore
+
+#endif // GTK_API_VERSION_2