summaryrefslogtreecommitdiff
path: root/gtk
diff options
context:
space:
mode:
authorTristan Van Berkom <tristan.van.berkom@gmail.com>2010-12-05 00:36:37 +0900
committerTristan Van Berkom <tristan.van.berkom@gmail.com>2010-12-05 00:36:37 +0900
commit0d786985a368d88e8ab4e45fc3607efc5e773732 (patch)
tree5dfc8ec0e27ac3c23b9dc2eb097018971c28fd6b /gtk
parent1d3961b34258a7a8c97ca12993220554369fccb1 (diff)
parentcd76b057e9693b6919f4ccbe40ce205efc8d3caf (diff)
downloadgtk+-0d786985a368d88e8ab4e45fc3607efc5e773732.tar.gz
Merge branch 'master' into treeview-refactor
Conflicts: gtk/gtkmarshalers.list tests/Makefile.am
Diffstat (limited to 'gtk')
-rw-r--r--gtk/Makefile.am80
-rw-r--r--gtk/gtk.h9
-rw-r--r--gtk/gtk.symbols7
-rw-r--r--gtk/gtk9slice.c375
-rw-r--r--gtk/gtk9slice.h59
-rw-r--r--gtk/gtkanimationdescription.c137
-rw-r--r--gtk/gtkanimationdescription.h49
-rw-r--r--gtk/gtkborder.c76
-rw-r--r--gtk/gtkborder.h68
-rw-r--r--gtk/gtkbutton.c120
-rw-r--r--gtk/gtkcellrenderer.c6
-rw-r--r--gtk/gtkcellrendererpixbuf.c26
-rw-r--r--gtk/gtkcheckbutton.c88
-rw-r--r--gtk/gtkcombobox.c309
-rw-r--r--gtk/gtkcontainer.c53
-rw-r--r--gtk/gtkcontainer.h5
-rw-r--r--gtk/gtkcssprovider.c3847
-rw-r--r--gtk/gtkcssprovider.h79
-rw-r--r--gtk/gtkentry.c23
-rw-r--r--gtk/gtkenums.h106
-rw-r--r--gtk/gtkexpander.c13
-rw-r--r--gtk/gtkgradient.c282
-rw-r--r--gtk/gtkgradient.h61
-rw-r--r--gtk/gtkiconfactory.c224
-rw-r--r--gtk/gtkiconfactory.h1
-rw-r--r--gtk/gtkicontheme.c94
-rw-r--r--gtk/gtkicontheme.h7
-rw-r--r--gtk/gtkiconview.c4
-rw-r--r--gtk/gtkimage.c24
-rw-r--r--gtk/gtkimcontextsimple.c2
-rw-r--r--gtk/gtkimmodule.c4
-rw-r--r--gtk/gtkimmulticontext.c2
-rw-r--r--gtk/gtkinfobar.c49
-rw-r--r--gtk/gtklabel.c77
-rw-r--r--gtk/gtkmain.c8
-rw-r--r--gtk/gtkmarshalers.list1
-rw-r--r--gtk/gtkmenu.c380
-rw-r--r--gtk/gtkmenubar.c59
-rw-r--r--gtk/gtkmenuitem.c6
-rw-r--r--gtk/gtkmodifierstyle.c287
-rw-r--r--gtk/gtkmodifierstyle.h74
-rw-r--r--gtk/gtknotebook.c62
-rw-r--r--gtk/gtkplug-x11.c10
-rw-r--r--gtk/gtkradiobutton.c74
-rw-r--r--gtk/gtkrange.c16
-rw-r--r--gtk/gtkrc.c55
-rw-r--r--gtk/gtkrc.h4
-rw-r--r--gtk/gtkscrollbar.c4
-rw-r--r--gtk/gtkselection.c2
-rw-r--r--gtk/gtksettings.c188
-rw-r--r--gtk/gtksettings.h2
-rw-r--r--gtk/gtksocket-x11.c45
-rw-r--r--gtk/gtkspinner.c157
-rw-r--r--gtk/gtkstatusicon.c10
-rw-r--r--gtk/gtkstyle.c3106
-rw-r--r--gtk/gtkstyle.h33
-rw-r--r--gtk/gtkstylecontext.c3985
-rw-r--r--gtk/gtkstylecontext.h564
-rw-r--r--gtk/gtkstyleproperties.c1226
-rw-r--r--gtk/gtkstyleproperties.h117
-rw-r--r--gtk/gtkstyleprovider.c148
-rw-r--r--gtk/gtkstyleprovider.h128
-rw-r--r--gtk/gtksymboliccolor.c558
-rw-r--r--gtk/gtksymboliccolor.h55
-rw-r--r--gtk/gtktextbufferserialize.c4
-rw-r--r--gtk/gtkthemes.h4
-rw-r--r--gtk/gtkthemingengine.c3059
-rw-r--r--gtk/gtkthemingengine.h251
-rw-r--r--gtk/gtktimeline.c735
-rw-r--r--gtk/gtktimeline.h117
-rw-r--r--gtk/gtktogglebutton.c28
-rw-r--r--gtk/gtktoolitemgroup.c2
-rw-r--r--gtk/gtktrayicon-x11.c4
-rw-r--r--gtk/gtkwidget.c1197
-rw-r--r--gtk/gtkwidget.h93
-rw-r--r--gtk/gtkwidgetpath.c1048
-rw-r--r--gtk/gtkwidgetpath.h113
-rw-r--r--gtk/gtkwindow.c76
-rw-r--r--gtk/gtkxembed.c4
-rw-r--r--gtk/tests/Makefile.am9
-rw-r--r--gtk/tests/stylecontext.c534
-rw-r--r--gtk/tests/test.css0
-rw-r--r--gtk/tests/test.pngbin0 -> 3569 bytes
83 files changed, 21889 insertions, 3119 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index b3d6e61198..75fe3fbca4 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -167,6 +167,7 @@ gtk_public_h_sources = \
gtkbbox.h \
gtkbin.h \
gtkbindings.h \
+ gtkborder.h \
gtkbox.h \
gtkbuilder.h \
gtkbuildable.h \
@@ -183,7 +184,7 @@ gtk_public_h_sources = \
gtkcellrenderercombo.h \
gtkcellrendererpixbuf.h \
gtkcellrendererprogress.h \
- gtkcellrendererspin.h \
+ gtkcellrendererspin.h \
gtkcellrendererspinner.h\
gtkcellrenderertext.h \
gtkcellrenderertoggle.h \
@@ -197,26 +198,28 @@ gtk_public_h_sources = \
gtkcombobox.h \
gtkcomboboxtext.h \
gtkcontainer.h \
- gtkdebug.h \
+ gtkcssprovider.h \
+ gtkdebug.h \
gtkdialog.h \
gtkdnd.h \
gtkdrawingarea.h \
- gtkeditable.h \
+ gtkeditable.h \
gtkentry.h \
gtkentrybuffer.h \
gtkentrycompletion.h \
gtkenums.h \
gtkeventbox.h \
gtkexpander.h \
- gtkfilechooser.h \
- gtkfilechooserbutton.h \
- gtkfilechooserdialog.h \
- gtkfilechooserwidget.h \
+ gtkfilechooser.h \
+ gtkfilechooserbutton.h \
+ gtkfilechooserdialog.h \
+ gtkfilechooserwidget.h \
gtkfilefilter.h \
gtkfixed.h \
gtkfontbutton.h \
gtkfontsel.h \
gtkframe.h \
+ gtkgradient.h \
gtkgrid.h \
gtkhandlebox.h \
gtkhbbox.h \
@@ -238,7 +241,7 @@ gtk_public_h_sources = \
gtkinfobar.h \
gtkinvisible.h \
gtklabel.h \
- gtklayout.h \
+ gtklayout.h \
gtklinkbutton.h \
gtkliststore.h \
gtkmain.h \
@@ -250,7 +253,7 @@ gtk_public_h_sources = \
gtkmessagedialog.h \
gtkmisc.h \
gtkmodules.h \
- gtkmountoperation.h \
+ gtkmountoperation.h \
gtknotebook.h \
gtkoffscreenwindow.h \
gtkorientable.h \
@@ -260,7 +263,7 @@ gtk_public_h_sources = \
gtkplug.h \
gtkprintcontext.h \
gtkprintoperation.h \
- gtkprintoperationpreview.h \
+ gtkprintoperationpreview.h \
gtkprintsettings.h \
gtkprogressbar.h \
gtkradioaction.h \
@@ -272,13 +275,13 @@ gtk_public_h_sources = \
gtkrecentaction.h \
gtkrecentchooser.h \
gtkrecentchooserdialog.h \
- gtkrecentchoosermenu.h \
+ gtkrecentchoosermenu.h \
gtkrecentchooserwidget.h \
- gtkrecentfilter.h \
+ gtkrecentfilter.h \
gtkrecentmanager.h \
gtkscale.h \
gtkscalebutton.h \
- gtkscrollable.h \
+ gtkscrollable.h \
gtkscrollbar.h \
gtkscrolledwindow.h \
gtkselection.h \
@@ -295,10 +298,14 @@ gtk_public_h_sources = \
gtkstatusbar.h \
gtkstatusicon.h \
gtkstock.h \
+ gtkstylecontext.h \
+ gtkstyleproperties.h \
+ gtkstyleprovider.h \
gtkstyle.h \
gtkswitch.h \
+ gtksymboliccolor.h \
gtktable.h \
- gtktearoffmenuitem.h \
+ gtktearoffmenuitem.h \
gtktestutils.h \
gtktextbuffer.h \
gtktextbufferrichtext.h \
@@ -309,6 +316,7 @@ gtk_public_h_sources = \
gtktexttag.h \
gtktexttagtable.h \
gtktextview.h \
+ gtkthemingengine.h \
gtktoggleaction.h \
gtktogglebutton.h \
gtktoggletoolbutton.h \
@@ -339,6 +347,7 @@ gtk_public_h_sources = \
gtkvscrollbar.h \
gtkvseparator.h \
gtkwidget.h \
+ gtkwidgetpath.h \
gtkwindow.h
if OS_UNIX
@@ -369,12 +378,14 @@ endif
# GTK+ header files that don't get installed
gtk_private_h_sources = \
+ gtk9slice.h \
gtkbuttonprivate.h \
gtkquery.h \
gtksearchengine.h \
gtksearchenginesimple.h \
gtkdndcursors.h \
gtkentryprivate.h \
+ gtkanimationdescription.h \
gtkbuilderprivate.h \
gtkcustompaperunixdialog.h\
gtkfilechooserdefault.h \
@@ -390,6 +401,7 @@ gtk_private_h_sources = \
gtkkeyhash.h \
gtkmenuprivate.h \
gtkmnemonichash.h \
+ gtkmodifierstyle.h \
gtkmountoperationprivate.h \
gtkappchooserprivate.h \
gtkappchoosermodule.h \
@@ -415,6 +427,7 @@ gtk_private_h_sources = \
gtktexttagprivate.h \
gtktexttypes.h \
gtktextutil.h \
+ gtktimeline.h \
gtkthemes.h \
gtktoolpaletteprivate.h \
gtktreedatalist.h \
@@ -426,6 +439,7 @@ gtk_private_h_sources = \
# GTK+ C sources to build the library from
gtk_base_c_sources = \
+ gtk9slice.c \
gtkquery.c \
gtksearchengine.c \
gtksearchenginesimple.c \
@@ -447,12 +461,14 @@ gtk_base_c_sources = \
gtkappchoosermodule.c \
gtkappchooseronline.c \
gtkapplication.c \
+ gtkanimationdescription.c \
gtkarrow.c \
gtkaspectframe.c \
gtkassistant.c \
gtkbbox.c \
gtkbin.c \
gtkbindings.c \
+ gtkborder.c \
gtkbox.c \
gtkbuildable.c \
gtkbuilder.c \
@@ -483,9 +499,10 @@ gtk_base_c_sources = \
gtkcombobox.c \
gtkcomboboxtext.c \
gtkcontainer.c \
+ gtkcssprovider.c \
gtkdialog.c \
gtkdrawingarea.c \
- gtkeditable.c \
+ gtkeditable.c \
gtkentry.c \
gtkentrybuffer.c \
gtkentrycompletion.c \
@@ -503,9 +520,10 @@ gtk_base_c_sources = \
gtkfilesystem.c \
gtkfilesystemmodel.c \
gtkfixed.c \
- gtkfontbutton.c \
- gtkfontsel.c \
+ gtkfontbutton.c \
+ gtkfontsel.c \
gtkframe.c \
+ gtkgradient.c \
gtkgrid.c \
gtkhandlebox.c \
gtkhbbox.c \
@@ -543,8 +561,9 @@ gtk_base_c_sources = \
gtkmessagedialog.c \
gtkmisc.c \
gtkmnemonichash.c \
+ gtkmodifierstyle.c \
gtkmodules.c \
- gtkmountoperation.c \
+ gtkmountoperation.c \
gtknotebook.c \
gtkoffscreenwindow.c \
gtkorientable.c \
@@ -556,7 +575,7 @@ gtk_base_c_sources = \
gtkplug.c \
gtkprintcontext.c \
gtkprintoperation.c \
- gtkprintoperationpreview.c \
+ gtkprintoperationpreview.c \
gtkprintsettings.c \
gtkprintutils.c \
gtkprogressbar.c \
@@ -565,20 +584,20 @@ gtk_base_c_sources = \
gtkradiomenuitem.c \
gtkradiotoolbutton.c \
gtkrange.c \
- gtkrbtree.c \
+ gtkrbtree.c \
gtkrc.c \
gtkrecentaction.c \
gtkrecentchooserdefault.c \
- gtkrecentchooserdialog.c \
- gtkrecentchoosermenu.c \
- gtkrecentchooserwidget.c \
+ gtkrecentchooserdialog.c\
+ gtkrecentchoosermenu.c \
+ gtkrecentchooserwidget.c\
gtkrecentchooserutils.c \
gtkrecentchooser.c \
gtkrecentfilter.c \
gtkrecentmanager.c \
gtkscale.c \
gtkscalebutton.c \
- gtkscrollable.c \
+ gtkscrollable.c \
gtkscrollbar.c \
gtkscrolledwindow.c \
gtkselection.c \
@@ -595,10 +614,14 @@ gtk_base_c_sources = \
gtkstatusbar.c \
gtkstatusicon.c \
gtkstock.c \
+ gtkstylecontext.c \
+ gtkstyleproperties.c \
+ gtkstyleprovider.c \
gtkstyle.c \
gtkswitch.c \
+ gtksymboliccolor.c \
gtktable.c \
- gtktearoffmenuitem.c \
+ gtktearoffmenuitem.c \
gtktestutils.c \
gtktextbtree.c \
gtktextbuffer.c \
@@ -616,13 +639,15 @@ gtk_base_c_sources = \
gtktextutil.c \
gtktextview.c \
gtkthemes.c \
+ gtkthemingengine.c \
+ gtktimeline.c \
gtktoggleaction.c \
gtktogglebutton.c \
gtktoggletoolbutton.c \
gtktoolbar.c \
gtktoolbutton.c \
gtktoolitem.c \
- gtktoolitemgroup.c \
+ gtktoolitemgroup.c \
gtktoolpalette.c \
gtktoolshell.c \
gtktooltip.c \
@@ -648,7 +673,8 @@ gtk_base_c_sources = \
gtkvscrollbar.c \
gtkvseparator.c \
gtkwidget.c \
- gtkwindow-decorate.c \
+ gtkwidgetpath.c \
+ gtkwindow-decorate.c \
gtkwindow.c \
$(gtk_clipboard_dnd_c_sources) \
$(gtk_appchooser_impl_c_sources)
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 38889aef1b..0d85793a3c 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -51,6 +51,7 @@
#include <gtk/gtkbbox.h>
#include <gtk/gtkbin.h>
#include <gtk/gtkbindings.h>
+#include <gtk/gtkborder.h>
#include <gtk/gtkbox.h>
#include <gtk/gtkbuildable.h>
#include <gtk/gtkbuilder.h>
@@ -80,6 +81,7 @@
#include <gtk/gtkcombobox.h>
#include <gtk/gtkcomboboxtext.h>
#include <gtk/gtkcontainer.h>
+#include <gtk/gtkcssprovider.h>
#include <gtk/gtkdebug.h>
#include <gtk/gtkdialog.h>
#include <gtk/gtkdnd.h>
@@ -100,6 +102,7 @@
#include <gtk/gtkfontbutton.h>
#include <gtk/gtkfontsel.h>
#include <gtk/gtkframe.h>
+#include <gtk/gtkgradient.h>
#include <gtk/gtkgrid.h>
#include <gtk/gtkhandlebox.h>
#include <gtk/gtkhbbox.h>
@@ -177,8 +180,12 @@
#include <gtk/gtkstatusbar.h>
#include <gtk/gtkstatusicon.h>
#include <gtk/gtkstock.h>
+#include <gtk/gtkstylecontext.h>
+#include <gtk/gtkstyleproperties.h>
+#include <gtk/gtkstyleprovider.h>
#include <gtk/gtkstyle.h>
#include <gtk/gtkswitch.h>
+#include <gtk/gtksymboliccolor.h>
#include <gtk/gtktable.h>
#include <gtk/gtktearoffmenuitem.h>
#include <gtk/gtktextbuffer.h>
@@ -189,6 +196,7 @@
#include <gtk/gtktexttag.h>
#include <gtk/gtktexttagtable.h>
#include <gtk/gtktextview.h>
+#include <gtk/gtkthemingengine.h>
#include <gtk/gtktoggleaction.h>
#include <gtk/gtktogglebutton.h>
#include <gtk/gtktoggletoolbutton.h>
@@ -221,6 +229,7 @@
#include <gtk/gtkvscrollbar.h>
#include <gtk/gtkvseparator.h>
#include <gtk/gtkwidget.h>
+#include <gtk/gtkwidgetpath.h>
#include <gtk/gtkwindow.h>
#undef __GTK_H_INSIDE__
diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols
index 89f52525e6..0974cae0b5 100644
--- a/gtk/gtk.symbols
+++ b/gtk/gtk.symbols
@@ -1109,6 +1109,7 @@ gtk_icon_set_new
gtk_icon_set_new_from_pixbuf
gtk_icon_set_ref
gtk_icon_set_render_icon
+gtk_icon_set_render_icon_pixbuf
gtk_icon_set_unref
gtk_icon_size_from_name
gtk_icon_size_get_name
@@ -3336,9 +3337,12 @@ gtk_widget_modify_cursor
gtk_widget_modify_fg
gtk_widget_modify_font
gtk_widget_modify_style
-gtk_widget_modify_symbolic_color
gtk_widget_modify_text
gtk_widget_new
+gtk_widget_override_background_color
+gtk_widget_override_color
+gtk_widget_override_font
+gtk_widget_override_symbolic_color
gtk_widget_path
gtk_widget_pop_composite_child
gtk_widget_push_composite_child
@@ -3353,6 +3357,7 @@ gtk_widget_region_intersect
gtk_widget_remove_accelerator
gtk_widget_remove_mnemonic_label
gtk_widget_render_icon
+gtk_widget_render_icon_pixbuf
gtk_widget_reparent
gtk_widget_reset_rc_styles
gtk_widget_reset_shapes
diff --git a/gtk/gtk9slice.c b/gtk/gtk9slice.c
new file mode 100644
index 0000000000..c3e824b4d5
--- /dev/null
+++ b/gtk/gtk9slice.c
@@ -0,0 +1,375 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gtk9slice.h"
+
+enum {
+ BORDER_LEFT,
+ BORDER_MIDDLE,
+ BORDER_RIGHT,
+ BORDER_LAST,
+ BORDER_TOP = BORDER_LEFT,
+ BORDER_BOTTOM = BORDER_RIGHT
+};
+
+enum {
+ SIDE_TOP,
+ SIDE_RIGHT,
+ SIDE_BOTTOM,
+ SIDE_LEFT
+};
+
+struct Gtk9Slice
+{
+ cairo_surface_t *surfaces[BORDER_LAST][BORDER_LAST];
+ GtkSliceSideModifier modifiers[4];
+ gdouble distances[4];
+ gint ref_count;
+};
+
+G_DEFINE_BOXED_TYPE (Gtk9Slice, gtk_9slice,
+ gtk_9slice_ref, gtk_9slice_unref)
+
+
+Gtk9Slice *
+gtk_9slice_new (GdkPixbuf *pixbuf,
+ gdouble distance_top,
+ gdouble distance_bottom,
+ gdouble distance_left,
+ gdouble distance_right,
+ GtkSliceSideModifier horizontal_modifier,
+ GtkSliceSideModifier vertical_modifier)
+{
+ Gtk9Slice *slice;
+ cairo_surface_t *surface;
+ gint width, height;
+ cairo_t *cr;
+
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
+
+ slice = g_slice_new0 (Gtk9Slice);
+ slice->ref_count = 1;
+
+ slice->distances[SIDE_TOP] = distance_top;
+ slice->distances[SIDE_BOTTOM] = distance_bottom;
+ slice->distances[SIDE_LEFT] = distance_left;
+ slice->distances[SIDE_RIGHT] = distance_right;
+
+ slice->modifiers[SIDE_TOP] = slice->modifiers[SIDE_BOTTOM] = horizontal_modifier;
+ slice->modifiers[SIDE_LEFT] = slice->modifiers[SIDE_RIGHT] = vertical_modifier;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ /* Get an image surface from the pixbuf */
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ cr = cairo_create (surface);
+
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ /* Top /left corner */
+ slice->surfaces[BORDER_LEFT][BORDER_TOP] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA,
+ distance_left, distance_top);
+ cr = cairo_create (slice->surfaces[BORDER_LEFT][BORDER_TOP]);
+
+ cairo_set_source_surface (cr, surface, 0, 0);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ /* Top/right corner */
+ slice->surfaces[BORDER_RIGHT][BORDER_TOP] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA,
+ distance_right, distance_top);
+ cr = cairo_create (slice->surfaces[BORDER_RIGHT][BORDER_TOP]);
+
+ cairo_set_source_surface (cr, surface, - width + distance_right, 0);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ /* Bottom/left corner */
+ slice->surfaces[BORDER_LEFT][BORDER_BOTTOM] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA,
+ distance_left, distance_bottom);
+ cr = cairo_create (slice->surfaces[BORDER_LEFT][BORDER_BOTTOM]);
+
+ cairo_set_source_surface (cr, surface, 0, - height + distance_bottom);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ /* Bottom/right corner */
+ slice->surfaces[BORDER_RIGHT][BORDER_BOTTOM] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA,
+ distance_right, distance_bottom);
+ cr = cairo_create (slice->surfaces[BORDER_RIGHT][BORDER_BOTTOM]);
+
+ cairo_set_source_surface (cr, surface, - width + distance_right, - height + distance_bottom);
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+
+ /* Top side */
+ slice->surfaces[BORDER_MIDDLE][BORDER_TOP] = cairo_surface_create_similar (surface,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ width - distance_left - distance_right,
+ distance_top);
+ cr = cairo_create (slice->surfaces[BORDER_MIDDLE][BORDER_TOP]);
+ cairo_set_source_surface (cr, surface, - distance_left, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ /* Bottom side */
+ slice->surfaces[BORDER_MIDDLE][BORDER_BOTTOM] = cairo_surface_create_similar (surface,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ width - distance_left - distance_right,
+ distance_bottom);
+ cr = cairo_create (slice->surfaces[BORDER_MIDDLE][BORDER_BOTTOM]);
+ cairo_set_source_surface (cr, surface, - distance_left, - height + distance_bottom);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ /* Left side */
+ slice->surfaces[BORDER_LEFT][BORDER_MIDDLE] = cairo_surface_create_similar (surface,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ distance_left,
+ height - distance_top - distance_bottom);
+ cr = cairo_create (slice->surfaces[BORDER_LEFT][BORDER_MIDDLE]);
+ cairo_set_source_surface (cr, surface, 0, - distance_top);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ /* Right side */
+ slice->surfaces[BORDER_RIGHT][BORDER_MIDDLE] = cairo_surface_create_similar (surface,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ distance_right,
+ height - distance_top - distance_bottom);
+ cr = cairo_create (slice->surfaces[BORDER_RIGHT][BORDER_MIDDLE]);
+ cairo_set_source_surface (cr, surface, - width + distance_right, - distance_top);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ /* Center */
+ slice->surfaces[BORDER_MIDDLE][BORDER_MIDDLE] = cairo_surface_create_similar (surface,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ width - distance_left - distance_right,
+ height - distance_top - distance_bottom);
+ cr = cairo_create (slice->surfaces[BORDER_MIDDLE][BORDER_MIDDLE]);
+ cairo_set_source_surface (cr, surface, - distance_left, - distance_top);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ cairo_surface_destroy (surface);
+
+ return slice;
+}
+
+static void
+render_border (cairo_t *cr,
+ cairo_surface_t *surface,
+ guint side,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkSliceSideModifier modifier)
+{
+ cairo_pattern_t *pattern;
+ cairo_matrix_t matrix;
+
+ cairo_save (cr);
+
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_clip (cr);
+
+ pattern = cairo_pattern_create_for_surface (surface);
+
+ if (modifier == GTK_SLICE_REPEAT)
+ {
+ cairo_matrix_init_translate (&matrix, - x, - y);
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+
+ cairo_pattern_set_matrix (pattern, &matrix);
+ cairo_set_source (cr, pattern);
+
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_fill (cr);
+ }
+ else
+ {
+ /* Use nearest filter so borders aren't blurred */
+ cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST);
+
+ if (side == SIDE_TOP || side == SIDE_BOTTOM)
+ {
+ gint w = cairo_image_surface_get_width (surface);
+
+ cairo_translate (cr, x, y);
+ cairo_scale (cr, width / w, 1.0);
+ }
+ else
+ {
+ gint h = cairo_image_surface_get_height (surface);
+
+ cairo_translate (cr, x, y);
+ cairo_scale (cr, 1.0, height / h);
+ }
+
+ cairo_set_source (cr, pattern);
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_paint (cr);
+ }
+
+ cairo_restore (cr);
+
+ cairo_pattern_destroy (pattern);
+}
+
+static void
+render_corner (cairo_t *cr,
+ cairo_surface_t *surface,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ cairo_save (cr);
+
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_clip (cr);
+
+ cairo_set_source_surface (cr, surface, x, y);
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_fill (cr);
+
+ cairo_restore (cr);
+}
+
+void
+gtk_9slice_render (Gtk9Slice *slice,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ int img_width, img_height;
+ cairo_surface_t *surface;
+
+ cairo_save (cr);
+
+ /* Top side */
+ surface = slice->surfaces[BORDER_MIDDLE][BORDER_TOP];
+ img_height = cairo_image_surface_get_height (surface);
+
+ render_border (cr, surface, SIDE_TOP,
+ x + slice->distances[SIDE_LEFT], y,
+ width - slice->distances[SIDE_LEFT] - slice->distances[SIDE_RIGHT],
+ (gdouble) img_height,
+ slice->modifiers[SIDE_TOP]);
+
+ /* Bottom side */
+ surface = slice->surfaces[BORDER_MIDDLE][BORDER_BOTTOM];
+ img_height = cairo_image_surface_get_height (surface);
+
+ render_border (cr, surface, SIDE_BOTTOM,
+ x + slice->distances[SIDE_LEFT], y + height - img_height,
+ width - slice->distances[SIDE_LEFT] - slice->distances[SIDE_RIGHT],
+ (gdouble) img_height,
+ slice->modifiers[SIDE_BOTTOM]);
+
+ /* Left side */
+ surface = slice->surfaces[BORDER_LEFT][BORDER_MIDDLE];
+ img_width = cairo_image_surface_get_width (surface);
+
+ render_border (cr, surface, SIDE_LEFT,
+ x, y + slice->distances[SIDE_TOP],
+ (gdouble) img_width,
+ height - slice->distances[SIDE_TOP] - slice->distances[SIDE_BOTTOM],
+ slice->modifiers[SIDE_LEFT]);
+
+ /* Right side */
+ surface = slice->surfaces[BORDER_RIGHT][BORDER_MIDDLE];
+ img_width = cairo_image_surface_get_width (surface);
+
+ render_border (cr, surface, SIDE_RIGHT,
+ x + width - img_width, y + slice->distances[SIDE_TOP],
+ (gdouble) img_width,
+ height - slice->distances[SIDE_TOP] - slice->distances[SIDE_BOTTOM],
+ slice->modifiers[SIDE_RIGHT]);
+
+ /* Top/Left corner */
+ surface = slice->surfaces[BORDER_LEFT][BORDER_TOP];
+ img_width = cairo_image_surface_get_width (surface);
+ img_height = cairo_image_surface_get_height (surface);
+ render_corner (cr, surface, x, y, (gdouble) img_width, (gdouble) img_height);
+
+ /* Top/right corner */
+ surface = slice->surfaces[BORDER_RIGHT][BORDER_TOP];
+ img_width = cairo_image_surface_get_width (surface);
+ img_height = cairo_image_surface_get_height (surface);
+ render_corner (cr, surface, x + width - img_width, y,
+ (gdouble) img_width, (gdouble) img_height);
+
+ /* Bottom/left corner */
+ surface = slice->surfaces[BORDER_LEFT][BORDER_BOTTOM];
+ img_width = cairo_image_surface_get_width (surface);
+ img_height = cairo_image_surface_get_height (surface);
+ render_corner (cr, surface, x, y + height - img_height,
+ (gdouble) img_width, (gdouble) img_height);
+
+ /* Bottom/right corner */
+ surface = slice->surfaces[BORDER_RIGHT][BORDER_BOTTOM];
+ img_width = cairo_image_surface_get_width (surface);
+ img_height = cairo_image_surface_get_height (surface);
+ render_corner (cr, surface, x + width - img_width, y + height - img_height,
+ (gdouble) img_width, (gdouble) img_height);
+
+ cairo_restore (cr);
+}
+
+Gtk9Slice *
+gtk_9slice_ref (Gtk9Slice *slice)
+{
+ g_return_val_if_fail (slice != NULL, NULL);
+
+ slice->ref_count++;
+ return slice;
+}
+
+void
+gtk_9slice_unref (Gtk9Slice *slice)
+{
+ g_return_if_fail (slice != NULL);
+
+ slice->ref_count--;
+
+ if (slice->ref_count == 0)
+ {
+ gint i, j;
+
+ for (i = 0; i < BORDER_LAST; i++)
+ for (j = 0; j < BORDER_LAST; j++)
+ cairo_surface_destroy (slice->surfaces[i][j]);
+
+ g_slice_free (Gtk9Slice, slice);
+ }
+}
diff --git a/gtk/gtk9slice.h b/gtk/gtk9slice.h
new file mode 100644
index 0000000000..10e3df079d
--- /dev/null
+++ b/gtk/gtk9slice.h
@@ -0,0 +1,59 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_9SLICE_H__
+#define __GTK_9SLICE_H__
+
+#include "gtktimeline.h"
+
+G_BEGIN_DECLS
+
+/* Dummy typedefs */
+typedef struct Gtk9Slice Gtk9Slice;
+
+#define GTK_TYPE_9SLICE (gtk_9slice_get_type ())
+
+typedef enum {
+ GTK_SLICE_REPEAT,
+ GTK_SLICE_STRETCH
+} GtkSliceSideModifier;
+
+GType gtk_9slice_get_type (void) G_GNUC_CONST;
+
+Gtk9Slice * gtk_9slice_new (GdkPixbuf *pixbuf,
+ gdouble distance_top,
+ gdouble distance_bottom,
+ gdouble distance_left,
+ gdouble distance_right,
+ GtkSliceSideModifier horizontal_modifier,
+ GtkSliceSideModifier vertical_modifier);
+
+Gtk9Slice * gtk_9slice_ref (Gtk9Slice *slice);
+void gtk_9slice_unref (Gtk9Slice *slice);
+
+void gtk_9slice_render (Gtk9Slice *slice,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+
+G_END_DECLS
+
+#endif /* __GTK_9SLICE_H__ */
diff --git a/gtk/gtkanimationdescription.c b/gtk/gtkanimationdescription.c
new file mode 100644
index 0000000000..785018ad37
--- /dev/null
+++ b/gtk/gtkanimationdescription.c
@@ -0,0 +1,137 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gtkanimationdescription.h"
+#include "gtkintl.h"
+
+struct GtkAnimationDescription
+{
+ GtkTimelineProgressType progress_type;
+ gdouble duration;
+ guint loop : 1;
+ guint ref_count;
+};
+
+GtkAnimationDescription *
+gtk_animation_description_new (gdouble duration,
+ GtkTimelineProgressType progress_type,
+ gboolean loop)
+{
+ GtkAnimationDescription *desc;
+
+ desc = g_slice_new (GtkAnimationDescription);
+ desc->duration = duration;
+ desc->progress_type = progress_type;
+ desc->loop = loop;
+ desc->ref_count = 1;
+
+ return desc;
+}
+
+gdouble
+gtk_animation_description_get_duration (GtkAnimationDescription *desc)
+{
+ return desc->duration;
+}
+
+GtkTimelineProgressType
+gtk_animation_description_get_progress_type (GtkAnimationDescription *desc)
+{
+ return desc->progress_type;
+}
+
+gboolean
+gtk_animation_description_get_loop (GtkAnimationDescription *desc)
+{
+ return (desc->loop != 0);
+}
+
+GtkAnimationDescription *
+gtk_animation_description_ref (GtkAnimationDescription *desc)
+{
+ desc->ref_count++;
+ return desc;
+}
+
+void
+gtk_animation_description_unref (GtkAnimationDescription *desc)
+{
+ desc->ref_count--;
+
+ if (desc->ref_count == 0)
+ g_slice_free (GtkAnimationDescription, desc);
+}
+
+GtkAnimationDescription *
+gtk_animation_description_from_string (const gchar *str)
+{
+ gchar timing_function[16] = { 0, };
+ gchar duration_unit[3] = { 0, };
+ gchar loop_str[5] = { 0, };
+ GtkTimelineProgressType progress_type;
+ guint duration = 0;
+ gboolean loop;
+
+ if (sscanf (str, "%d%2s %15s %5s", &duration, duration_unit, timing_function, loop_str) == 4)
+ loop = TRUE;
+ else if (sscanf (str, "%d%2s %15s", &duration, duration_unit, timing_function) == 3)
+ loop = FALSE;
+ else
+ return NULL;
+
+ if (strcmp (duration_unit, "s") == 0)
+ duration *= 1000;
+ else if (strcmp (duration_unit, "ms") != 0)
+ {
+ g_warning ("Unknown duration unit: %s\n", duration_unit);
+ return NULL;
+ }
+
+ if (strcmp (timing_function, "linear") == 0)
+ progress_type = GTK_TIMELINE_PROGRESS_LINEAR;
+ else if (strcmp (timing_function, "ease") == 0)
+ progress_type = GTK_TIMELINE_PROGRESS_EASE;
+ else if (strcmp (timing_function, "ease-in") == 0)
+ progress_type = GTK_TIMELINE_PROGRESS_EASE_IN;
+ else if (strcmp (timing_function, "ease-out") == 0)
+ progress_type = GTK_TIMELINE_PROGRESS_EASE_OUT;
+ else if (strcmp (timing_function, "ease-in-out") == 0)
+ progress_type = GTK_TIMELINE_PROGRESS_EASE_IN_OUT;
+ else
+ {
+ g_warning ("Unknown timing function: %s\n", timing_function);
+ return NULL;
+ }
+
+ return gtk_animation_description_new ((gdouble) duration, progress_type, loop);
+}
+
+GType
+gtk_animation_description_get_type (void)
+{
+ static GType type = 0;
+
+ if (G_UNLIKELY (!type))
+ type = g_boxed_type_register_static (I_("GtkAnimationDescription"),
+ (GBoxedCopyFunc) gtk_animation_description_ref,
+ (GBoxedFreeFunc) gtk_animation_description_unref);
+
+ return type;
+}
diff --git a/gtk/gtkanimationdescription.h b/gtk/gtkanimationdescription.h
new file mode 100644
index 0000000000..34fc8fcb95
--- /dev/null
+++ b/gtk/gtkanimationdescription.h
@@ -0,0 +1,49 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_ANIMATION_DESCRIPTION_H__
+#define __GTK_ANIMATION_DESCRIPTION_H__
+
+#include "gtktimeline.h"
+
+G_BEGIN_DECLS
+
+/* Dummy typedefs */
+typedef struct GtkAnimationDescription GtkAnimationDescription;
+
+#define GTK_TYPE_ANIMATION_DESCRIPTION (gtk_animation_description_get_type ())
+
+GType gtk_animation_description_get_type (void) G_GNUC_CONST;
+
+GtkAnimationDescription * gtk_animation_description_new (gdouble duration,
+ GtkTimelineProgressType progress_type,
+ gboolean loop);
+
+gdouble gtk_animation_description_get_duration (GtkAnimationDescription *desc);
+GtkTimelineProgressType gtk_animation_description_get_progress_type (GtkAnimationDescription *desc);
+gboolean gtk_animation_description_get_loop (GtkAnimationDescription *desc);
+
+GtkAnimationDescription * gtk_animation_description_ref (GtkAnimationDescription *desc);
+void gtk_animation_description_unref (GtkAnimationDescription *desc);
+
+GtkAnimationDescription * gtk_animation_description_from_string (const gchar *str);
+
+G_END_DECLS
+
+#endif /* __GTK_ANIMATION_DESCRIPTION_H__ */
diff --git a/gtk/gtkborder.c b/gtk/gtkborder.c
new file mode 100644
index 0000000000..6ea466a405
--- /dev/null
+++ b/gtk/gtkborder.c
@@ -0,0 +1,76 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#include "config.h"
+
+#include "gtkborder.h"
+
+/**
+ * gtk_border_new:
+ *
+ * Allocates a new #GtkBorder structure and initializes its elements to zero.
+ *
+ * Returns: a newly allocated #GtkBorder. Free with gtk_border_free()
+ *
+ * Since: 2.14
+ */
+GtkBorder *
+gtk_border_new (void)
+{
+ return g_slice_new0 (GtkBorder);
+}
+
+/**
+ * gtk_border_copy:
+ * @border_: a #GtkBorder
+ *
+ * Copies a #GtkBorder structure.
+ *
+ * Returns: a copy of @border_.
+ */
+GtkBorder *
+gtk_border_copy (const GtkBorder *border_)
+{
+ g_return_val_if_fail (border_ != NULL, NULL);
+
+ return g_slice_dup (GtkBorder, border_);
+}
+
+/**
+ * gtk_border_free:
+ * @border_: a #GtkBorder
+ *
+ * Frees a #GtkBorder structure.
+ */
+void
+gtk_border_free (GtkBorder *border_)
+{
+ g_slice_free (GtkBorder, border_);
+}
+
+G_DEFINE_BOXED_TYPE (GtkBorder, gtk_border,
+ gtk_border_copy,
+ gtk_border_free)
diff --git a/gtk/gtkborder.h b/gtk/gtkborder.h
new file mode 100644
index 0000000000..51a5b7bfd6
--- /dev/null
+++ b/gtk/gtkborder.h
@@ -0,0 +1,68 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GTK+ Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_BORDER_H__
+#define __GTK_BORDER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GtkBorder GtkBorder;
+
+#define GTK_TYPE_BORDER (gtk_border_get_type ())
+
+/**
+ * GtkBorder:
+ * @left: The width of the left border.
+ * @right: The width of the right border.
+ * @top: The width of the top border.
+ * @bottom: The width of the bottom border.
+ *
+ * A struct that specifies a border around a rectangular area that can
+ * be of different width on each side.
+ */
+struct _GtkBorder
+{
+ gint16 left;
+ gint16 right;
+ gint16 top;
+ gint16 bottom;
+};
+
+GType gtk_border_get_type (void) G_GNUC_CONST;
+GtkBorder *gtk_border_new (void) G_GNUC_MALLOC;
+GtkBorder *gtk_border_copy (const GtkBorder *border_);
+void gtk_border_free (GtkBorder *border_);
+
+
+G_END_DECLS
+
+#endif /* __GTK_BORDER_H__ */
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c
index 71fb865f43..f0e00dadef 100644
--- a/gtk/gtkbutton.c
+++ b/gtk/gtkbutton.c
@@ -113,7 +113,7 @@ static void gtk_button_realize (GtkWidget * widget);
static void gtk_button_unrealize (GtkWidget * widget);
static void gtk_button_map (GtkWidget * widget);
static void gtk_button_unmap (GtkWidget * widget);
-static void gtk_button_style_set (GtkWidget * widget, GtkStyle * prev_style);
+static void gtk_button_style_updated (GtkWidget * widget);
static void gtk_button_size_allocate (GtkWidget * widget,
GtkAllocation * allocation);
static gint gtk_button_draw (GtkWidget * widget, cairo_t *cr);
@@ -197,7 +197,7 @@ gtk_button_class_init (GtkButtonClass *klass)
widget_class->unrealize = gtk_button_unrealize;
widget_class->map = gtk_button_map;
widget_class->unmap = gtk_button_unmap;
- widget_class->style_set = gtk_button_style_set;
+ widget_class->style_updated = gtk_button_style_updated;
widget_class->size_allocate = gtk_button_size_allocate;
widget_class->draw = gtk_button_draw;
widget_class->button_press_event = gtk_button_button_press;
@@ -533,6 +533,7 @@ static void
gtk_button_init (GtkButton *button)
{
GtkButtonPrivate *priv;
+ GtkStyleContext *context;
button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
GTK_TYPE_BUTTON,
@@ -561,6 +562,9 @@ gtk_button_init (GtkButton *button)
priv->image_is_stock = TRUE;
priv->image_position = GTK_POS_LEFT;
priv->use_action_appearance = TRUE;
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (button));
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
}
static void
@@ -1375,8 +1379,7 @@ gtk_button_update_image_spacing (GtkButton *button)
}
static void
-gtk_button_style_set (GtkWidget *widget,
- GtkStyle *prev_style)
+gtk_button_style_updated (GtkWidget *widget)
{
gtk_button_update_image_spacing (GTK_BUTTON (widget));
}
@@ -1441,18 +1444,21 @@ gtk_button_size_allocate (GtkWidget *widget,
GtkButton *button = GTK_BUTTON (widget);
GtkButtonPrivate *priv = button->priv;
GtkAllocation child_allocation;
- GtkStyle *style;
+ GtkStyleContext *context;
+ GtkStateFlags state;
GtkWidget *child;
- gint xthickness, ythickness;
GtkBorder default_border;
- GtkBorder inner_border;
+ GtkBorder inner_border, *border;
gint focus_width;
gint focus_pad;
- style = gtk_widget_get_style (widget);
- xthickness = style->xthickness;
- ythickness = style->ythickness;
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+
+ gtk_style_context_get (context, state,
+ "border-width", &border,
+ NULL);
gtk_button_get_props (button, &default_border, NULL, &inner_border, NULL);
gtk_widget_style_get (GTK_WIDGET (widget),
@@ -1472,18 +1478,18 @@ gtk_button_size_allocate (GtkWidget *widget,
child = gtk_bin_get_child (GTK_BIN (button));
if (child && gtk_widget_get_visible (child))
{
- child_allocation.x = allocation->x + inner_border.left + xthickness;
- child_allocation.y = allocation->y + inner_border.top + ythickness;
+ child_allocation.x = allocation->x + inner_border.left + border->left;
+ child_allocation.y = allocation->y + inner_border.top + border->top;
child_allocation.width =
allocation->width -
- xthickness * 2 -
+ (border->left + border->right) -
inner_border.left -
inner_border.right;
child_allocation.height =
allocation->height -
- ythickness * 2 -
+ (border->top + border->bottom) -
inner_border.top -
inner_border.bottom;
@@ -1521,6 +1527,8 @@ gtk_button_size_allocate (GtkWidget *widget,
gtk_widget_size_allocate (child, &child_allocation);
}
+
+ gtk_border_free (border);
}
void
@@ -1543,7 +1551,8 @@ _gtk_button_paint (GtkButton *button,
gint focus_pad;
GtkAllocation allocation;
GdkWindow *window;
- GtkStyle *style;
+ GtkStyleContext *context;
+ GtkStateFlags state;
widget = GTK_WIDGET (button);
@@ -1554,7 +1563,7 @@ _gtk_button_paint (GtkButton *button,
NULL);
gtk_widget_get_allocation (widget, &allocation);
- style = gtk_widget_get_style (widget);
+ context = gtk_widget_get_style_context (widget);
window = gtk_widget_get_window (widget);
x = 0;
@@ -1563,15 +1572,12 @@ _gtk_button_paint (GtkButton *button,
if (gtk_widget_has_default (widget) &&
priv->relief == GTK_RELIEF_NORMAL)
{
- gtk_paint_box (style, cr,
- GTK_STATE_NORMAL, GTK_SHADOW_IN,
- widget, "buttondefault",
- x, y, width, height);
-
x += default_border.left;
y += default_border.top;
width -= default_border.left + default_border.right;
height -= default_border.top + default_border.bottom;
+
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_DEFAULT);
}
else if (gtk_widget_get_can_default (widget))
{
@@ -1589,31 +1595,41 @@ _gtk_button_paint (GtkButton *button,
height -= 2 * (focus_width + focus_pad);
}
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_set_state (context, state);
+
if (priv->relief != GTK_RELIEF_NONE || priv->depressed ||
- gtk_widget_get_state(widget) == GTK_STATE_PRELIGHT)
- gtk_paint_box (style, cr,
- state_type,
- shadow_type, widget, "button",
- x, y, width, height);
-
+ state & GTK_STATE_FLAG_PRELIGHT)
+ {
+ gtk_render_background (context, cr,
+ x, y, width, height);
+ gtk_render_frame (context, cr,
+ x, y, width, height);
+ }
+
if (gtk_widget_has_focus (widget))
{
gint child_displacement_x;
gint child_displacement_y;
gboolean displace_focus;
-
+ GtkBorder *border;
+
gtk_widget_style_get (widget,
"child-displacement-y", &child_displacement_y,
"child-displacement-x", &child_displacement_x,
"displace-focus", &displace_focus,
NULL);
+ gtk_style_context_get (context, state,
+ "border-width", &border,
+ NULL);
+
if (interior_focus)
{
- x += style->xthickness + focus_pad;
- y += style->ythickness + focus_pad;
- width -= 2 * (style->xthickness + focus_pad);
- height -= 2 * (style->ythickness + focus_pad);
+ x += border->left + focus_pad;
+ y += border->top + focus_pad;
+ width -= (2 * focus_pad) + border->left + border->right;
+ height -= (2 * focus_pad) + border->top + border->bottom;
}
else
{
@@ -1629,10 +1645,10 @@ _gtk_button_paint (GtkButton *button,
y += child_displacement_y;
}
- gtk_paint_focus (style, cr,
- gtk_widget_get_state (widget),
- widget, "button",
- x, y, width, height);
+ gtk_render_focus (context, cr,
+ x, y, width, height);
+
+ gtk_border_free (border);
}
}
@@ -1889,10 +1905,12 @@ gtk_button_get_size (GtkWidget *widget,
gint *natural_size)
{
GtkButton *button = GTK_BUTTON (widget);
- GtkStyle *style;
+ GtkStyleContext *context;
GtkWidget *child;
GtkBorder default_border;
GtkBorder inner_border;
+ GtkStateFlags state;
+ GtkBorder *border;
gint focus_width;
gint focus_pad;
gint minimum, natural;
@@ -1903,11 +1921,16 @@ gtk_button_get_size (GtkWidget *widget,
"focus-padding", &focus_pad,
NULL);
- style = gtk_widget_get_style (GTK_WIDGET (widget));
+ context = gtk_widget_get_style_context (GTK_WIDGET (widget));
+ state = gtk_widget_get_state_flags (GTK_WIDGET (widget));
+
+ gtk_style_context_get (context, state,
+ "border-width", &border,
+ NULL);
if (orientation == GTK_ORIENTATION_HORIZONTAL)
{
- minimum = (style->xthickness * 2 +
+ minimum = (border->left + border->right +
inner_border.left + inner_border.right);
if (gtk_widget_get_can_default (GTK_WIDGET (widget)))
@@ -1915,7 +1938,7 @@ gtk_button_get_size (GtkWidget *widget,
}
else
{
- minimum = (style->ythickness * 2 +
+ minimum = (border->top + border->bottom +
inner_border.top + inner_border.bottom);
if (gtk_widget_get_can_default (GTK_WIDGET (widget)))
@@ -1944,6 +1967,8 @@ gtk_button_get_size (GtkWidget *widget,
if (natural_size)
*natural_size = natural;
+
+ gtk_border_free (border);
}
static void
@@ -2255,7 +2280,7 @@ static void
gtk_button_update_state (GtkButton *button)
{
GtkButtonPrivate *priv = button->priv;
- GtkStateType new_state;
+ GtkStateFlags new_state = 0;
gboolean depressed;
if (priv->activate_timeout)
@@ -2263,13 +2288,14 @@ gtk_button_update_state (GtkButton *button)
else
depressed = priv->in_button && priv->button_down;
- if (priv->in_button && (!priv->button_down || !depressed))
- new_state = GTK_STATE_PRELIGHT;
- else
- new_state = depressed ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL;
+ if (priv->in_button)
+ new_state |= GTK_STATE_FLAG_PRELIGHT;
+
+ if (priv->button_down || depressed)
+ new_state |= GTK_STATE_FLAG_ACTIVE;
- _gtk_button_set_depressed (button, depressed);
- gtk_widget_set_state (GTK_WIDGET (button), new_state);
+ _gtk_button_set_depressed (button, depressed);
+ gtk_widget_set_state_flags (GTK_WIDGET (button), new_state, TRUE);
}
static void
diff --git a/gtk/gtkcellrenderer.c b/gtk/gtkcellrenderer.c
index 96465a84dc..a60b8885cc 100644
--- a/gtk/gtkcellrenderer.c
+++ b/gtk/gtkcellrenderer.c
@@ -27,18 +27,18 @@
/**
* SECTION:gtkcellrenderer
- * @Short_description: An object for rendering a single cell on a GdkDrawable
+ * @Short_description: An object for rendering a single cell
* @Title: GtkCellRenderer
* @See_also: #GtkCellRendererText, #GtkCellRendererPixbuf, #GtkCellRendererToggle
*
* The #GtkCellRenderer is a base class of a set of objects used for
- * rendering a cell to a #GdkDrawable. These objects are used primarily by
+ * rendering a cell to a #cairo_t. These objects are used primarily by
* the #GtkTreeView widget, though they aren't tied to them in any
* specific way. It is worth noting that #GtkCellRenderer is not a
* #GtkWidget and cannot be treated as such.
*
* The primary use of a #GtkCellRenderer is for drawing a certain graphical
- * elements on a #GdkDrawable. Typically, one cell renderer is used to
+ * elements on a #cairo_t. Typically, one cell renderer is used to
* draw many cells on the screen. To this extent, it isn't expected that a
* CellRenderer keep any permanent state around. Instead, any state is set
* just prior to use using #GObject<!-- -->s property system. Then, the
diff --git a/gtk/gtkcellrendererpixbuf.c b/gtk/gtkcellrendererpixbuf.c
index 52b4ec98e5..729d08a89d 100644
--- a/gtk/gtkcellrendererpixbuf.c
+++ b/gtk/gtkcellrendererpixbuf.c
@@ -533,14 +533,13 @@ gtk_cell_renderer_pixbuf_create_themed_pixbuf (GtkCellRendererPixbuf *cellpixbuf
if (info)
{
- GtkStyle *style;
-
- style = gtk_widget_get_style (GTK_WIDGET (widget));
- priv->pixbuf = gtk_icon_info_load_symbolic_for_style (info,
- style,
- GTK_STATE_NORMAL,
- NULL,
- NULL);
+ GtkStyleContext *context;
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (widget));
+ priv->pixbuf = gtk_icon_info_load_symbolic_for_context (info,
+ context,
+ NULL,
+ NULL);
gtk_icon_info_free (info);
}
@@ -603,12 +602,13 @@ create_symbolic_pixbuf (GtkCellRendererPixbuf *cellpixbuf,
if (info)
{
- GtkStyle *style;
+ GtkStyleContext *context;
- style = gtk_widget_get_style (GTK_WIDGET (widget));
- pixbuf = gtk_icon_info_load_symbolic_for_style (info,
- style, state,
- NULL, NULL);
+ context = gtk_widget_get_style_context (GTK_WIDGET (widget));
+ pixbuf = gtk_icon_info_load_symbolic_for_context (info,
+ context,
+ NULL,
+ NULL);
gtk_icon_info_free (info);
return pixbuf;
}
diff --git a/gtk/gtkcheckbutton.c b/gtk/gtkcheckbutton.c
index f07ecf1282..91b92f0f70 100644
--- a/gtk/gtkcheckbutton.c
+++ b/gtk/gtkcheckbutton.c
@@ -155,34 +155,33 @@ gtk_check_button_paint (GtkWidget *widget,
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
if (gtk_widget_has_focus (widget))
{
- GtkStateType state = gtk_widget_get_state (widget);
- GtkStyle *style = gtk_widget_get_style (widget);
GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
+ GtkStyleContext *context;
+ GtkStateFlags state;
GtkAllocation allocation;
gtk_widget_get_allocation (widget, &allocation);
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+
+ gtk_style_context_set_state (context, state);
if (interior_focus && child && gtk_widget_get_visible (child))
{
GtkAllocation child_allocation;
gtk_widget_get_allocation (child, &child_allocation);
- gtk_paint_focus (style, cr, state,
- widget, "checkbutton",
- child_allocation.x - allocation.x - focus_width - focus_pad,
- child_allocation.y - allocation.y - focus_width - focus_pad,
- child_allocation.width + 2 * (focus_width + focus_pad),
- child_allocation.height + 2 * (focus_width + focus_pad));
+ gtk_render_focus (context, cr,
+ child_allocation.x - allocation.x - focus_width - focus_pad,
+ child_allocation.y - allocation.y - focus_width - focus_pad,
+ child_allocation.width + 2 * (focus_width + focus_pad),
+ child_allocation.height + 2 * (focus_width + focus_pad));
}
else
- {
- gtk_paint_focus (style, cr, state,
- widget, "checkbutton",
- border_width,
- border_width,
- allocation.width - 2 * border_width,
- allocation.height - 2 * border_width);
- }
+ gtk_render_focus (context, cr,
+ border_width, border_width,
+ allocation.width - 2 * border_width,
+ allocation.height - 2 * border_width);
}
}
@@ -408,8 +407,7 @@ gtk_real_check_button_draw_indicator (GtkCheckButton *check_button,
GtkWidget *child;
GtkButton *button;
GtkToggleButton *toggle_button;
- GtkStateType state_type;
- GtkShadowType shadow_type;
+ GtkStateFlags state = 0;
gint x, y;
gint indicator_size;
gint indicator_spacing;
@@ -418,16 +416,14 @@ gtk_real_check_button_draw_indicator (GtkCheckButton *check_button,
guint border_width;
gboolean interior_focus;
GtkAllocation allocation;
- GtkStyle *style;
- GdkWindow *window;
+ GtkStyleContext *context;
widget = GTK_WIDGET (check_button);
button = GTK_BUTTON (check_button);
toggle_button = GTK_TOGGLE_BUTTON (check_button);
gtk_widget_get_allocation (widget, &allocation);
- style = gtk_widget_get_style (widget);
- window = gtk_widget_get_window (widget);
+ context = gtk_widget_get_style_context (widget);
gtk_widget_style_get (widget,
"interior-focus", &interior_focus,
@@ -447,37 +443,35 @@ gtk_real_check_button_draw_indicator (GtkCheckButton *check_button,
x += focus_width + focus_pad;
if (gtk_toggle_button_get_inconsistent (toggle_button))
- shadow_type = GTK_SHADOW_ETCHED_IN;
- else if (gtk_toggle_button_get_active (toggle_button))
- shadow_type = GTK_SHADOW_IN;
- else
- shadow_type = GTK_SHADOW_OUT;
+ state |= GTK_STATE_FLAG_INCONSISTENT;
+ else if (gtk_toggle_button_get_active (toggle_button) ||
+ (button->priv->button_down && button->priv->in_button))
+ state |= GTK_STATE_FLAG_ACTIVE;
if (button->priv->activate_timeout || (button->priv->button_down && button->priv->in_button))
- state_type = GTK_STATE_ACTIVE;
- else if (button->priv->in_button)
- state_type = GTK_STATE_PRELIGHT;
+ state |= GTK_STATE_FLAG_SELECTED;
+
+ if (button->priv->in_button)
+ state |= GTK_STATE_FLAG_PRELIGHT;
else if (!gtk_widget_is_sensitive (widget))
- state_type = GTK_STATE_INSENSITIVE;
- else
- state_type = GTK_STATE_NORMAL;
-
+ state |= GTK_STATE_FLAG_INSENSITIVE;
+
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
x = allocation.width - (indicator_size + x);
- if (gtk_widget_get_state (widget) == GTK_STATE_PRELIGHT)
- {
+ gtk_style_context_set_state (context, state);
- gtk_paint_flat_box (style, cr, GTK_STATE_PRELIGHT,
- GTK_SHADOW_ETCHED_OUT,
- widget, "checkbutton",
- border_width, border_width,
- allocation.width - (2 * border_width),
- allocation.height - (2 * border_width));
- }
+ if (state & GTK_STATE_FLAG_PRELIGHT)
+ gtk_render_background (context, cr,
+ border_width, border_width,
+ allocation.width - (2 * border_width),
+ allocation.height - (2 * border_width));
+
+ gtk_style_context_save (context);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_CHECK);
+
+ gtk_render_check (context, cr,
+ x, y, indicator_size, indicator_size);
- gtk_paint_check (style, cr,
- state_type, shadow_type,
- widget, "checkbutton",
- x, y, indicator_size, indicator_size);
+ gtk_style_context_restore (context);
}
diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c
index ebdd7f5e1d..0cf07377b0 100644
--- a/gtk/gtkcombobox.c
+++ b/gtk/gtkcombobox.c
@@ -280,13 +280,12 @@ static void gtk_combo_box_get_property (GObject *object,
static void gtk_combo_box_state_changed (GtkWidget *widget,
GtkStateType previous);
static void gtk_combo_box_grab_focus (GtkWidget *widget);
-static void gtk_combo_box_style_set (GtkWidget *widget,
- GtkStyle *previous);
+static void gtk_combo_box_style_updated (GtkWidget *widget);
static void gtk_combo_box_button_toggled (GtkWidget *widget,
gpointer data);
-static void gtk_combo_box_button_state_changed (GtkWidget *widget,
- GtkStateType previous,
- gpointer data);
+static void gtk_combo_box_button_state_flags_changed (GtkWidget *widget,
+ GtkStateFlags previous,
+ gpointer data);
static void gtk_combo_box_add (GtkContainer *container,
GtkWidget *widget);
static void gtk_combo_box_remove (GtkContainer *container,
@@ -566,7 +565,7 @@ gtk_combo_box_class_init (GtkComboBoxClass *klass)
widget_class->scroll_event = gtk_combo_box_scroll_event;
widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
widget_class->grab_focus = gtk_combo_box_grab_focus;
- widget_class->style_set = gtk_combo_box_style_set;
+ widget_class->style_updated = gtk_combo_box_style_updated;
widget_class->state_changed = gtk_combo_box_state_changed;
widget_class->get_preferred_width = gtk_combo_box_get_preferred_width;
widget_class->get_preferred_height = gtk_combo_box_get_preferred_height;
@@ -1079,6 +1078,7 @@ static void
gtk_combo_box_init (GtkComboBox *combo_box)
{
GtkComboBoxPrivate *priv;
+ GtkStyleContext *context;
combo_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (combo_box,
GTK_TYPE_COMBO_BOX,
@@ -1116,6 +1116,9 @@ gtk_combo_box_init (GtkComboBox *combo_box)
priv->id_column = -1;
gtk_combo_box_check_appearance (combo_box);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (combo_box));
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
}
static void
@@ -1314,17 +1317,30 @@ gtk_combo_box_state_changed (GtkWidget *widget,
if (gtk_widget_get_realized (widget))
{
if (priv->tree_view && priv->cell_view)
- gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
- &gtk_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
+ {
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GdkRGBA *color;
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+
+ gtk_style_context_get (context, state,
+ "background-color", &color,
+ NULL);
+
+ gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
+ gdk_rgba_free (color);
+ }
}
gtk_widget_queue_draw (widget);
}
static void
-gtk_combo_box_button_state_changed (GtkWidget *widget,
- GtkStateType previous,
- gpointer data)
+gtk_combo_box_button_state_flags_changed (GtkWidget *widget,
+ GtkStateFlags previous,
+ gpointer data)
{
GtkComboBox *combo_box = GTK_COMBO_BOX (data);
GtkComboBoxPrivate *priv = combo_box->priv;
@@ -1332,14 +1348,9 @@ gtk_combo_box_button_state_changed (GtkWidget *widget,
if (gtk_widget_get_realized (widget))
{
if (!priv->tree_view && priv->cell_view)
- {
- if ((gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE) !=
- (gtk_widget_get_state (priv->cell_view) == GTK_STATE_INSENSITIVE))
- gtk_widget_set_sensitive (priv->cell_view, gtk_widget_get_sensitive (widget));
-
- gtk_widget_set_state (priv->cell_view,
- gtk_widget_get_state (widget));
- }
+ gtk_widget_set_state_flags (priv->cell_view,
+ gtk_widget_get_state_flags (widget),
+ TRUE);
}
gtk_widget_queue_draw (widget);
@@ -1388,8 +1399,7 @@ gtk_combo_box_check_appearance (GtkComboBox *combo_box)
}
static void
-gtk_combo_box_style_set (GtkWidget *widget,
- GtkStyle *previous)
+gtk_combo_box_style_updated (GtkWidget *widget)
{
GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
GtkComboBoxPrivate *priv = combo_box->priv;
@@ -1398,8 +1408,20 @@ gtk_combo_box_style_set (GtkWidget *widget,
gtk_combo_box_check_appearance (combo_box);
if (priv->tree_view && priv->cell_view)
- gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
- &gtk_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
+ {
+ GtkStyleContext *context;
+ GdkRGBA *color;
+
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_get (context, 0,
+ "background-color", &color,
+ NULL);
+
+ gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view),
+ color);
+
+ gdk_rgba_free (color);
+ }
child = gtk_bin_get_child (GTK_BIN (combo_box));
if (GTK_IS_ENTRY (child))
@@ -1718,6 +1740,24 @@ gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
}
static void
+get_widget_border (GtkWidget *widget,
+ GtkBorder *border)
+{
+ GtkStyleContext *context;
+ GtkBorder *border_width;
+
+ context = gtk_widget_get_style_context (widget);
+
+ gtk_style_context_get (context,
+ gtk_widget_get_state_flags (widget),
+ "border-width", &border_width,
+ NULL);
+
+ *border = *border_width;
+ gtk_border_free (border_width);
+}
+
+static void
gtk_combo_box_menu_position_below (GtkMenu *menu,
gint *x,
gint *y,
@@ -1732,7 +1772,8 @@ gtk_combo_box_menu_position_below (GtkMenu *menu,
GdkScreen *screen;
gint monitor_num;
GdkRectangle monitor;
-
+ GtkBorder border;
+
/* FIXME: is using the size request here broken? */
child = gtk_bin_get_child (GTK_BIN (combo_box));
@@ -1748,9 +1789,8 @@ gtk_combo_box_menu_position_below (GtkMenu *menu,
gdk_window_get_root_coords (gtk_widget_get_window (child),
sx, sy, &sx, &sy);
-
- if (GTK_SHADOW_NONE != combo_box->priv->shadow_type)
- sx -= gtk_widget_get_style (GTK_WIDGET (combo_box))->xthickness;
+ get_widget_border (GTK_WIDGET (combo_box), &border);
+ sx -= border.left;
if (combo_box->priv->popup_fixed_width)
gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
@@ -2440,13 +2480,13 @@ gtk_combo_box_update_requested_width (GtkComboBox *combo_box,
&req, NULL); \
\
if (is_rtl) \
- child.x = allocation->x + shadow_width; \
+ child.x = allocation->x + border.right; \
else \
- child.x = allocation->x + allocation->width - req.width - shadow_width; \
+ child.x = allocation->x + allocation->width - req.width - border.left; \
\
- child.y = allocation->y + shadow_height; \
+ child.y = allocation->y + border.top; \
child.width = req.width; \
- child.height = allocation->height - 2 * shadow_height; \
+ child.height = allocation->height - (border.top + border.bottom); \
child.width = MAX (1, child.width); \
child.height = MAX (1, child.height); \
\
@@ -2459,54 +2499,40 @@ gtk_combo_box_size_allocate (GtkWidget *widget,
GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
GtkComboBoxPrivate *priv = combo_box->priv;
GtkWidget *child_widget;
- gint shadow_width, shadow_height;
gint focus_width, focus_pad;
GtkAllocation child;
GtkRequisition req;
- GtkStyle *style;
+ GtkBorder border;
gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
gtk_widget_set_allocation (widget, allocation);
child_widget = gtk_bin_get_child (GTK_BIN (widget));
+ get_widget_border (widget, &border);
- style = gtk_widget_get_style (widget);
gtk_widget_style_get (widget,
"focus-line-width", &focus_width,
"focus-padding", &focus_pad,
NULL);
- if (GTK_SHADOW_NONE != priv->shadow_type)
- {
- shadow_width = style->xthickness;
- shadow_height = style->ythickness;
- }
- else
- {
- shadow_width = 0;
- shadow_height = 0;
- }
-
if (!priv->tree_view)
{
if (priv->cell_view)
{
- gint xthickness, ythickness;
+ GtkBorder button_border;
gint width;
guint border_width;
/* menu mode */
- allocation->x += shadow_width;
- allocation->y += shadow_height;
- allocation->width -= 2 * shadow_width;
- allocation->height -= 2 * shadow_height;
+ allocation->x += border.left;
+ allocation->y += border.top;
+ allocation->width -= border.left + border.right;
+ allocation->height -= border.top + border.bottom;
gtk_widget_size_allocate (priv->button, allocation);
/* set some things ready */
border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->button));
- style = gtk_widget_get_style (priv->button);
- xthickness = style->xthickness;
- ythickness = style->ythickness;
+ get_widget_border (priv->button, &button_border);
child.x = allocation->x;
child.y = allocation->y;
@@ -2515,10 +2541,12 @@ gtk_combo_box_size_allocate (GtkWidget *widget,
if (!priv->is_cell_renderer)
{
- child.x += border_width + xthickness + focus_width + focus_pad;
- child.y += border_width + ythickness + focus_width + focus_pad;
- width -= 2 * (child.x - allocation->x);
- child.height -= 2 * (child.y - allocation->y);
+ child.x += border_width + button_border.left + focus_width + focus_pad;
+ child.y += border_width + button_border.top + focus_width + focus_pad;
+ width -= (2 * (border_width + focus_width + focus_pad)) +
+ button_border.left + button_border.right;
+ child.height -= (2 * (border_width + focus_width + focus_pad)) +
+ button_border.top + button_border.bottom;
}
@@ -2544,14 +2572,14 @@ gtk_combo_box_size_allocate (GtkWidget *widget,
{
child.x += req.width;
child.width = allocation->x + allocation->width
- - (border_width + xthickness + focus_width + focus_pad)
+ - (border_width + button_border.right + focus_width + focus_pad)
- child.x;
}
else
{
child.width = child.x;
child.x = allocation->x
- + border_width + xthickness + focus_width + focus_pad;
+ + border_width + button_border.left + focus_width + focus_pad;
child.width -= child.x;
}
@@ -2589,11 +2617,11 @@ gtk_combo_box_size_allocate (GtkWidget *widget,
GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
if (is_rtl)
- child.x = allocation->x + req.width + shadow_width;
+ child.x = allocation->x + req.width + border.right;
else
- child.x = allocation->x + shadow_width;
- child.y = allocation->y + shadow_height;
- child.width = allocation->width - req.width - 2 * shadow_width;
+ child.x = allocation->x + border.left;
+ child.y = allocation->y + border.top;
+ child.width = allocation->width - req.width - (border.left + border.right);
child.width = MAX (1, child.width);
child.height = MAX (1, child.height);
gtk_widget_size_allocate (child_widget, &child);
@@ -2602,11 +2630,7 @@ gtk_combo_box_size_allocate (GtkWidget *widget,
else
{
/* list mode */
-
- /* Combobox thickness + border-width */
guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
- int delta_x = shadow_width + border_width;
- int delta_y = shadow_height + border_width;
/* button */
GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
@@ -2623,32 +2647,32 @@ gtk_combo_box_size_allocate (GtkWidget *widget,
if (priv->cell_view_frame)
{
- child.x += delta_x;
- child.y += delta_y;
- child.width = MAX (1, child.width - delta_x * 2);
- child.height = MAX (1, child.height - delta_y * 2);
+ child.x += border.left + border_width;
+ child.y += border.top + border_width;
+ child.width = MAX (1, child.width - (2 * border_width) - (border.left + border.right));
+ child.height = MAX (1, child.height - (2 * border_width) - (border.top + border.bottom));
gtk_widget_size_allocate (priv->cell_view_frame, &child);
/* the sample */
if (priv->has_frame)
{
+ GtkBorder frame_border;
+
border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
- style = gtk_widget_get_style (priv->cell_view_frame);
- delta_x = border_width + style->xthickness;
- delta_y = border_width + style->ythickness;
-
- child.x += delta_x;
- child.y += delta_y;
- child.width -= delta_x * 2;
- child.height -= delta_y * 2;
+ get_widget_border (priv->cell_view_frame, &frame_border);
+
+ child.x += border_width + frame_border.left;
+ child.y += border_width + frame_border.right;
+ child.width -= (2 * border_width) + frame_border.left + frame_border.right;
+ child.height -= (2 * border_width) + frame_border.top + frame_border.bottom;
}
}
else
{
- child.x += delta_x;
- child.y += delta_y;
- child.width -= delta_x * 2;
- child.height -= delta_y * 2;
+ child.x += border.left + border_width;
+ child.y += border.top + border_width;
+ child.width -= (2 * border_width) - (border.left + border.right);
+ child.height -= (2 * border_width) - (border.top + border.bottom);
}
if (gtk_widget_get_visible (priv->popup_window))
@@ -2762,11 +2786,17 @@ gtk_combo_box_draw (GtkWidget *widget,
if (priv->shadow_type != GTK_SHADOW_NONE)
{
- gtk_paint_shadow (gtk_widget_get_style (widget),
- cr,
- GTK_STATE_NORMAL, priv->shadow_type,
- widget, "combobox",
- 0, 0,
+ GtkStyleContext *context;
+ GtkStateFlags state;
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_set_state (context, state);
+
+ gtk_render_background (context, cr, 0, 0,
+ gtk_widget_get_allocated_width (widget),
+ gtk_widget_get_allocated_height (widget));
+ gtk_render_frame (context, cr, 0, 0,
gtk_widget_get_allocated_width (widget),
gtk_widget_get_allocated_height (widget));
}
@@ -3108,8 +3138,8 @@ gtk_combo_box_menu_setup (GtkComboBox *combo_box,
g_signal_connect (priv->button, "button-press-event",
G_CALLBACK (gtk_combo_box_menu_button_press),
combo_box);
- g_signal_connect (priv->button, "state-changed",
- G_CALLBACK (gtk_combo_box_button_state_changed),
+ g_signal_connect (priv->button, "state-flags-changed",
+ G_CALLBACK (gtk_combo_box_button_state_flags_changed),
combo_box);
/* create our funky menu */
@@ -3274,7 +3304,7 @@ gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
g_signal_handlers_disconnect_matched (priv->button,
G_SIGNAL_MATCH_DATA,
0, 0, NULL,
- gtk_combo_box_button_state_changed, combo_box);
+ gtk_combo_box_button_state_flags_changed, combo_box);
/* unparent will remove our latest ref */
gtk_widget_unparent (priv->button);
@@ -3920,7 +3950,6 @@ gtk_combo_box_list_setup (GtkComboBox *combo_box)
{
GtkComboBoxPrivate *priv = combo_box->priv;
GtkTreeSelection *sel;
- GtkStyle *style;
GtkWidget *child;
GtkWidget *widget = GTK_WIDGET (combo_box);
@@ -3940,9 +3969,19 @@ gtk_combo_box_list_setup (GtkComboBox *combo_box)
if (priv->cell_view)
{
- style = gtk_widget_get_style (widget);
- gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
- &style->base[gtk_widget_get_state (widget)]);
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GdkRGBA *color;
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+
+ gtk_style_context_get (context, state,
+ "background-color", &color,
+ NULL);
+
+ gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
+ gdk_rgba_free (color);
priv->box = gtk_event_box_new ();
gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
@@ -6377,7 +6416,6 @@ gtk_combo_box_get_preferred_width (GtkWidget *widget,
{
GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
GtkComboBoxPrivate *priv = combo_box->priv;
- GtkStyle *style;
gint focus_width, focus_pad;
gint font_size, arrow_size;
PangoContext *context;
@@ -6386,6 +6424,9 @@ gtk_combo_box_get_preferred_width (GtkWidget *widget,
GtkWidget *child;
gint minimum_width, natural_width;
gint child_min, child_nat;
+ GtkStyleContext *style_context;
+ GtkStateFlags state;
+ GtkBorder *border;
child = gtk_bin_get_child (GTK_BIN (widget));
@@ -6402,13 +6443,21 @@ gtk_combo_box_get_preferred_width (GtkWidget *widget,
"arrow-size", &arrow_size,
NULL);
- font_desc = gtk_widget_get_style (child)->font_desc;
+ style_context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+
+ gtk_style_context_get (style_context, state,
+ "font", &font_desc,
+ "border-width", &border,
+ NULL);
+
context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
metrics = pango_context_get_metrics (context, font_desc,
pango_context_get_language (context));
font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
pango_font_metrics_get_descent (metrics));
pango_font_metrics_unref (metrics);
+ pango_font_description_free (font_desc);
arrow_size = MAX (arrow_size, font_size);
@@ -6421,15 +6470,17 @@ gtk_combo_box_get_preferred_width (GtkWidget *widget,
if (priv->cell_view)
{
gint sep_width, arrow_width;
- gint border_width, xthickness, xpad;
+ gint border_width, xpad;
+ GtkBorder button_border;
border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
- xthickness = gtk_widget_get_style (priv->button)->xthickness;
+ get_widget_border (priv->button, &button_border);
gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
- xpad = 2*(border_width + xthickness + focus_width + focus_pad);
+ xpad = 2 * (border_width + focus_width + focus_pad) +
+ button_border.left + button_border.right;
minimum_width = child_min + sep_width + arrow_width + xpad;
natural_width = child_nat + sep_width + arrow_width + xpad;
@@ -6461,8 +6512,12 @@ gtk_combo_box_get_preferred_width (GtkWidget *widget,
{
if (priv->has_frame)
{
- gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
- gint xpad = 2 * (border_width + gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame))->xthickness);
+ gint border_width, xpad;
+ GtkBorder frame_border;
+
+ border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
+ get_widget_border (priv->cell_view_frame, &frame_border);
+ xpad = (2 * border_width) + frame_border.left + frame_border.right;
minimum_width += xpad;
natural_width += xpad;
@@ -6477,13 +6532,9 @@ gtk_combo_box_get_preferred_width (GtkWidget *widget,
natural_width += button_nat_width;
}
- if (GTK_SHADOW_NONE != priv->shadow_type)
- {
- style = gtk_widget_get_style (GTK_WIDGET (widget));
-
- minimum_width += 2 * style->xthickness;
- natural_width += 2 * style->xthickness;
- }
+ minimum_width += border->left + border->right;
+ natural_width += border->left + border->right;
+ gtk_border_free (border);
if (minimum_size)
*minimum_size = minimum_width;
@@ -6525,36 +6576,31 @@ gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
{
GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
GtkComboBoxPrivate *priv = combo_box->priv;
- GtkStyle *style;
gint focus_width, focus_pad;
gint min_height, nat_height;
gint size;
+ GtkBorder border;
gtk_widget_style_get (GTK_WIDGET (widget),
"focus-line-width", &focus_width,
"focus-padding", &focus_pad,
NULL);
- size = avail_size;
-
- if (GTK_SHADOW_NONE != priv->shadow_type)
- size -= gtk_widget_get_style (GTK_WIDGET (widget))->xthickness;
+ get_widget_border (widget, &border);
+ size = avail_size - border.left;
if (!priv->tree_view)
{
/* menu mode */
if (priv->cell_view)
{
- GtkStyle *button_style;
/* calculate x/y padding and separator/arrow size */
gint sep_width, arrow_width, sep_height, arrow_height;
- gint border_width, xthickness, ythickness, xpad, ypad;
+ gint border_width, xpad, ypad;
+ GtkBorder button_border;
border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
- button_style = gtk_widget_get_style (priv->button);
-
- xthickness = button_style->xthickness;
- ythickness = button_style->ythickness;
+ get_widget_border (priv->button, &button_border);
gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
@@ -6563,8 +6609,10 @@ gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
gtk_widget_get_preferred_height_for_width (priv->arrow,
arrow_width, &arrow_height, NULL);
- xpad = 2*(border_width + xthickness + focus_width + focus_pad);
- ypad = 2*(border_width + ythickness + focus_width + focus_pad);
+ xpad = 2 * (border_width + focus_width + focus_pad) +
+ button_border.left + button_border.right;
+ ypad = 2 * (border_width + focus_width + focus_pad) +
+ button_border.top + button_border.bottom;
size -= sep_width + arrow_width + xpad;
@@ -6606,14 +6654,14 @@ gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
if (priv->cell_view_frame && priv->has_frame)
{
- GtkStyle *cell_style;
+ GtkBorder frame_border;
gint border_width;
border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
- cell_style = gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame));
+ get_widget_border (GTK_WIDGET (priv->cell_view_frame), &frame_border);
- xpad = 2 * (border_width + cell_style->xthickness);
- ypad = 2 * (border_width + cell_style->ythickness);
+ xpad = (2 * border_width) + border.left + frame_border.right;
+ ypad = (2 * border_width) + border.top + frame_border.bottom;
}
size -= but_width;
@@ -6629,13 +6677,8 @@ gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
nat_height += ypad;
}
- if (GTK_SHADOW_NONE != priv->shadow_type)
- {
- style = gtk_widget_get_style (GTK_WIDGET (widget));
-
- min_height += 2 * style->ythickness;
- nat_height += 2 * style->ythickness;
- }
+ min_height += border.top + border.bottom;
+ nat_height += border.top + border.bottom;
if (minimum_size)
*minimum_size = min_height;
diff --git a/gtk/gtkcontainer.c b/gtk/gtkcontainer.c
index a37e361d65..5433766c25 100644
--- a/gtk/gtkcontainer.c
+++ b/gtk/gtkcontainer.c
@@ -334,6 +334,9 @@ static void gtk_container_adjust_size_allocation (GtkWidget *widget,
static gchar* gtk_container_child_default_composite_name (GtkContainer *container,
GtkWidget *child);
+static GtkWidgetPath * gtk_container_real_get_path_for_child (GtkContainer *container,
+ GtkWidget *child);
+
/* GtkBuildable */
static void gtk_container_buildable_init (GtkBuildableIface *iface);
static void gtk_container_buildable_add_child (GtkBuildable *buildable,
@@ -465,6 +468,7 @@ gtk_container_class_init (GtkContainerClass *class)
class->set_focus_child = gtk_container_real_set_focus_child;
class->child_type = NULL;
class->composite_name = gtk_container_child_default_composite_name;
+ class->get_path_for_child = gtk_container_real_get_path_for_child;
g_object_class_install_property (gobject_class,
PROP_RESIZE_MODE,
@@ -2207,6 +2211,34 @@ gtk_container_get_all_children (GtkContainer *container)
return children;
}
+static GtkWidgetPath *
+gtk_container_real_get_path_for_child (GtkContainer *container,
+ GtkWidget *child)
+{
+ GtkStyleContext *context;
+ GtkWidgetPath *path;
+ GList *classes;
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (container));
+ path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (container)));
+
+ /* Copy any permanent classes to the path */
+ classes = gtk_style_context_list_classes (context);
+
+ while (classes)
+ {
+ GList *cur;
+
+ cur = classes;
+ classes = classes->next;
+
+ gtk_widget_path_iter_add_class (path, -1, cur->data);
+ g_list_free_1 (cur);
+ }
+
+ return path;
+}
+
static gboolean
gtk_container_focus (GtkWidget *widget,
GtkDirectionType direction)
@@ -3220,3 +3252,24 @@ _gtk_container_get_reallocate_redraws (GtkContainer *container)
{
return container->priv->reallocate_redraws;
}
+
+/**
+ * gtk_container_get_path_for_child:
+ * @container: a #GtkContainer
+ * @child: a child of @container
+ *
+ * Returns a newly created widget path representing all the widget hierarchy
+ * from the toplevel down to @child (this one not being included).
+ *
+ * Returns: A newly created #GtkWidgetPath
+ **/
+GtkWidgetPath *
+gtk_container_get_path_for_child (GtkContainer *container,
+ GtkWidget *child)
+{
+ g_return_val_if_fail (GTK_IS_CONTAINER (container), NULL);
+ g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
+ g_return_val_if_fail (container == (GtkContainer *) gtk_widget_get_parent (child), NULL);
+
+ return GTK_CONTAINER_GET_CLASS (container)->get_path_for_child (container, child);
+}
diff --git a/gtk/gtkcontainer.h b/gtk/gtkcontainer.h
index f469d89eba..94a8502c8f 100644
--- a/gtk/gtkcontainer.h
+++ b/gtk/gtkcontainer.h
@@ -88,6 +88,8 @@ struct _GtkContainerClass
guint property_id,
GValue *value,
GParamSpec *pspec);
+ GtkWidgetPath * (*get_path_for_child) (GtkContainer *container,
+ GtkWidget *child);
/* Padding for future expansion */
void (*_gtk_reserved1) (void);
@@ -228,6 +230,9 @@ void _gtk_container_set_need_resize (GtkContainer *container,
gboolean need_resize);
gboolean _gtk_container_get_reallocate_redraws (GtkContainer *container);
+GtkWidgetPath * gtk_container_get_path_for_child (GtkContainer *container,
+ GtkWidget *child);
+
G_END_DECLS
#endif /* __GTK_CONTAINER_H__ */
diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c
new file mode 100644
index 0000000000..5ab102a85e
--- /dev/null
+++ b/gtk/gtkcssprovider.c
@@ -0,0 +1,3847 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <gtkstyleprovider.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <cairo-gobject.h>
+
+#include "gtkanimationdescription.h"
+#include "gtk9slice.h"
+#include "gtkcssprovider.h"
+
+/**
+ * SECTION:gtkcssprovider
+ * @Short_description: CSS-like styling for widgets
+ * @Title: GtkCssProvider
+ * @See_also: #GtkStyleContext, #GtkStyleProvider
+ *
+ * GtkCssProvider is an object implementing the #GtkStyleProvider interface.
+ * It is able to parse <ulink url="http://www.w3.org/TR/CSS2">CSS</ulink>-like
+ * input in order to style widgets.
+ *
+ * <refsect2 id="gtkcssprovider-files">
+ * <title>Default files</title>
+ * <para>
+ * An application can cause GTK+ to parse a specific CSS style sheet by
+ * calling gtk_css_provider_load_from_file() and adding the provider with
+ * gtk_style_context_add_provider() or gtk_style_context_add_provider_for_screen().
+ * In addition, certain files will be read when GTK+ is initialized. First,
+ * the file <filename><replaceable>XDG_CONFIG_HOME</replaceable>/gtk-3.0/gtk.css</filename>
+ * is loaded if it exists. Then, GTK+ tries to load
+ * <filename><replaceable>HOME</replaceable>/.themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
+ * falling back to
+ * <filename><replaceable>GTK_DATA_PREFIX</replaceable>/share/themes/<replaceable>theme-name</replaceable>/gtk-3.0/gtk.css</filename>,
+ * where <replaceable>theme-name</replaceable> is the name of the current theme
+ * (see the #GtkSettings:gtk-theme-name setting) and <replaceable>GTK_DATA_PREFIX</replaceable>
+ * is the prefix configured when GTK+ was compiled, unless overridden by the
+ * <envar>GTK_DATA_PREFIX</envar> environment variable.
+ * </para>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-stylesheets">
+ * <title>Style sheets</title>
+ * <para>
+ * The basic structure of the style sheets understood by this provider is
+ * a series of statements, which are either rule sets or '@-rules', separated
+ * by whitespace.
+ * </para>
+ * <para>
+ * A rule set consists of a selector and a declaration block, which is
+ * a series of declarations enclosed in curly braces ({ and }). The
+ * declarations are separated by semicolons (;). Multiple selectors can
+ * share the same declaration block, by putting all the separators in
+ * front of the block, separated by commas.
+ * </para>
+ * <example><title>A rule set with two selectors</title>
+ * <programlisting language="text">
+ * GtkButton, GtkEntry {
+ * color: &num;ff00ea;
+ * font: Comic Sans 12
+ * }
+ * </programlisting>
+ * </example>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-selectors">
+ * <title>Selectors</title>
+ * <para>
+ * Selectors work very similar to the way they do in CSS, with widget class
+ * names taking the role of element names, and widget names taking the role
+ * of IDs. When used in a selector, widget names must be prefixed with a
+ * '&num;' character. The '*' character represents the so-called universal
+ * selector, which matches any widget.
+ * </para>
+ * <para>
+ * To express more complicated situations, selectors can be combined in
+ * various ways:
+ * <itemizedlist>
+ * <listitem><para>To require that a widget satisfies several conditions,
+ * combine several selectors into one by concatenating them. E.g.
+ * <literal>GtkButton&num;button1</literal> matches a GtkButton widget
+ * with the name button1.</para></listitem>
+ * <listitem><para>To only match a widget when it occurs inside some other
+ * widget, write the two selectors after each other, separated by whitespace.
+ * E.g. <literal>GtkToolBar GtkButton</literal> matches GtkButton widgets
+ * that occur inside a GtkToolBar.</para></listitem>
+ * <listitem><para>In the previous example, the GtkButton is matched even
+ * if it occurs deeply nested inside the toolbar. To restrict the match
+ * to direct children of the parent widget, insert a '>' character between
+ * the two selectors. E.g. <literal>GtkNotebook > GtkLabel</literal> matches
+ * GtkLabel widgets that are direct children of a GtkNotebook.</para></listitem>
+ * </itemizedlist>
+ * </para>
+ * <example>
+ * <title>Widget classes and names in selectors</title>
+ * <programlisting language="text">
+ * /&ast; Theme labels that are descendants of a window &ast;/
+ * GtkWindow GtkLabel {
+ * background-color: &num;898989
+ * }
+ *
+ * /&ast; Theme notebooks, and anything that's within these &ast;/
+ * GtkNotebook {
+ * background-color: &num;a939f0
+ * }
+ *
+ * /&ast; Theme combo boxes, and entries that
+ * are direct children of a notebook &ast;/
+ * GtkComboBox,
+ * GtkNotebook > GtkEntry {
+ * color: @fg_color;
+ * background-color: &num;1209a2
+ * }
+ *
+ * /&ast; Theme any widget within a GtkBin &ast;/
+ * GtkBin * {
+ * font-name: Sans 20
+ * }
+ *
+ * /&ast; Theme a label named title-label &ast;/
+ * GtkLabel&num;title-label {
+ * font-name: Sans 15
+ * }
+ *
+ * /&ast; Theme any widget named main-entry &ast;/
+ * &num;main-entry {
+ * background-color: &num;f0a810
+ * }
+ * </programlisting>
+ * </example>
+ * <para>
+ * Widgets may also define style classes, which can be used for matching.
+ * When used in a selector, style classes must be prefixed with a '.'
+ * character.
+ * </para>
+ * <para>
+ * Refer to the documentation of individual widgets to learn which
+ * style classes they define and see <xref linkend="gtkstylecontext-classes"/>
+ * for a list of all style classes used by GTK+ widgets.
+ * </para>
+ * <example>
+ * <title>Style classes in selectors</title>
+ * <programlisting language="text">
+ * /&ast; Theme all widgets defining the class entry &ast;/
+ * .entry {
+ * color: &num;39f1f9;
+ * }
+ *
+ * /&ast; Theme spinbuttons' entry &ast;/
+ * GtkSpinButton.entry {
+ * color: &num;900185
+ * }
+ * </programlisting>
+ * </example>
+ * <para>
+ * In complicated widgets like e.g. a GtkNotebook, it may be desirable
+ * to style different parts of the widget differently. To make this
+ * possible, container widgets may define regions, whose names
+ * may be used for matching in selectors.
+ * </para>
+ * <para>
+ * Some containers allow to further differentiate between regions by
+ * applying so-called pseudo-classes to the region. For example, the
+ * tab region in GtkNotebook allows to single out the first or last
+ * tab by using the :first-child or :last-child pseudo-class.
+ * When used in selectors, pseudo-classes must be prefixed with a
+ * ':' character.
+ * </para>
+ * <para>
+ * Refer to the documentation of individual widgets to learn which
+ * regions and pseudo-classes they define and see
+ * <xref linkend="gtkstylecontext-classes"/> for a list of all regions
+ * used by GTK+ widgets.
+ * </para>
+ * <example>
+ * <title>Regions in selectors</title>
+ * <programlisting language="text">
+ * /&ast; Theme any label within a notebook &ast;/
+ * GtkNotebook GtkLabel {
+ * color: &num;f90192;
+ * }
+ *
+ * /&ast; Theme labels within notebook tabs &ast;/
+ * GtkNotebook tab GtkLabel {
+ * color: &num;703910;
+ * }
+ *
+ * /&ast; Theme labels in the any first notebook
+ * tab, both selectors are equivalent &ast;/
+ * GtkNotebook tab:nth-child(first) GtkLabel,
+ * GtkNotebook tab:first-child GtkLabel {
+ * color: &num;89d012;
+ * }
+ * </programlisting>
+ * </example>
+ * <para>
+ * Another use of pseudo-classes is to match widgets depending on their
+ * state. This is conceptually similar to the :hover, :active or :focus
+ * pseudo-classes in CSS. The available pseudo-classes for widget states
+ * are :active, :prelight (or :hover), :insensitive, :selected, :focused
+ * and :inconsistent.
+ * </para>
+ * <example>
+ * <title>Styling specific widget states</title>
+ * <programlisting language="text">
+ * /&ast; Theme active (pressed) buttons &ast;/
+ * GtkButton:active {
+ * background-color: &num;0274d9;
+ * }
+ *
+ * /&ast; Theme buttons with the mouse pointer on it,
+ * both are equivalent &ast;/
+ * GtkButton:hover,
+ * GtkButton:prelight {
+ * background-color: &num;3085a9;
+ * }
+ *
+ * /&ast; Theme insensitive widgets, both are equivalent &ast;/
+ * :insensitive,
+ * *:insensitive {
+ * background-color: &num;320a91;
+ * }
+ *
+ * /&ast; Theme selection colors in entries &ast;/
+ * GtkEntry:selected {
+ * background-color: &num;56f9a0;
+ * }
+ *
+ * /&ast; Theme focused labels &ast;/
+ * GtkLabel:focused {
+ * background-color: &num;b4940f;
+ * }
+ *
+ * /&ast; Theme inconsistent checkbuttons &ast;/
+ * GtkCheckButton:inconsistent {
+ * background-color: &num;20395a;
+ * }
+ * </programlisting>
+ * </example>
+ * <para>
+ * Widget state pseudoclasses may only apply to the last element
+ * in a selector.
+ * </para>
+ * <para>
+ * To determine the effective style for a widget, all the matching rule
+ * sets are merged. As in CSS, rules apply by specificity, so the rules
+ * whose selectors more closely match a widget path will take precedence
+ * over the others.
+ * </para>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-rules">
+ * <title>&commat; Rules</title>
+ * <para>
+ * GTK+'s CSS supports the &commat;import rule, in order to load another
+ * CSS style sheet in addition to the currently parsed one.
+ * </para>
+ * <example>
+ * <title>Using the &commat;import rule</title>
+ * <programlisting language="text">
+ * &commat;import url ("path/to/common.css");
+ * </programlisting>
+ * </example>
+ * <para>
+ * GTK+ also supports an additional &commat;define-color rule, in order
+ * to define a color name which may be used instead of color numeric
+ * representations. Also see the #GtkSettings:gtk-color-scheme setting
+ * for a way to override the values of these named colors.
+ * </para>
+ * <example>
+ * <title>Defining colors</title>
+ * <programlisting language="text">
+ * &commat;define-color bg_color &num;f9a039;
+ *
+ * &ast; {
+ * background-color: &commat;bg_color;
+ * }
+ * </programlisting>
+ * </example>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-symbolic-colors">
+ * <title>Symbolic colors</title>
+ * <para>
+ * Besides being able to define color names, the CSS parser is also able
+ * to read different color expressions, which can also be nested, providing
+ * a rich language to define colors which are derived from a set of base
+ * colors.
+ * </para>
+ * <example>
+ * <title>Using symbolic colors</title>
+ * <programlisting language="text">
+ * &commat;define-color entry-color shade (&commat;bg_color, 0.7);
+ *
+ * GtkEntry {
+ * background-color: @entry-color;
+ * }
+ *
+ * GtkEntry:focused {
+ * background-color: mix (&commat;entry-color,
+ * shade (&num;fff, 0.5),
+ * 0.8);
+ * }
+ * </programlisting>
+ * </example>
+ * <para>
+ * The various ways to express colors in GTK+ CSS are:
+ * </para>
+ * <informaltable>
+ * <tgroup cols="3">
+ * <thead>
+ * <row>
+ * <entry>Syntax</entry>
+ * <entry>Explanation</entry>
+ * <entry>Examples</entry>
+ * </row>
+ * </thead>
+ * <tbody>
+ * <row>
+ * <entry>rgb(@r, @g, @b)</entry>
+ * <entry>An opaque color; @r, @g, @b can be either integers between
+ * 0 and 255 or percentages</entry>
+ * <entry><literallayout>rgb(128, 10, 54)
+ * rgb(20%, 30%, 0%)</literallayout></entry>
+ * </row>
+ * <row>
+ * <entry>rgba(@r, @g, @b, @a)</entry>
+ * <entry>A translucent color; @r, @g, @b are as in the previous row,
+ * @a is a floating point number between 0 and 1</entry>
+ * <entry><literallayout>rgba(255, 255, 0, 0.5)</literallayout></entry>
+ * </row>
+ * <row>
+ * <entry>&num;@xxyyzz</entry>
+ * <entry>An opaque color; @xx, @yy, @zz are hexadecimal numbers
+ * specifying @r, @g, @b variants with between 1 and 4
+ * hexadecimal digits per component are allowed</entry>
+ * <entry><literallayout>&num;ff12ab
+ * &num;f0c</literallayout></entry>
+ * </row>
+ * <row>
+ * <entry>&commat;name</entry>
+ * <entry>Reference to a color that has been defined with
+ * &commat;define-color
+ * </entry>
+ * <entry>&commat;bg_color</entry>
+ * </row>
+ * <row>
+ * <entry>mix(@color1, @color2, @f)</entry>
+ * <entry>A linear combination of @color1 and @color2. @f is a
+ * floating point number between 0 and 1.</entry>
+ * <entry><literallayout>mix(&num;ff1e0a, &commat;bg_color, 0.8)</literallayout></entry>
+ * </row>
+ * <row>
+ * <entry>shade(@color, @f)</entry>
+ * <entry>A lighter or darker variant of @color. @f is a
+ * floating point number.
+ * </entry>
+ * <entry>shade(&commat;fg_color, 0.5)</entry>
+ * </row>
+ * <row>
+ * <entry>lighter(@color)</entry>
+ * <entry>A lighter variant of @color</entry>
+ * </row>
+ * <row>
+ * <entry>darker(@color)</entry>
+ * <entry>A darker variant of @color</entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-gradients">
+ * <title>Gradients</title>
+ * <para>
+ * Linear or radial Gradients can be used as background images.
+ * </para>
+ * <para>
+ * A linear gradient along the line from (@start_x, @start_y) to
+ * (@end_x, @end_y) is specified using the syntax
+ * <literallayout>-gtk-gradient (linear,
+ * @start_x @start_y, @end_x @end_y,
+ * color-stop (@position, @color),
+ * ...)</literallayout>
+ * where @start_x and @end_x can be either a floating point number between
+ * 0 and 1 or one of the special values 'left', 'right' or 'center', @start_y
+ * and @end_y can be either a floating point number between 0 and 1 or one
+ * of the special values 'top', 'bottom' or 'center', @position is a floating
+ * point number between 0 and 1 and @color is a color expression (see above).
+ * The color-stop can be repeated multiple times to add more than one color
+ * stop. 'from (@color)' and 'to (@color)' can be used as abbreviations for
+ * color stops with position 0 and 1, respectively.
+ * </para>
+ * <example>
+ * <title>A linear gradient</title>
+ * <inlinegraphic fileref="gradient1.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (linear,
+ * left top, right bottom,
+ * from(&commat;yellow), to(&commat;blue))</literallayout></para>
+ * </example>
+ * <example>
+ * <title>Another linear gradient</title>
+ * <inlinegraphic fileref="gradient2.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (linear,
+ * 0 0, 0 1,
+ * color-stop(0, &commat;yellow),
+ * color-stop(0.2, &commat;blue),
+ * color-stop(1, &num;0f0))</literallayout></para>
+ * </example>
+ * <para>
+ * A radial gradient along the two circles defined by (@start_x, @start_y,
+ * @start_radius) and (@end_x, @end_y, @end_radius) is specified using the
+ * syntax
+ * <literallayout>-gtk-gradient (radial,
+ * @start_x @start_y, @start_radius,
+ * @end_x @end_y, @end_radius,
+ * color-stop (@position, @color),
+ * ...)</literallayout>
+ * where @start_radius and @end_radius are floating point numbers and
+ * the other parameters are as before.
+ * </para>
+ * <example>
+ * <title>A radial gradient</title>
+ * <inlinegraphic fileref="gradient3.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (radial,
+ * center center, 0,
+ * center center, 1,
+ * from(&commat;yellow), to(&commat;green))</literallayout></para>
+ * </example>
+ * <example>
+ * <title>Another radial gradient</title>
+ * <inlinegraphic fileref="gradient4.png" format="PNG"/>
+ * <para>This gradient was specified with
+ * <literallayout>-gtk-gradient (radial,
+ * 0.4 0.4, 0.1,
+ * 0.6 0.6, 0.7,
+ * color-stop (0, &num;f00),
+ * color-stop (0.1, &num;a0f),
+ * color-stop (0.2, &commat;yellow),
+ * color-stop (1, &commat;green))</literallayout></para>
+ * </example>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-slices">
+ * <title>Border images</title>
+ * <para>
+ * Images can be used in 'slices' for the purpose of creating scalable
+ * borders.
+ * </para>
+ * <inlinegraphic fileref="slices.png" format="PNG"/>
+ * <para>
+ * The syntax for specifying border images of this kind is:
+ * <literallayout>url(@path) @top @right @bottom @left [repeat|stretch]? [repeat|stretch]?</literallayout>
+ * The sizes of the 'cut off' portions are specified
+ * with the @top, @right, @bottom and @left parameters.
+ * The 'middle' sections can be repeated or stretched to create
+ * the desired effect, by adding the 'repeat' or 'stretch' options after
+ * the dimensions. If two options are specified, the first one affects
+ * the horizontal behaviour and the second one the vertical behaviour.
+ * If only one option is specified, it affects both.
+ * </para>
+ * <example>
+ * <title>A border image</title>
+ * <inlinegraphic fileref="border1.png" format="PNG"/>
+ * <para>This border image was specified with
+ * <literallayout>url("gradient1.png") 10 10 10 10</literallayout>
+ * </para>
+ * </example>
+ * <example>
+ * <title>A repeating border image</title>
+ * <inlinegraphic fileref="border2.png" format="PNG"/>
+ * <para>This border image was specified with
+ * <literallayout>url("gradient1.png") 10 10 10 10 repeat</literallayout>
+ * </para>
+ * </example>
+ * <example>
+ * <title>A stretched border image</title>
+ * <inlinegraphic fileref="border3.png" format="PNG"/>
+ * <para>This border image was specified with
+ * <literallayout>url("gradient1.png") 10 10 10 10 stretch</literallayout>
+ * </para>
+ * </example>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-transitions">
+ * <para>Styles can specify transitions that will be used to create a gradual
+ * change in the appearance when a widget state changes. The following
+ * syntax is used to specify transitions:
+ * <literallayout>@duration [s|ms] [linear|ease|ease-in|ease-out|ease-in-out] [loop]?</literallayout>
+ * The @duration is the amount of time that the animation will take for
+ * a complete cycle from start to end. If the loop option is given, the
+ * animation will be repated until the state changes again.
+ * The option after the duration determines the transition function from a
+ * small set of predefined functions.
+ * <figure><title>Linear transition</title>
+ * <graphic fileref="linear.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease transition</title>
+ * <graphic fileref="ease.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease-in-out transition</title>
+ * <graphic fileref="ease-in-out.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease-in transition</title>
+ * <graphic fileref="ease-in.png" format="PNG"/>
+ * </figure>
+ * <figure><title>Ease-out transition</title>
+ * <graphic fileref="ease-out.png" format="PNG"/>
+ * </figure>
+ * </para>
+ * </refsect2>
+ * <refsect2 id="gtkcssprovider-properties">
+ * <title>Supported properties</title>
+ * <para>
+ * Properties are the part that differ the most to common CSS,
+ * not all properties are supported (some are planned to be
+ * supported eventually, some others are meaningless or don't
+ * map intuitively in a widget based environment).
+ * </para>
+ * <para>
+ * There is also a difference in shorthand properties, for
+ * example in common CSS it is fine to define a font through
+ * the different @font-family, @font-style, @font-size
+ * properties, meanwhile in GTK+'s CSS only the canonical
+ * @font property is supported.
+ * </para>
+ * <para>
+ * The currently supported properties are:
+ * </para>
+ * <informaltable>
+ * <tgroup cols="4">
+ * <thead>
+ * <row>
+ * <entry>Property name</entry>
+ * <entry>Syntax</entry>
+ * <entry>Maps to</entry>
+ * <entry>Examples</entry>
+ * </row>
+ * </thead>
+ * <tbody>
+ * <row>
+ * <entry>engine</entry>
+ * <entry>engine-name</entry>
+ * <entry>#GtkThemingEngine</entry>
+ * <entry>engine: clearlooks;</entry>
+ * </row>
+ * <row>
+ * <entry>background-color</entry>
+ * <entry morerows="2">color (see above)</entry>
+ * <entry morerows="2">#GdkRGBA</entry>
+ * <entry morerows="2"><literallayout>background-color: &num;fff;
+ * color: &amp;color1;
+ * background-color: shade (&amp;color1, 0.5);
+ * color: mix (&amp;color1, &num;f0f, 0.8);</literallayout>
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>color</entry>
+ * </row>
+ * <row>
+ * <entry>border-color</entry>
+ * </row>
+ * <row>
+ * <entry>font</entry>
+ * <entry>@family [@style] [@size]</entry>
+ * <entry>#PangoFontDescription</entry>
+ * <entry>font: Sans 15;</entry>
+ * </row>
+ * <row>
+ * <entry>margin</entry>
+ * <entry morerows="1"><literallayout>@width
+ * @vertical_width @horizontal_width
+ * @top_width @horizontal_width @bottom_width
+ * @top_width @right_width @bottom_width @left_width</literallayout>
+ * </entry>
+ * <entry morerows="1">#GtkBorder</entry>
+ * <entry morerows="1"><literallayout>margin: 5;
+ * margin: 5 10;
+ * margin: 5 10 3;
+ * margin: 5 10 3 5;</literallayout>
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>padding</entry>
+ * </row>
+ * <row>
+ * <entry>background-image</entry>
+ * <entry><literallayout>gradient (see above) or
+ * url(@path)</literallayout></entry>
+ * <entry>#cairo_pattern_t</entry>
+ * <entry><literallayout>-gtk-gradient (linear,
+ * left top, right top,
+ * from (&num;fff), to (&num;000));
+ * -gtk-gradient (linear, 0.0 0.5, 0.5 1.0,
+ * from (&num;fff),
+ * color-stop (0.5, &num;f00),
+ * to (&num;000));
+ * -gtk-gradient (radial,
+ * center center, 0.2,
+ * center center, 0.8,
+ * color-stop (0.0, &num;fff),
+ * color-stop (1.0, &num;000));
+ * url ('background.png');</literallayout>
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>border-width</entry>
+ * <entry>integer</entry>
+ * <entry>#gint</entry>
+ * <entry>border-width: 5;</entry>
+ * </row>
+ * <row>
+ * <entry>border-radius</entry>
+ * <entry>integer</entry>
+ * <entry>#gint</entry>
+ * <entry>border-radius: 5;</entry>
+ * </row>
+ * <row>
+ * <entry>border-style</entry>
+ * <entry>[none|solid|inset|outset]</entry>
+ * <entry>#GtkBorderStyle</entry>
+ * <entry>border-style: solid;</entry>
+ * </row>
+ * <row>
+ * <entry>border-image</entry>
+ * <entry><literallayout>border image (see above)</literallayout></entry>
+ * <entry>internal use only</entry>
+ * <entry><literallayout>border-image: url("/path/to/image.png") 3 4 3 4 stretch;
+ * border-image: url("/path/to/image.png") 3 4 4 3 repeat stretch;</literallayout>
+ * </entry>
+ * </row>
+ * <row>
+ * <entry>transition</entry>
+ * <entry>transition (see above)</entry>
+ * <entry>internal use only</entry>
+ * <entry><literallayout>transition: 150ms ease-in-out;
+ * transition: 1s linear loop;</literallayout>
+ * </entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ * <para>
+ * GtkThemingEngines can register their own, engine-specific style properties
+ * with the function gtk_theming_engine_register_property(). These properties
+ * can be set in CSS like other properties, using a name of the form
+ * <literallayout>-<replaceable>namespace</replaceable>-<replaceable>name</replaceable></literallayout>, where <replaceable>namespace</replaceable> is typically
+ * the name of the theming engine, and <replaceable>name</replaceable> is the
+ * name of the property. Style properties that have been registered by widgets
+ * using gtk_widget_class_install_style_property() can also be set in this
+ * way, using the widget class name for <replaceable>namespace</replaceable>.
+ * </para>
+ * <example>
+ * <title>Using engine-specific style properties</title>
+ * <programlisting>
+ * * {
+ * engine: clearlooks;
+ * border-radius: 4;
+ * -GtkPaned-handle-size: 6;
+ * -clearlooks-colorize-scrollbar: false;
+ * }
+ * </programlisting>
+ * </example>
+ * </refsect2>
+ */
+
+typedef struct GtkCssProviderPrivate GtkCssProviderPrivate;
+typedef struct SelectorElement SelectorElement;
+typedef struct SelectorPath SelectorPath;
+typedef struct SelectorStyleInfo SelectorStyleInfo;
+typedef enum SelectorElementType SelectorElementType;
+typedef enum CombinatorType CombinatorType;
+typedef enum ParserScope ParserScope;
+typedef enum ParserSymbol ParserSymbol;
+
+enum SelectorElementType {
+ SELECTOR_TYPE_NAME,
+ SELECTOR_NAME,
+ SELECTOR_GTYPE,
+ SELECTOR_REGION,
+ SELECTOR_CLASS,
+ SELECTOR_GLOB
+};
+
+enum CombinatorType {
+ COMBINATOR_DESCENDANT, /* No direct relation needed */
+ COMBINATOR_CHILD /* Direct child */
+};
+
+struct SelectorElement
+{
+ SelectorElementType elem_type;
+ CombinatorType combinator;
+
+ union
+ {
+ GQuark name;
+ GType type;
+
+ struct
+ {
+ GQuark name;
+ GtkRegionFlags flags;
+ } region;
+ };
+};
+
+struct SelectorPath
+{
+ GSList *elements;
+ GtkStateFlags state;
+ guint ref_count;
+};
+
+struct SelectorStyleInfo
+{
+ SelectorPath *path;
+ GHashTable *style;
+};
+
+struct GtkCssProviderPrivate
+{
+ GScanner *scanner;
+ gchar *filename;
+
+ const gchar *buffer;
+ const gchar *value_pos;
+
+ GHashTable *symbolic_colors;
+
+ GPtrArray *selectors_info;
+
+ /* Current parser state */
+ GSList *state;
+ GSList *cur_selectors;
+ GHashTable *cur_properties;
+};
+
+enum ParserScope {
+ SCOPE_SELECTOR,
+ SCOPE_PSEUDO_CLASS,
+ SCOPE_NTH_CHILD,
+ SCOPE_DECLARATION,
+ SCOPE_VALUE
+};
+
+/* Extend GtkStateType, since these
+ * values are also used as symbols
+ */
+enum ParserSymbol {
+ /* Scope: pseudo-class */
+ SYMBOL_NTH_CHILD = GTK_STATE_FOCUSED + 1,
+ SYMBOL_FIRST_CHILD,
+ SYMBOL_LAST_CHILD,
+ SYMBOL_SORTED_CHILD,
+
+ /* Scope: nth-child */
+ SYMBOL_NTH_CHILD_EVEN,
+ SYMBOL_NTH_CHILD_ODD,
+ SYMBOL_NTH_CHILD_FIRST,
+ SYMBOL_NTH_CHILD_LAST
+};
+
+static void gtk_css_provider_finalize (GObject *object);
+static void gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface);
+
+static void scanner_apply_scope (GScanner *scanner,
+ ParserScope scope);
+static gboolean css_provider_parse_value (GtkCssProvider *css_provider,
+ const gchar *value_str,
+ GValue *value,
+ GError **error);
+static gboolean gtk_css_provider_load_from_path_internal (GtkCssProvider *css_provider,
+ const gchar *path,
+ gboolean reset,
+ GError **error);
+
+enum {
+ CSS_PROVIDER_PARSE_ERROR
+};
+
+
+GQuark
+gtk_css_provider_error_quark (void)
+{
+ return g_quark_from_static_string ("gtk-css-provider-error-quark");
+}
+
+G_DEFINE_TYPE_EXTENDED (GtkCssProvider, gtk_css_provider, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
+ gtk_css_style_provider_iface_init));
+
+static void
+gtk_css_provider_class_init (GtkCssProviderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_css_provider_finalize;
+
+ g_type_class_add_private (object_class, sizeof (GtkCssProviderPrivate));
+}
+
+static SelectorPath *
+selector_path_new (void)
+{
+ SelectorPath *path;
+
+ path = g_slice_new0 (SelectorPath);
+ path->ref_count = 1;
+
+ return path;
+}
+
+static SelectorPath *
+selector_path_ref (SelectorPath *path)
+{
+ path->ref_count++;
+ return path;
+}
+
+static void
+selector_path_unref (SelectorPath *path)
+{
+ path->ref_count--;
+
+ if (path->ref_count > 0)
+ return;
+
+ while (path->elements)
+ {
+ g_slice_free (SelectorElement, path->elements->data);
+ path->elements = g_slist_delete_link (path->elements, path->elements);
+ }
+
+ g_slice_free (SelectorPath, path);
+}
+
+static void
+selector_path_prepend_type (SelectorPath *path,
+ const gchar *type_name)
+{
+ SelectorElement *elem;
+ GType type;
+
+ elem = g_slice_new (SelectorElement);
+ elem->combinator = COMBINATOR_DESCENDANT;
+ type = g_type_from_name (type_name);
+
+ if (type == G_TYPE_INVALID)
+ {
+ elem->elem_type = SELECTOR_TYPE_NAME;
+ elem->name = g_quark_from_string (type_name);
+ }
+ else
+ {
+ elem->elem_type = SELECTOR_GTYPE;
+ elem->type = type;
+ }
+
+ path->elements = g_slist_prepend (path->elements, elem);
+}
+
+static void
+selector_path_prepend_glob (SelectorPath *path)
+{
+ SelectorElement *elem;
+
+ elem = g_slice_new (SelectorElement);
+ elem->elem_type = SELECTOR_GLOB;
+ elem->combinator = COMBINATOR_DESCENDANT;
+
+ path->elements = g_slist_prepend (path->elements, elem);
+}
+
+static void
+selector_path_prepend_region (SelectorPath *path,
+ const gchar *name,
+ GtkRegionFlags flags)
+{
+ SelectorElement *elem;
+
+ elem = g_slice_new (SelectorElement);
+ elem->combinator = COMBINATOR_DESCENDANT;
+ elem->elem_type = SELECTOR_REGION;
+
+ elem->region.name = g_quark_from_string (name);
+ elem->region.flags = flags;
+
+ path->elements = g_slist_prepend (path->elements, elem);
+}
+
+static void
+selector_path_prepend_name (SelectorPath *path,
+ const gchar *name)
+{
+ SelectorElement *elem;
+
+ elem = g_slice_new (SelectorElement);
+ elem->combinator = COMBINATOR_DESCENDANT;
+ elem->elem_type = SELECTOR_NAME;
+
+ elem->name = g_quark_from_string (name);
+
+ path->elements = g_slist_prepend (path->elements, elem);
+}
+
+static void
+selector_path_prepend_class (SelectorPath *path,
+ const gchar *name)
+{
+ SelectorElement *elem;
+
+ elem = g_slice_new (SelectorElement);
+ elem->combinator = COMBINATOR_DESCENDANT;
+ elem->elem_type = SELECTOR_CLASS;
+
+ elem->name = g_quark_from_string (name);
+
+ path->elements = g_slist_prepend (path->elements, elem);
+}
+
+static void
+selector_path_prepend_combinator (SelectorPath *path,
+ CombinatorType combinator)
+{
+ SelectorElement *elem;
+
+ g_assert (path->elements != NULL);
+
+ /* It is actually stored in the last element */
+ elem = path->elements->data;
+ elem->combinator = combinator;
+}
+
+static gint
+selector_path_depth (SelectorPath *path)
+{
+ return g_slist_length (path->elements);
+}
+
+static SelectorStyleInfo *
+selector_style_info_new (SelectorPath *path)
+{
+ SelectorStyleInfo *info;
+
+ info = g_slice_new0 (SelectorStyleInfo);
+ info->path = selector_path_ref (path);
+
+ return info;
+}
+
+static void
+selector_style_info_free (SelectorStyleInfo *info)
+{
+ if (info->style)
+ g_hash_table_unref (info->style);
+
+ if (info->path)
+ selector_path_unref (info->path);
+}
+
+static void
+selector_style_info_set_style (SelectorStyleInfo *info,
+ GHashTable *style)
+{
+ if (info->style)
+ g_hash_table_unref (info->style);
+
+ if (style)
+ info->style = g_hash_table_ref (style);
+ else
+ info->style = NULL;
+}
+
+static GScanner *
+create_scanner (void)
+{
+ GScanner *scanner;
+
+ scanner = g_scanner_new (NULL);
+
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "active", GUINT_TO_POINTER (GTK_STATE_ACTIVE));
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "prelight", GUINT_TO_POINTER (GTK_STATE_PRELIGHT));
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "hover", GUINT_TO_POINTER (GTK_STATE_PRELIGHT));
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "selected", GUINT_TO_POINTER (GTK_STATE_SELECTED));
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "insensitive", GUINT_TO_POINTER (GTK_STATE_INSENSITIVE));
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "inconsistent", GUINT_TO_POINTER (GTK_STATE_INCONSISTENT));
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "focused", GUINT_TO_POINTER (GTK_STATE_FOCUSED));
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "focus", GUINT_TO_POINTER (GTK_STATE_FOCUSED));
+
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "nth-child", GUINT_TO_POINTER (SYMBOL_NTH_CHILD));
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "first-child", GUINT_TO_POINTER (SYMBOL_FIRST_CHILD));
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "last-child", GUINT_TO_POINTER (SYMBOL_LAST_CHILD));
+ g_scanner_scope_add_symbol (scanner, SCOPE_PSEUDO_CLASS, "sorted", GUINT_TO_POINTER (SYMBOL_SORTED_CHILD));
+
+ g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "even", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_EVEN));
+ g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "odd", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_ODD));
+ g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "first", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_FIRST));
+ g_scanner_scope_add_symbol (scanner, SCOPE_NTH_CHILD, "last", GUINT_TO_POINTER (SYMBOL_NTH_CHILD_LAST));
+
+ scanner_apply_scope (scanner, SCOPE_SELECTOR);
+
+ return scanner;
+}
+
+static void
+gtk_css_provider_init (GtkCssProvider *css_provider)
+{
+ GtkCssProviderPrivate *priv;
+
+ priv = css_provider->priv = G_TYPE_INSTANCE_GET_PRIVATE (css_provider,
+ GTK_TYPE_CSS_PROVIDER,
+ GtkCssProviderPrivate);
+
+ priv->selectors_info = g_ptr_array_new_with_free_func ((GDestroyNotify) selector_style_info_free);
+ priv->scanner = create_scanner ();
+
+ priv->symbolic_colors = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) gtk_symbolic_color_unref);
+}
+
+typedef struct ComparePathData ComparePathData;
+
+struct ComparePathData
+{
+ guint64 score;
+ SelectorPath *path;
+ GSList *iter;
+};
+
+static gboolean
+compare_selector_element (GtkWidgetPath *path,
+ guint index,
+ SelectorElement *elem,
+ guint8 *score)
+{
+ *score = 0;
+
+ if (elem->elem_type == SELECTOR_TYPE_NAME)
+ {
+ const gchar *type_name;
+ GType resolved_type;
+
+ /* Resolve the type name */
+ type_name = g_quark_to_string (elem->name);
+ resolved_type = g_type_from_name (type_name);
+
+ if (resolved_type == G_TYPE_INVALID)
+ {
+ /* Type couldn't be resolved, so the selector
+ * clearly doesn't affect the given widget path
+ */
+ return FALSE;
+ }
+
+ elem->elem_type = SELECTOR_GTYPE;
+ elem->type = resolved_type;
+ }
+
+ if (elem->elem_type == SELECTOR_GTYPE)
+ {
+ GType type;
+
+ type = gtk_widget_path_iter_get_widget_type (path, index);
+
+ if (!g_type_is_a (type, elem->type))
+ return FALSE;
+
+ if (type == elem->type)
+ *score |= 0xF;
+ else
+ {
+ GType parent = type;
+
+ *score = 0xE;
+
+ while ((parent = g_type_parent (parent)) != G_TYPE_INVALID)
+ {
+ if (parent == elem->type)
+ break;
+
+ *score -= 1;
+
+ if (*score == 1)
+ {
+ g_warning ("Hierarchy is higher than expected.");
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+ }
+ else if (elem->elem_type == SELECTOR_REGION)
+ {
+ GtkRegionFlags flags;
+
+ if (!gtk_widget_path_iter_has_qregion (path, index,
+ elem->region.name,
+ &flags))
+ return FALSE;
+
+ if (elem->region.flags != 0 &&
+ (flags & elem->region.flags) == 0)
+ return FALSE;
+
+ *score = 0xF;
+ return TRUE;
+ }
+ else if (elem->elem_type == SELECTOR_GLOB)
+ {
+ /* Treat as lowest matching type */
+ *score = 1;
+ return TRUE;
+ }
+ else if (elem->elem_type == SELECTOR_NAME)
+ {
+ if (!gtk_widget_path_iter_has_qname (path, index, elem->name))
+ return FALSE;
+
+ *score = 0xF;
+ return TRUE;
+ }
+ else if (elem->elem_type == SELECTOR_CLASS)
+ {
+ if (!gtk_widget_path_iter_has_qclass (path, index, elem->name))
+ return FALSE;
+
+ *score = 0xF;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static guint64
+compare_selector (GtkWidgetPath *path,
+ SelectorPath *selector)
+{
+ GSList *elements = selector->elements;
+ gboolean match = TRUE;
+ guint64 score = 0;
+ gint i;
+
+ i = gtk_widget_path_length (path) - 1;
+
+ while (elements && match && i >= 0)
+ {
+ SelectorElement *elem;
+ guint8 elem_score;
+
+ elem = elements->data;
+
+ match = compare_selector_element (path, i, elem, &elem_score);
+
+ /* Only move on to the next index if there is no match
+ * with the current element (whether to continue or not
+ * handled right after in the combinator check), or a
+ * GType or glob has just been matched.
+ *
+ * Region and widget names do not trigger this because
+ * the next element in the selector path could also be
+ * related to the same index.
+ */
+ if (!match ||
+ (elem->elem_type == SELECTOR_GTYPE ||
+ elem->elem_type == SELECTOR_GLOB))
+ i--;
+
+ if (!match &&
+ elem->elem_type != SELECTOR_TYPE_NAME &&
+ elem->combinator == COMBINATOR_DESCENDANT)
+ {
+ /* With descendant combinators there may
+ * be intermediate chidren in the hierarchy
+ */
+ match = TRUE;
+ }
+ else if (match)
+ elements = elements->next;
+
+ if (match)
+ {
+ /* Only 4 bits are actually used */
+ score <<= 4;
+ score |= elem_score;
+ }
+ }
+
+ /* If there are pending selector
+ * elements to compare, it's not
+ * a match.
+ */
+ if (elements)
+ match = FALSE;
+
+ if (!match)
+ score = 0;
+
+ return score;
+}
+
+typedef struct StylePriorityInfo StylePriorityInfo;
+
+struct StylePriorityInfo
+{
+ guint64 score;
+ GHashTable *style;
+ GtkStateFlags state;
+};
+
+static GArray *
+css_provider_get_selectors (GtkCssProvider *css_provider,
+ GtkWidgetPath *path)
+{
+ GtkCssProviderPrivate *priv;
+ GArray *priority_info;
+ guint i, j;
+
+ priv = css_provider->priv;
+ priority_info = g_array_new (FALSE, FALSE, sizeof (StylePriorityInfo));
+
+ for (i = 0; i < priv->selectors_info->len; i++)
+ {
+ SelectorStyleInfo *info;
+ StylePriorityInfo new;
+ gboolean added = FALSE;
+ guint64 score;
+
+ info = g_ptr_array_index (priv->selectors_info, i);
+ score = compare_selector (path, info->path);
+
+ if (score <= 0)
+ continue;
+
+ new.score = score;
+ new.style = info->style;
+ new.state = info->path->state;
+
+ for (j = 0; j < priority_info->len; j++)
+ {
+ StylePriorityInfo *cur;
+
+ cur = &g_array_index (priority_info, StylePriorityInfo, j);
+
+ if (cur->score > new.score)
+ {
+ g_array_insert_val (priority_info, j, new);
+ added = TRUE;
+ break;
+ }
+ }
+
+ if (!added)
+ g_array_append_val (priority_info, new);
+ }
+
+ return priority_info;
+}
+
+static void
+css_provider_dump_symbolic_colors (GtkCssProvider *css_provider,
+ GtkStyleProperties *props)
+{
+ GtkCssProviderPrivate *priv;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ priv = css_provider->priv;
+ g_hash_table_iter_init (&iter, priv->symbolic_colors);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ const gchar *name;
+ GtkSymbolicColor *color;
+
+ name = key;
+ color = value;
+
+ gtk_style_properties_map_color (props, name, color);
+ }
+}
+
+static GtkStyleProperties *
+gtk_css_provider_get_style (GtkStyleProvider *provider,
+ GtkWidgetPath *path)
+{
+ GtkCssProvider *css_provider;
+ GtkCssProviderPrivate *priv;
+ GtkStyleProperties *props;
+ GArray *priority_info;
+ guint i;
+
+ css_provider = GTK_CSS_PROVIDER (provider);
+ props = gtk_style_properties_new ();
+ priv = css_provider->priv;
+
+ css_provider_dump_symbolic_colors (css_provider, props);
+ priority_info = css_provider_get_selectors (css_provider, path);
+
+ for (i = 0; i < priority_info->len; i++)
+ {
+ StylePriorityInfo *info;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ info = &g_array_index (priority_info, StylePriorityInfo, i);
+ g_hash_table_iter_init (&iter, info->style);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ gchar *prop = key;
+
+ /* Properties starting with '-' may be both widget style properties
+ * or custom properties from the theming engine, so check whether
+ * the type is registered or not.
+ */
+ if (prop[0] == '-' &&
+ !gtk_style_properties_lookup_property (prop, NULL, NULL))
+ continue;
+
+ gtk_style_properties_set_property (props, key, info->state, value);
+ }
+ }
+
+ g_array_free (priority_info, TRUE);
+
+ return props;
+}
+
+static gboolean
+gtk_css_provider_get_style_property (GtkStyleProvider *provider,
+ GtkWidgetPath *path,
+ GtkStateFlags state,
+ GParamSpec *pspec,
+ GValue *value)
+{
+ GArray *priority_info;
+ gboolean found = FALSE;
+ gchar *prop_name;
+ gint i;
+
+ prop_name = g_strdup_printf ("-%s-%s",
+ g_type_name (pspec->owner_type),
+ pspec->name);
+
+ priority_info = css_provider_get_selectors (GTK_CSS_PROVIDER (provider), path);
+
+ for (i = priority_info->len - 1; i >= 0; i--)
+ {
+ StylePriorityInfo *info;
+ GValue *val;
+
+ info = &g_array_index (priority_info, StylePriorityInfo, i);
+ val = g_hash_table_lookup (info->style, prop_name);
+
+ if (val &&
+ (info->state == 0 ||
+ info->state == state ||
+ ((info->state & state) != 0 &&
+ (info->state & ~(state)) == 0)))
+ {
+ const gchar *val_str;
+
+ val_str = g_value_get_string (val);
+ found = TRUE;
+
+ css_provider_parse_value (GTK_CSS_PROVIDER (provider), val_str, value, NULL);
+ break;
+ }
+ }
+
+ g_array_free (priority_info, TRUE);
+ g_free (prop_name);
+
+ return found;
+}
+
+static void
+gtk_css_style_provider_iface_init (GtkStyleProviderIface *iface)
+{
+ iface->get_style = gtk_css_provider_get_style;
+ iface->get_style_property = gtk_css_provider_get_style_property;
+}
+
+static void
+gtk_css_provider_finalize (GObject *object)
+{
+ GtkCssProvider *css_provider;
+ GtkCssProviderPrivate *priv;
+
+ css_provider = GTK_CSS_PROVIDER (object);
+ priv = css_provider->priv;
+
+ g_scanner_destroy (priv->scanner);
+ g_free (priv->filename);
+
+ g_ptr_array_free (priv->selectors_info, TRUE);
+
+ g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
+ g_slist_free (priv->cur_selectors);
+
+ if (priv->cur_properties)
+ g_hash_table_unref (priv->cur_properties);
+ if (priv->symbolic_colors)
+ g_hash_table_destroy (priv->symbolic_colors);
+
+ G_OBJECT_CLASS (gtk_css_provider_parent_class)->finalize (object);
+}
+
+/**
+ * gtk_css_provider_new:
+ *
+ * Returns a newly created #GtkCssProvider.
+ *
+ * Returns: A new #GtkCssProvider
+ **/
+GtkCssProvider *
+gtk_css_provider_new (void)
+{
+ return g_object_new (GTK_TYPE_CSS_PROVIDER, NULL);
+}
+
+static void
+property_value_free (GValue *value)
+{
+ if (G_IS_VALUE (value))
+ g_value_unset (value);
+
+ g_slice_free (GValue, value);
+}
+
+static void
+scanner_apply_scope (GScanner *scanner,
+ ParserScope scope)
+{
+ g_scanner_set_scope (scanner, scope);
+
+ if (scope == SCOPE_VALUE)
+ {
+ scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_";
+ scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "@#-_ (),.%\t\n'/\"";
+ scanner->config->scan_identifier_1char = TRUE;
+ }
+ else if (scope == SCOPE_SELECTOR)
+ {
+ scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "*@";
+ scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_#.";
+ scanner->config->scan_identifier_1char = TRUE;
+ }
+ else if (scope == SCOPE_PSEUDO_CLASS ||
+ scope == SCOPE_NTH_CHILD ||
+ scope == SCOPE_DECLARATION)
+ {
+ scanner->config->cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "-_";
+ scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "-_";
+ scanner->config->scan_identifier_1char = FALSE;
+ }
+ else
+ g_assert_not_reached ();
+
+ scanner->config->scan_float = FALSE;
+ scanner->config->cpair_comment_single = NULL;
+}
+
+static void
+css_provider_push_scope (GtkCssProvider *css_provider,
+ ParserScope scope)
+{
+ GtkCssProviderPrivate *priv;
+
+ priv = css_provider->priv;
+ priv->state = g_slist_prepend (priv->state, GUINT_TO_POINTER (scope));
+
+ scanner_apply_scope (priv->scanner, scope);
+}
+
+static ParserScope
+css_provider_pop_scope (GtkCssProvider *css_provider)
+{
+ GtkCssProviderPrivate *priv;
+ ParserScope scope = SCOPE_SELECTOR;
+
+ priv = css_provider->priv;
+
+ if (!priv->state)
+ {
+ g_warning ("Push/pop calls to parser scope aren't paired");
+ scanner_apply_scope (priv->scanner, SCOPE_SELECTOR);
+ return SCOPE_SELECTOR;
+ }
+
+ priv->state = g_slist_delete_link (priv->state, priv->state);
+
+ /* Fetch new scope */
+ if (priv->state)
+ scope = GPOINTER_TO_INT (priv->state->data);
+
+ scanner_apply_scope (priv->scanner, scope);
+
+ return scope;
+}
+
+static void
+css_provider_reset_parser (GtkCssProvider *css_provider)
+{
+ GtkCssProviderPrivate *priv;
+
+ priv = css_provider->priv;
+
+ g_slist_free (priv->state);
+ priv->state = NULL;
+
+ scanner_apply_scope (priv->scanner, SCOPE_SELECTOR);
+ priv->scanner->user_data = NULL;
+ priv->value_pos = NULL;
+
+ g_slist_foreach (priv->cur_selectors, (GFunc) selector_path_unref, NULL);
+ g_slist_free (priv->cur_selectors);
+ priv->cur_selectors = NULL;
+
+ if (priv->cur_properties)
+ g_hash_table_unref (priv->cur_properties);
+
+ priv->cur_properties = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) property_value_free);
+}
+
+static void
+css_provider_commit (GtkCssProvider *css_provider)
+{
+ GtkCssProviderPrivate *priv;
+ GSList *l;
+
+ priv = css_provider->priv;
+ l = priv->cur_selectors;
+
+ while (l)
+ {
+ SelectorPath *path = l->data;
+ SelectorStyleInfo *info;
+
+ info = selector_style_info_new (path);
+ selector_style_info_set_style (info, priv->cur_properties);
+
+ g_ptr_array_add (priv->selectors_info, info);
+ l = l->next;
+ }
+}
+
+static GTokenType
+parse_nth_child (GtkCssProvider *css_provider,
+ GScanner *scanner,
+ GtkRegionFlags *flags)
+{
+ ParserSymbol symbol;
+
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != G_TOKEN_SYMBOL)
+ return G_TOKEN_SYMBOL;
+
+ symbol = GPOINTER_TO_INT (scanner->value.v_symbol);
+
+ if (symbol == SYMBOL_NTH_CHILD)
+ {
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != G_TOKEN_LEFT_PAREN)
+ return G_TOKEN_LEFT_PAREN;
+
+ css_provider_push_scope (css_provider, SCOPE_NTH_CHILD);
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != G_TOKEN_SYMBOL)
+ return G_TOKEN_SYMBOL;
+
+ symbol = GPOINTER_TO_INT (scanner->value.v_symbol);
+
+ switch (symbol)
+ {
+ case SYMBOL_NTH_CHILD_EVEN:
+ *flags = GTK_REGION_EVEN;
+ break;
+ case SYMBOL_NTH_CHILD_ODD:
+ *flags = GTK_REGION_ODD;
+ break;
+ case SYMBOL_NTH_CHILD_FIRST:
+ *flags = GTK_REGION_FIRST;
+ break;
+ case SYMBOL_NTH_CHILD_LAST:
+ *flags = GTK_REGION_LAST;
+ break;
+ default:
+ break;
+ }
+
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != G_TOKEN_RIGHT_PAREN)
+ return G_TOKEN_RIGHT_PAREN;
+
+ css_provider_pop_scope (css_provider);
+ }
+ else if (symbol == SYMBOL_FIRST_CHILD)
+ *flags = GTK_REGION_FIRST;
+ else if (symbol == SYMBOL_LAST_CHILD)
+ *flags = GTK_REGION_LAST;
+ else if (symbol == SYMBOL_SORTED_CHILD)
+ *flags = GTK_REGION_SORTED;
+ else
+ {
+ *flags = 0;
+ return G_TOKEN_SYMBOL;
+ }
+
+ return G_TOKEN_NONE;
+}
+
+static GTokenType
+parse_pseudo_class (GtkCssProvider *css_provider,
+ GScanner *scanner,
+ SelectorPath *selector)
+{
+ GtkStateType state;
+
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != G_TOKEN_SYMBOL)
+ return G_TOKEN_SYMBOL;
+
+ state = GPOINTER_TO_INT (scanner->value.v_symbol);
+
+ switch (state)
+ {
+ case GTK_STATE_ACTIVE:
+ selector->state |= GTK_STATE_FLAG_ACTIVE;
+ break;
+ case GTK_STATE_PRELIGHT:
+ selector->state |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ selector->state |= GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ selector->state |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ case GTK_STATE_INCONSISTENT:
+ selector->state |= GTK_STATE_FLAG_INCONSISTENT;
+ break;
+ case GTK_STATE_FOCUSED:
+ selector->state |= GTK_STATE_FLAG_FOCUSED;
+ break;
+ default:
+ return G_TOKEN_SYMBOL;
+ }
+
+ return G_TOKEN_NONE;
+}
+
+/* Parses a number of concatenated classes */
+static void
+parse_classes (SelectorPath *path,
+ const gchar *str)
+{
+ gchar *pos;
+
+ if ((pos = strchr (str, '.')) != NULL)
+ {
+ /* Leave the last class to the call after the loop */
+ while (pos)
+ {
+ *pos = '\0';
+ selector_path_prepend_class (path, str);
+
+ str = pos + 1;
+ pos = strchr (str, '.');
+ }
+ }
+
+ selector_path_prepend_class (path, str);
+}
+
+static GTokenType
+parse_selector (GtkCssProvider *css_provider,
+ GScanner *scanner,
+ SelectorPath **selector_out)
+{
+ SelectorPath *path;
+
+ path = selector_path_new ();
+ *selector_out = path;
+
+ if (scanner->token != ':' &&
+ scanner->token != '#' &&
+ scanner->token != '.' &&
+ scanner->token != G_TOKEN_IDENTIFIER)
+ return G_TOKEN_IDENTIFIER;
+
+ while (scanner->token == '#' ||
+ scanner->token == '.' ||
+ scanner->token == G_TOKEN_IDENTIFIER)
+ {
+ if (scanner->token == '#' ||
+ scanner->token == '.')
+ {
+ gboolean is_class;
+ gchar *pos;
+
+ is_class = (scanner->token == '.');
+
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != G_TOKEN_IDENTIFIER)
+ return G_TOKEN_IDENTIFIER;
+
+ selector_path_prepend_glob (path);
+ selector_path_prepend_combinator (path, COMBINATOR_CHILD);
+
+ if (is_class)
+ parse_classes (path, scanner->value.v_identifier);
+ else
+ {
+ if ((pos = strchr (scanner->value.v_identifier, '.')) != NULL)
+ *pos = '\0';
+
+ selector_path_prepend_name (path, scanner->value.v_identifier);
+
+ /* Parse any remaining classes */
+ if (pos)
+ parse_classes (path, pos + 1);
+ }
+ }
+ else if (g_ascii_isupper (scanner->value.v_identifier[0]))
+ {
+ gchar *pos;
+
+ if ((pos = strchr (scanner->value.v_identifier, '#')) != NULL ||
+ (pos = strchr (scanner->value.v_identifier, '.')) != NULL)
+ {
+ gchar *type_name, *name;
+ gboolean is_class;
+
+ is_class = (*pos == '.');
+
+ /* Widget type and name/class put together */
+ name = pos + 1;
+ *pos = '\0';
+ type_name = scanner->value.v_identifier;
+
+ selector_path_prepend_type (path, type_name);
+
+ /* This is only so there is a direct relationship
+ * between widget type and its name.
+ */
+ selector_path_prepend_combinator (path, COMBINATOR_CHILD);
+
+ if (is_class)
+ parse_classes (path, name);
+ else
+ {
+ if ((pos = strchr (name, '.')) != NULL)
+ *pos = '\0';
+
+ selector_path_prepend_name (path, name);
+
+ /* Parse any remaining classes */
+ if (pos)
+ parse_classes (path, pos + 1);
+ }
+ }
+ else
+ selector_path_prepend_type (path, scanner->value.v_identifier);
+ }
+ else if (g_ascii_islower (scanner->value.v_identifier[0]))
+ {
+ GtkRegionFlags flags = 0;
+ gchar *region_name;
+
+ region_name = g_strdup (scanner->value.v_identifier);
+
+ if (g_scanner_peek_next_token (scanner) == ':')
+ {
+ ParserSymbol symbol;
+
+ g_scanner_get_next_token (scanner);
+ css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
+
+ /* Check for the next token being nth-child, parse in that
+ * case, and fallback into common state parsing if not.
+ */
+ if (g_scanner_peek_next_token (scanner) != G_TOKEN_SYMBOL)
+ return G_TOKEN_SYMBOL;
+
+ symbol = GPOINTER_TO_INT (scanner->next_value.v_symbol);
+
+ if (symbol == SYMBOL_FIRST_CHILD ||
+ symbol == SYMBOL_LAST_CHILD ||
+ symbol == SYMBOL_NTH_CHILD ||
+ symbol == SYMBOL_SORTED_CHILD)
+ {
+ GTokenType token;
+
+ if ((token = parse_nth_child (css_provider, scanner, &flags)) != G_TOKEN_NONE)
+ return token;
+
+ css_provider_pop_scope (css_provider);
+ }
+ else
+ {
+ css_provider_pop_scope (css_provider);
+ selector_path_prepend_region (path, region_name, 0);
+ g_free (region_name);
+ break;
+ }
+ }
+
+ selector_path_prepend_region (path, region_name, flags);
+ g_free (region_name);
+ }
+ else if (scanner->value.v_identifier[0] == '*')
+ selector_path_prepend_glob (path);
+ else
+ return G_TOKEN_IDENTIFIER;
+
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token == '>')
+ {
+ selector_path_prepend_combinator (path, COMBINATOR_CHILD);
+ g_scanner_get_next_token (scanner);
+ }
+ }
+
+ if (scanner->token == ':')
+ {
+ /* Add glob selector if path is empty */
+ if (selector_path_depth (path) == 0)
+ selector_path_prepend_glob (path);
+
+ css_provider_push_scope (css_provider, SCOPE_PSEUDO_CLASS);
+
+ while (scanner->token == ':')
+ {
+ GTokenType token;
+
+ if ((token = parse_pseudo_class (css_provider, scanner, path)) != G_TOKEN_NONE)
+ return token;
+
+ g_scanner_get_next_token (scanner);
+ }
+
+ css_provider_pop_scope (css_provider);
+ }
+
+ return G_TOKEN_NONE;
+}
+
+#define SKIP_SPACES(s) while (s[0] == ' ' || s[0] == '\t' || s[0] == '\n') s++;
+#define SKIP_SPACES_BACK(s) while (s[0] == ' ' || s[0] == '\t' || s[0] == '\n') s--;
+
+static GtkSymbolicColor *
+symbolic_color_parse_str (const gchar *string,
+ gchar **end_ptr)
+{
+ GtkSymbolicColor *symbolic_color = NULL;
+ gchar *str;
+
+ str = (gchar *) string;
+ *end_ptr = str;
+
+ if (str[0] == '@')
+ {
+ const gchar *end;
+ gchar *name;
+
+ str++;
+ end = str;
+
+ while (*end == '-' || *end == '_' || g_ascii_isalpha (*end))
+ end++;
+
+ name = g_strndup (str, end - str);
+ symbolic_color = gtk_symbolic_color_new_name (name);
+ g_free (name);
+
+ *end_ptr = (gchar *) end;
+ }
+ else if (g_str_has_prefix (str, "lighter") ||
+ g_str_has_prefix (str, "darker"))
+ {
+ GtkSymbolicColor *param_color;
+ gboolean is_lighter = FALSE;
+
+ is_lighter = g_str_has_prefix (str, "lighter");
+
+ if (is_lighter)
+ str += strlen ("lighter");
+ else
+ str += strlen ("darker");
+
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ param_color = symbolic_color_parse_str (str, end_ptr);
+
+ if (!param_color)
+ return NULL;
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+ *end_ptr = (gchar *) str;
+
+ if (*str != ')')
+ {
+ gtk_symbolic_color_unref (param_color);
+ return NULL;
+ }
+
+ if (is_lighter)
+ symbolic_color = gtk_symbolic_color_new_shade (param_color, 1.3);
+ else
+ symbolic_color = gtk_symbolic_color_new_shade (param_color, 0.7);
+
+ gtk_symbolic_color_unref (param_color);
+ (*end_ptr)++;
+ }
+ else if (g_str_has_prefix (str, "shade") ||
+ g_str_has_prefix (str, "alpha"))
+ {
+ GtkSymbolicColor *param_color;
+ gboolean is_shade = FALSE;
+ gdouble factor;
+
+ is_shade = g_str_has_prefix (str, "shade");
+
+ if (is_shade)
+ str += strlen ("shade");
+ else
+ str += strlen ("alpha");
+
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ param_color = symbolic_color_parse_str (str, end_ptr);
+
+ if (!param_color)
+ return NULL;
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ if (str[0] != ',')
+ {
+ gtk_symbolic_color_unref (param_color);
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ factor = g_ascii_strtod (str, end_ptr);
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+ *end_ptr = (gchar *) str;
+
+ if (str[0] != ')')
+ {
+ gtk_symbolic_color_unref (param_color);
+ return NULL;
+ }
+
+ if (is_shade)
+ symbolic_color = gtk_symbolic_color_new_shade (param_color, factor);
+ else
+ symbolic_color = gtk_symbolic_color_new_alpha (param_color, factor);
+
+ gtk_symbolic_color_unref (param_color);
+ (*end_ptr)++;
+ }
+ else if (g_str_has_prefix (str, "mix"))
+ {
+ GtkSymbolicColor *color1, *color2;
+ gdouble factor;
+
+ str += strlen ("mix");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ color1 = symbolic_color_parse_str (str, end_ptr);
+
+ if (!color1)
+ return NULL;
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ if (str[0] != ',')
+ {
+ gtk_symbolic_color_unref (color1);
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ color2 = symbolic_color_parse_str (str, end_ptr);
+
+ if (!color2 || *end_ptr[0] != ',')
+ {
+ gtk_symbolic_color_unref (color1);
+ return NULL;
+ }
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ if (str[0] != ',')
+ {
+ gtk_symbolic_color_unref (color1);
+ gtk_symbolic_color_unref (color2);
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+ factor = g_ascii_strtod (str, end_ptr);
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+ *end_ptr = (gchar *) str;
+
+ if (str[0] != ')')
+ {
+ gtk_symbolic_color_unref (color1);
+ gtk_symbolic_color_unref (color2);
+ return NULL;
+ }
+
+ symbolic_color = gtk_symbolic_color_new_mix (color1, color2, factor);
+ gtk_symbolic_color_unref (color1);
+ gtk_symbolic_color_unref (color2);
+ (*end_ptr)++;
+ }
+ else
+ {
+ GdkRGBA color;
+ gchar *color_str;
+ const gchar *end;
+
+ end = str + 1;
+
+ if (str[0] == '#')
+ {
+ /* Color in hex format */
+ while (g_ascii_isxdigit (*end))
+ end++;
+ }
+ else if (g_str_has_prefix (str, "rgb"))
+ {
+ /* color in rgb/rgba format */
+ while (*end != ')' && *end != '\0')
+ end++;
+
+ if (*end == ')')
+ end++;
+ }
+ else
+ {
+ /* color name? parse until first whitespace */
+ while (*end != ' ' && *end != '\0')
+ end++;
+ }
+
+ color_str = g_strndup (str, end - str);
+ *end_ptr = (gchar *) end;
+
+ if (!gdk_rgba_parse (&color, color_str))
+ {
+ g_free (color_str);
+ return NULL;
+ }
+
+ symbolic_color = gtk_symbolic_color_new_literal (&color);
+ g_free (color_str);
+ }
+
+ return symbolic_color;
+}
+
+static GtkSymbolicColor *
+symbolic_color_parse (const gchar *str,
+ GError **error)
+{
+ GtkSymbolicColor *color;
+ gchar *end;
+
+ color = symbolic_color_parse_str (str, &end);
+
+ if (*end != '\0')
+ {
+ g_set_error_literal (error,
+ gtk_css_provider_error_quark (),
+ CSS_PROVIDER_PARSE_ERROR,
+ "Could not parse symbolic color");
+
+ if (color)
+ {
+ gtk_symbolic_color_unref (color);
+ color = NULL;
+ }
+ }
+
+ return color;
+}
+
+static GtkGradient *
+gradient_parse_str (const gchar *str,
+ gchar **end_ptr)
+{
+ GtkGradient *gradient = NULL;
+ gdouble coords[6];
+ gchar *end;
+ guint i;
+
+ if (g_str_has_prefix (str, "-gtk-gradient"))
+ {
+ cairo_pattern_type_t type;
+
+ str += strlen ("-gtk-gradient");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ /* Parse gradient type */
+ if (g_str_has_prefix (str, "linear"))
+ {
+ type = CAIRO_PATTERN_TYPE_LINEAR;
+ str += strlen ("linear");
+ }
+ else if (g_str_has_prefix (str, "radial"))
+ {
+ type = CAIRO_PATTERN_TYPE_RADIAL;
+ str += strlen ("radial");
+ }
+ else
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ SKIP_SPACES (str);
+
+ /* Parse start/stop position parameters */
+ for (i = 0; i < 2; i++)
+ {
+ if (*str != ',')
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ if (strncmp (str, "left", 4) == 0)
+ {
+ coords[i * 3] = 0;
+ str += strlen ("left");
+ }
+ else if (strncmp (str, "right", 5) == 0)
+ {
+ coords[i * 3] = 1;
+ str += strlen ("right");
+ }
+ else if (strncmp (str, "center", 6) == 0)
+ {
+ coords[i * 3] = 0.5;
+ str += strlen ("center");
+ }
+ else
+ {
+ coords[i * 3] = g_ascii_strtod (str, &end);
+
+ if (str == end)
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str = end;
+ }
+
+ SKIP_SPACES (str);
+
+ if (strncmp (str, "top", 3) == 0)
+ {
+ coords[(i * 3) + 1] = 0;
+ str += strlen ("top");
+ }
+ else if (strncmp (str, "bottom", 6) == 0)
+ {
+ coords[(i * 3) + 1] = 1;
+ str += strlen ("bottom");
+ }
+ else if (strncmp (str, "center", 6) == 0)
+ {
+ coords[(i * 3) + 1] = 0.5;
+ str += strlen ("center");
+ }
+ else
+ {
+ coords[(i * 3) + 1] = g_ascii_strtod (str, &end);
+
+ if (str == end)
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str = end;
+ }
+
+ SKIP_SPACES (str);
+
+ if (type == CAIRO_PATTERN_TYPE_RADIAL)
+ {
+ /* Parse radius */
+ if (*str != ',')
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ coords[(i * 3) + 2] = g_ascii_strtod (str, &end);
+ str = end;
+
+ SKIP_SPACES (str);
+ }
+ }
+
+ if (type == CAIRO_PATTERN_TYPE_LINEAR)
+ gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
+ else
+ gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
+ coords[3], coords[4], coords[5]);
+
+ while (*str == ',')
+ {
+ GtkSymbolicColor *color;
+ gdouble position;
+
+ if (*str != ',')
+ {
+ *end_ptr = (gchar *) str;
+ return gradient;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ if (g_str_has_prefix (str, "from"))
+ {
+ position = 0;
+ str += strlen ("from");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return gradient;
+ }
+ }
+ else if (g_str_has_prefix (str, "to"))
+ {
+ position = 1;
+ str += strlen ("to");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return gradient;
+ }
+ }
+ else if (g_str_has_prefix (str, "color-stop"))
+ {
+ str += strlen ("color-stop");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return gradient;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ position = g_ascii_strtod (str, &end);
+
+ str = end;
+ SKIP_SPACES (str);
+
+ if (*str != ',')
+ {
+ *end_ptr = (gchar *) str;
+ return gradient;
+ }
+ }
+ else
+ {
+ *end_ptr = (gchar *) str;
+ return gradient;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ color = symbolic_color_parse_str (str, &end);
+
+ str = end;
+ SKIP_SPACES (str);
+
+ if (*str != ')')
+ {
+ *end_ptr = (gchar *) str;
+ return gradient;
+ }
+
+ str++;
+ SKIP_SPACES (str);
+
+ if (color)
+ {
+ gtk_gradient_add_color_stop (gradient, position, color);
+ gtk_symbolic_color_unref (color);
+ }
+ }
+
+ if (*str != ')')
+ {
+ *end_ptr = (gchar *) str;
+ return gradient;
+ }
+
+ str++;
+ }
+
+ *end_ptr = (gchar *) str;
+
+ return gradient;
+}
+
+static gchar *
+path_parse_str (GtkCssProvider *css_provider,
+ const gchar *str,
+ gchar **end_ptr,
+ GError **error)
+{
+ gchar *path, *chr;
+ const gchar *start, *end;
+
+ start = str;
+
+ if (g_str_has_prefix (str, "url"))
+ {
+ str += strlen ("url");
+ SKIP_SPACES (str);
+
+ if (*str != '(')
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ chr = strchr (str, ')');
+ if (!chr)
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ end = chr + 1;
+
+ str++;
+ SKIP_SPACES (str);
+
+ if (*str == '"' || *str == '\'')
+ {
+ const gchar *p;
+ p = str;
+ str++;
+
+ chr--;
+ SKIP_SPACES_BACK (chr);
+
+ if (*chr != *p || chr == p)
+ {
+ *end_ptr = (gchar *)str;
+ return NULL;
+ }
+ }
+ else
+ {
+ *end_ptr = (gchar *)str;
+ return NULL;
+ }
+
+ path = g_strndup (str, chr - str);
+ g_strstrip (path);
+
+ *end_ptr = (gchar *)end;
+ }
+ else
+ {
+ path = g_strdup (str);
+ *end_ptr = (gchar *)str + strlen (str);
+ }
+
+ /* Always return an absolute path */
+ if (!g_path_is_absolute (path))
+ {
+ GtkCssProviderPrivate *priv;
+ gchar *dirname, *full_path;
+
+ priv = css_provider->priv;
+
+ /* Use relative path to the current CSS file path, if any */
+ if (priv->filename)
+ dirname = g_path_get_dirname (priv->filename);
+ else
+ dirname = g_get_current_dir ();
+
+ full_path = g_build_filename (dirname, path, NULL);
+ g_free (path);
+ g_free (dirname);
+
+ path = full_path;
+ }
+
+ if (!g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_EXIST,
+ "File doesn't exist: %s", path);
+ g_free (path);
+ path = NULL;
+ *end_ptr = (gchar *)start;
+ }
+
+ return path;
+}
+
+static gchar *
+path_parse (GtkCssProvider *css_provider,
+ const gchar *str,
+ GError **error)
+{
+ gchar *path;
+ gchar *end;
+
+ path = path_parse_str (css_provider, str, &end, error);
+
+ if (!path)
+ return NULL;
+
+ if (*end != '\0')
+ {
+ g_set_error_literal (error,
+ gtk_css_provider_error_quark (),
+ CSS_PROVIDER_PARSE_ERROR,
+ "Error parsing path");
+ g_free (path);
+ path = NULL;
+ }
+
+ return path;
+}
+
+static Gtk9Slice *
+slice_parse_str (GtkCssProvider *css_provider,
+ const gchar *str,
+ gchar **end_ptr,
+ GError **error)
+{
+ gdouble distance_top, distance_bottom;
+ gdouble distance_left, distance_right;
+ GtkSliceSideModifier mods[2];
+ GdkPixbuf *pixbuf;
+ Gtk9Slice *slice;
+ gchar *path;
+ gint i = 0;
+
+ SKIP_SPACES (str);
+
+ /* Parse image url */
+ path = path_parse_str (css_provider, str, end_ptr, error);
+
+ if (!path)
+ return NULL;
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ /* Parse top/left/bottom/right distances */
+ distance_top = g_ascii_strtod (str, end_ptr);
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ distance_right = g_ascii_strtod (str, end_ptr);
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ distance_bottom = g_ascii_strtod (str, end_ptr);
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ distance_left = g_ascii_strtod (str, end_ptr);
+
+ str = *end_ptr;
+ SKIP_SPACES (str);
+
+ while (*str && i < 2)
+ {
+ if (g_str_has_prefix (str, "stretch"))
+ {
+ str += strlen ("stretch");
+ mods[i] = GTK_SLICE_STRETCH;
+ }
+ else if (g_str_has_prefix (str, "repeat"))
+ {
+ str += strlen ("repeat");
+ mods[i] = GTK_SLICE_REPEAT;
+ }
+ else
+ {
+ g_free (path);
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ SKIP_SPACES (str);
+ i++;
+ }
+
+ *end_ptr = (gchar *) str;
+
+ if (*str != '\0')
+ {
+ g_free (path);
+ return NULL;
+ }
+
+ if (i != 2)
+ {
+ /* Fill in second modifier, same as the first */
+ mods[1] = mods[0];
+ }
+
+ pixbuf = gdk_pixbuf_new_from_file (path, error);
+ g_free (path);
+
+ if (!pixbuf)
+ {
+ *end_ptr = (gchar *) str;
+ return NULL;
+ }
+
+ slice = gtk_9slice_new (pixbuf,
+ distance_top, distance_bottom,
+ distance_left, distance_right,
+ mods[0], mods[1]);
+ g_object_unref (pixbuf);
+
+ return slice;
+}
+
+static gdouble
+unit_parse_str (const gchar *str,
+ gchar **end_str)
+{
+ gdouble unit;
+
+ SKIP_SPACES (str);
+ unit = g_ascii_strtod (str, end_str);
+ str = *end_str;
+
+ /* Now parse the unit type, if any. We
+ * don't admit spaces between these.
+ */
+ if (*str != ' ' && *str != '\0')
+ {
+ while (**end_str != ' ' && **end_str != '\0')
+ (*end_str)++;
+
+ /* Only handle pixels at the moment */
+ if (strncmp (str, "px", 2) != 0)
+ {
+ gchar *type;
+
+ type = g_strndup (str, *end_str - str);
+ g_warning ("Unknown unit '%s', only pixel units are "
+ "currently supported in CSS style", type);
+ g_free (type);
+ }
+ }
+
+ return unit;
+}
+
+static GtkBorder *
+border_parse_str (const gchar *str,
+ gchar **end_str)
+{
+ gdouble first, second, third, fourth;
+ GtkBorder *border;
+
+ border = gtk_border_new ();
+
+ SKIP_SPACES (str);
+ if (!g_ascii_isdigit (*str))
+ return border;
+
+ first = unit_parse_str (str, end_str);
+ str = *end_str;
+ SKIP_SPACES (str);
+
+ if (!g_ascii_isdigit (*str))
+ {
+ border->left = border->right = border->top = border->bottom = (gint) first;
+ *end_str = (gchar *) str;
+ return border;
+ }
+
+ second = unit_parse_str (str, end_str);
+ str = *end_str;
+ SKIP_SPACES (str);
+
+ if (!g_ascii_isdigit (*str))
+ {
+ border->top = border->bottom = (gint) first;
+ border->left = border->right = (gint) second;
+ *end_str = (gchar *) str;
+ return border;
+ }
+
+ third = unit_parse_str (str, end_str);
+ str = *end_str;
+ SKIP_SPACES (str);
+
+ if (!g_ascii_isdigit (*str))
+ {
+ border->top = (gint) first;
+ border->left = border->right = (gint) second;
+ border->bottom = (gint) third;
+ *end_str = (gchar *) str;
+ return border;
+ }
+
+ fourth = unit_parse_str (str, end_str);
+
+ border->top = (gint) first;
+ border->right = (gint) second;
+ border->bottom = (gint) third;
+ border->left = (gint) fourth;
+
+ return border;
+}
+
+static gboolean
+css_provider_parse_value (GtkCssProvider *css_provider,
+ const gchar *value_str,
+ GValue *value,
+ GError **error)
+{
+ GtkCssProviderPrivate *priv;
+ GType type;
+ gboolean parsed = TRUE;
+ gchar *end = NULL;
+
+ priv = css_provider->priv;
+ type = G_VALUE_TYPE (value);
+
+ if (type == GDK_TYPE_RGBA ||
+ type == GDK_TYPE_COLOR)
+ {
+ GdkRGBA color;
+ GdkColor rgb;
+
+ if (type == GDK_TYPE_RGBA &&
+ gdk_rgba_parse (&color, value_str))
+ g_value_set_boxed (value, &color);
+ else if (type == GDK_TYPE_COLOR &&
+ gdk_color_parse (value_str, &rgb))
+ g_value_set_boxed (value, &rgb);
+ else
+ {
+ GtkSymbolicColor *symbolic_color;
+
+ symbolic_color = symbolic_color_parse_str (value_str, &end);
+
+ if (!symbolic_color)
+ return FALSE;
+
+ g_value_unset (value);
+ g_value_init (value, GTK_TYPE_SYMBOLIC_COLOR);
+ g_value_take_boxed (value, symbolic_color);
+ }
+ }
+ else if (type == PANGO_TYPE_FONT_DESCRIPTION)
+ {
+ PangoFontDescription *font_desc;
+
+ font_desc = pango_font_description_from_string (value_str);
+ g_value_take_boxed (value, font_desc);
+ }
+ else if (type == G_TYPE_BOOLEAN)
+ {
+ if (value_str[0] == '1' ||
+ g_ascii_strcasecmp (value_str, "true") == 0)
+ g_value_set_boolean (value, TRUE);
+ else
+ g_value_set_boolean (value, FALSE);
+ }
+ else if (type == G_TYPE_INT)
+ g_value_set_int (value, atoi (value_str));
+ else if (type == G_TYPE_UINT)
+ g_value_set_uint (value, (guint) atoi (value_str));
+ else if (type == G_TYPE_DOUBLE)
+ g_value_set_double (value, g_ascii_strtod (value_str, NULL));
+ else if (type == G_TYPE_FLOAT)
+ g_value_set_float (value, (gfloat) g_ascii_strtod (value_str, NULL));
+ else if (type == GTK_TYPE_THEMING_ENGINE)
+ {
+ GtkThemingEngine *engine;
+
+ engine = gtk_theming_engine_load (value_str);
+ g_value_set_object (value, engine);
+ }
+ else if (type == GTK_TYPE_ANIMATION_DESCRIPTION)
+ {
+ GtkAnimationDescription *desc;
+
+ desc = gtk_animation_description_from_string (value_str);
+
+ if (desc)
+ g_value_take_boxed (value, desc);
+ else
+ parsed = FALSE;
+ }
+ else if (type == GTK_TYPE_BORDER)
+ {
+ GtkBorder *border;
+
+ border = border_parse_str (value_str, &end);
+ g_value_take_boxed (value, border);
+ }
+ else if (type == CAIRO_GOBJECT_TYPE_PATTERN)
+ {
+ GtkGradient *gradient;
+
+ gradient = gradient_parse_str (value_str, &end);
+
+ if (gradient)
+ {
+ g_value_unset (value);
+ g_value_init (value, GTK_TYPE_GRADIENT);
+ g_value_take_boxed (value, gradient);
+ }
+ else
+ {
+ gchar *path;
+ GdkPixbuf *pixbuf;
+
+ g_clear_error (error);
+ path = path_parse_str (css_provider, value_str, &end, error);
+
+ if (path)
+ {
+ pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+ g_free (path);
+
+ if (pixbuf)
+ {
+ cairo_surface_t *surface;
+ cairo_pattern_t *pattern;
+ cairo_t *cr;
+ cairo_matrix_t matrix;
+
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf));
+ cr = cairo_create (surface);
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_paint (cr);
+ pattern = cairo_pattern_create_for_surface (surface);
+
+ cairo_matrix_init_scale (&matrix,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf));
+ cairo_pattern_set_matrix (pattern, &matrix);
+
+ cairo_surface_destroy (surface);
+ cairo_destroy (cr);
+ g_object_unref (pixbuf);
+
+ g_value_take_boxed (value, pattern);
+ }
+ else
+ parsed = FALSE;
+ }
+ else
+ parsed = FALSE;
+ }
+ }
+ else if (G_TYPE_IS_ENUM (type))
+ {
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ enum_class = g_type_class_ref (type);
+ enum_value = g_enum_get_value_by_nick (enum_class, value_str);
+
+ if (!enum_value)
+ {
+ g_warning ("Unknown value '%s' for enum type '%s'",
+ value_str, g_type_name (type));
+ parsed = FALSE;
+ }
+ else
+ g_value_set_enum (value, enum_value->value);
+
+ g_type_class_unref (enum_class);
+ }
+ else if (G_TYPE_IS_FLAGS (type))
+ {
+ GFlagsClass *flags_class;
+ GFlagsValue *flag_value;
+ guint flags = 0;
+ gchar *ptr;
+
+ flags_class = g_type_class_ref (type);
+
+ /* Parse comma separated values */
+ ptr = strchr (value_str, ',');
+
+ while (ptr && parsed)
+ {
+ gchar *flag_str;
+
+ *ptr = '\0';
+ ptr++;
+
+ flag_str = (gchar *) value_str;
+ flag_value = g_flags_get_value_by_nick (flags_class,
+ g_strstrip (flag_str));
+
+ if (!flag_value)
+ {
+ g_warning ("Unknown flag '%s' for type '%s'",
+ value_str, g_type_name (type));
+ parsed = FALSE;
+ }
+ else
+ flags |= flag_value->value;
+
+ value_str = ptr;
+ ptr = strchr (value_str, ',');
+ }
+
+ /* Store last/only value */
+ flag_value = g_flags_get_value_by_nick (flags_class, value_str);
+
+ if (!flag_value)
+ {
+ g_warning ("Unknown flag '%s' for type '%s'",
+ value_str, g_type_name (type));
+ parsed = FALSE;
+ }
+ else
+ flags |= flag_value->value;
+
+ if (parsed)
+ g_value_set_enum (value, flags);
+
+ g_type_class_unref (flags_class);
+ }
+ else if (type == GTK_TYPE_9SLICE)
+ {
+ Gtk9Slice *slice;
+
+ slice = slice_parse_str (css_provider, value_str, &end, error);
+
+ if (slice)
+ g_value_take_boxed (value, slice);
+ else
+ parsed = FALSE;
+ }
+ else
+ {
+ g_warning ("Cannot parse string '%s' for type %s", value_str, g_type_name (type));
+ parsed = FALSE;
+ }
+
+ if (end && *end)
+ {
+ /* Set error position in the scanner
+ * according to what we've parsed so far
+ */
+ priv->value_pos += (end - value_str);
+
+ if (error && !*error)
+ g_set_error_literal (error,
+ gtk_css_provider_error_quark (),
+ CSS_PROVIDER_PARSE_ERROR,
+ "Failed to parse value");
+ }
+
+ return parsed;
+}
+
+static void
+scanner_report_warning (GtkCssProvider *css_provider,
+ GTokenType expected_token,
+ GError *error)
+{
+ GtkCssProviderPrivate *priv;
+ const gchar *line_end, *line_start;
+ const gchar *expected_str;
+ gchar buf[2], *line, *str;
+ guint pos;
+
+ priv = css_provider->priv;
+
+ if (error)
+ str = g_strdup (error->message);
+ else
+ {
+ if (priv->scanner->user_data)
+ expected_str = priv->scanner->user_data;
+ else
+ {
+ switch (expected_token)
+ {
+ case G_TOKEN_SYMBOL:
+ expected_str = "Symbol";
+ case G_TOKEN_IDENTIFIER:
+ expected_str = "Identifier";
+ default:
+ buf[0] = expected_token;
+ buf[1] = '\0';
+ expected_str = buf;
+ }
+ }
+
+ str = g_strdup_printf ("Parse error, expecting a %s '%s'",
+ (expected_str != buf) ? "valid" : "",
+ expected_str);
+ }
+
+ if (priv->value_pos)
+ line_start = priv->value_pos - 1;
+ else
+ line_start = priv->scanner->text - 1;
+
+ while (*line_start != '\n' &&
+ line_start != priv->buffer)
+ line_start--;
+
+ if (*line_start == '\n')
+ line_start++;
+
+ if (priv->value_pos)
+ pos = priv->value_pos - line_start + 1;
+ else
+ pos = priv->scanner->text - line_start - 1;
+
+ line_end = strchr (line_start, '\n');
+
+ if (line_end)
+ line = g_strndup (line_start, (line_end - line_start));
+ else
+ line = g_strdup (line_start);
+
+ g_message ("CSS: %s\n"
+ "%s, line %d, char %d:\n"
+ "%*c %s\n"
+ "%*c ^",
+ str, priv->scanner->input_name,
+ priv->scanner->line, priv->scanner->position,
+ 3, ' ', line,
+ 3 + pos, ' ');
+
+ g_free (line);
+ g_free (str);
+}
+
+static GTokenType
+parse_rule (GtkCssProvider *css_provider,
+ GScanner *scanner,
+ GError **error)
+{
+ GtkCssProviderPrivate *priv;
+ GTokenType expected_token;
+ SelectorPath *selector;
+
+ priv = css_provider->priv;
+
+ css_provider_push_scope (css_provider, SCOPE_SELECTOR);
+
+ /* Handle directives */
+ if (scanner->token == G_TOKEN_IDENTIFIER &&
+ scanner->value.v_identifier[0] == '@')
+ {
+ gchar *directive;
+
+ directive = &scanner->value.v_identifier[1];
+
+ if (strcmp (directive, "define-color") == 0)
+ {
+ GtkSymbolicColor *color;
+ gchar *color_name, *color_str;
+
+ /* Directive is a color mapping */
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != G_TOKEN_IDENTIFIER)
+ {
+ scanner->user_data = "Color name";
+ return G_TOKEN_IDENTIFIER;
+ }
+
+ color_name = g_strdup (scanner->value.v_identifier);
+ css_provider_push_scope (css_provider, SCOPE_VALUE);
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != G_TOKEN_IDENTIFIER)
+ {
+ scanner->user_data = "Color definition";
+ return G_TOKEN_IDENTIFIER;
+ }
+
+ color_str = g_strstrip (scanner->value.v_identifier);
+ color = symbolic_color_parse (color_str, error);
+
+ if (!color)
+ {
+ scanner->user_data = "Color definition";
+ return G_TOKEN_IDENTIFIER;
+ }
+
+ g_hash_table_insert (priv->symbolic_colors, color_name, color);
+
+ css_provider_pop_scope (css_provider);
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != ';')
+ return ';';
+
+ return G_TOKEN_NONE;
+ }
+ else if (strcmp (directive, "import") == 0)
+ {
+ GScanner *scanner_backup;
+ GSList *state_backup;
+ gboolean loaded;
+ gchar *path = NULL;
+
+ css_provider_push_scope (css_provider, SCOPE_VALUE);
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token == G_TOKEN_IDENTIFIER &&
+ g_str_has_prefix (scanner->value.v_identifier, "url"))
+ path = path_parse (css_provider,
+ g_strstrip (scanner->value.v_identifier),
+ error);
+ else if (scanner->token == G_TOKEN_STRING)
+ path = path_parse (css_provider,
+ g_strstrip (scanner->value.v_string),
+ error);
+
+ if (path == NULL)
+ {
+ scanner->user_data = "File URL";
+ return G_TOKEN_IDENTIFIER;
+ }
+
+ css_provider_pop_scope (css_provider);
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != ';')
+ {
+ g_free (path);
+ return ';';
+ }
+
+ /* Snapshot current parser state and scanner in order to restore after importing */
+ state_backup = priv->state;
+ scanner_backup = priv->scanner;
+
+ priv->state = NULL;
+ priv->scanner = create_scanner ();
+
+ /* FIXME: Avoid recursive importing */
+ loaded = gtk_css_provider_load_from_path_internal (css_provider, path,
+ FALSE, error);
+
+ /* Restore previous state */
+ css_provider_reset_parser (css_provider);
+ priv->state = state_backup;
+ g_scanner_destroy (priv->scanner);
+ priv->scanner = scanner_backup;
+
+ g_free (path);
+
+ if (!loaded)
+ {
+ scanner->user_data = "File URL";
+ return G_TOKEN_IDENTIFIER;
+ }
+ else
+ return G_TOKEN_NONE;
+ }
+ else
+ {
+ scanner->user_data = "Directive";
+ return G_TOKEN_IDENTIFIER;
+ }
+ }
+
+ expected_token = parse_selector (css_provider, scanner, &selector);
+
+ if (expected_token != G_TOKEN_NONE)
+ {
+ selector_path_unref (selector);
+ scanner->user_data = "Selector";
+ return expected_token;
+ }
+
+ priv->cur_selectors = g_slist_prepend (priv->cur_selectors, selector);
+
+ while (scanner->token == ',')
+ {
+ g_scanner_get_next_token (scanner);
+
+ expected_token = parse_selector (css_provider, scanner, &selector);
+
+ if (expected_token != G_TOKEN_NONE)
+ {
+ selector_path_unref (selector);
+ scanner->user_data = "Selector";
+ return expected_token;
+ }
+
+ priv->cur_selectors = g_slist_prepend (priv->cur_selectors, selector);
+ }
+
+ css_provider_pop_scope (css_provider);
+
+ if (scanner->token != G_TOKEN_LEFT_CURLY)
+ return G_TOKEN_LEFT_CURLY;
+
+ /* Declarations parsing */
+ css_provider_push_scope (css_provider, SCOPE_DECLARATION);
+ g_scanner_get_next_token (scanner);
+
+ while (scanner->token == G_TOKEN_IDENTIFIER)
+ {
+ gchar *value_str = NULL;
+ GtkStylePropertyParser parse_func = NULL;
+ GParamSpec *pspec;
+ gchar *prop;
+
+ prop = g_strdup (scanner->value.v_identifier);
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != ':')
+ {
+ g_free (prop);
+ return ':';
+ }
+
+ priv->value_pos = priv->scanner->text;
+
+ css_provider_push_scope (css_provider, SCOPE_VALUE);
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != G_TOKEN_IDENTIFIER)
+ {
+ g_free (prop);
+ scanner->user_data = "Property value";
+ return G_TOKEN_IDENTIFIER;
+ }
+
+ value_str = scanner->value.v_identifier;
+ SKIP_SPACES (value_str);
+ g_strchomp (value_str);
+
+ if (gtk_style_properties_lookup_property (prop, &parse_func, &pspec))
+ {
+ GValue *val;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, pspec->value_type);
+
+ if (strcmp (value_str, "none") == 0)
+ {
+ /* Insert the default value, so it has an opportunity
+ * to override other style providers when merged
+ */
+ g_param_value_set_default (pspec, val);
+ g_hash_table_insert (priv->cur_properties, prop, val);
+ }
+ else if (pspec->value_type == G_TYPE_STRING)
+ {
+ g_value_set_string (val, value_str);
+ g_hash_table_insert (priv->cur_properties, prop, val);
+ }
+ else if ((parse_func && (parse_func) (value_str, val, error)) ||
+ (!parse_func && css_provider_parse_value (css_provider, value_str, val, error)))
+ g_hash_table_insert (priv->cur_properties, prop, val);
+ else
+ {
+ g_value_unset (val);
+ g_slice_free (GValue, val);
+ g_free (prop);
+
+ scanner->user_data = "Property value";
+ return G_TOKEN_IDENTIFIER;
+ }
+ }
+ else if (prop[0] == '-' &&
+ g_ascii_isupper (prop[1]))
+ {
+ GValue *val;
+
+ val = g_slice_new0 (GValue);
+ g_value_init (val, G_TYPE_STRING);
+ g_value_set_string (val, value_str);
+
+ g_hash_table_insert (priv->cur_properties, prop, val);
+ }
+ else
+ g_free (prop);
+
+ css_provider_pop_scope (css_provider);
+ g_scanner_get_next_token (scanner);
+
+ if (scanner->token != ';')
+ break;
+
+ g_scanner_get_next_token (scanner);
+ }
+
+ if (scanner->token != G_TOKEN_RIGHT_CURLY)
+ return G_TOKEN_RIGHT_CURLY;
+
+ css_provider_pop_scope (css_provider);
+
+ return G_TOKEN_NONE;
+}
+
+static gboolean
+parse_stylesheet (GtkCssProvider *css_provider,
+ GError **error)
+{
+ GtkCssProviderPrivate *priv;
+
+ priv = css_provider->priv;
+ g_scanner_get_next_token (priv->scanner);
+
+ while (!g_scanner_eof (priv->scanner))
+ {
+ GTokenType expected_token;
+ GError *err = NULL;
+
+ css_provider_reset_parser (css_provider);
+ expected_token = parse_rule (css_provider, priv->scanner, &err);
+
+ if (expected_token != G_TOKEN_NONE)
+ {
+ scanner_report_warning (css_provider, expected_token, err);
+
+ while (!g_scanner_eof (priv->scanner) &&
+ priv->scanner->token != G_TOKEN_RIGHT_CURLY)
+ g_scanner_get_next_token (priv->scanner);
+ }
+ else
+ css_provider_commit (css_provider);
+
+ g_clear_error (&err);
+ g_scanner_get_next_token (priv->scanner);
+ }
+
+ return TRUE;
+}
+
+/**
+ * gtk_css_provider_load_from_data:
+ * @css_provider: a #GtkCssProvider
+ * @data: CSS data loaded in memory
+ * @length: the length of @data in bytes, or -1 for NUL terminated strings
+ * @error: (out) (allow-none): return location for a #GError, or %NULL
+ *
+ * Loads @data into @css_provider, making it clear any previously loaded
+ * information.
+ *
+ * Returns: %TRUE if the data could be loaded.
+ **/
+gboolean
+gtk_css_provider_load_from_data (GtkCssProvider *css_provider,
+ const gchar *data,
+ gssize length,
+ GError **error)
+{
+ GtkCssProviderPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ priv = css_provider->priv;
+
+ if (length < 0)
+ length = strlen (data);
+
+ if (priv->selectors_info->len > 0)
+ g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
+
+ priv->scanner->input_name = "-";
+ priv->buffer = data;
+ g_scanner_input_text (priv->scanner, data, (guint) length);
+
+ g_free (priv->filename);
+ priv->filename = NULL;
+ priv->buffer = NULL;
+
+ return parse_stylesheet (css_provider, error);
+}
+
+/**
+ * gtk_css_provider_load_from_file:
+ * @css_provider: a #GtkCssProvider
+ * @file: #GFile pointing to a file to load
+ * @error: (out) (allow-none): return location for a #GError, or %NULL
+ *
+ * Loads the data contained in @file into @css_provider, making it
+ * clear any previously loaded information.
+ *
+ * Returns: %TRUE if the data could be loaded.
+ **/
+gboolean
+gtk_css_provider_load_from_file (GtkCssProvider *css_provider,
+ GFile *file,
+ GError **error)
+{
+ GtkCssProviderPrivate *priv;
+ GError *internal_error = NULL;
+ gchar *data;
+ gsize length;
+ gboolean ret;
+
+ g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+ priv = css_provider->priv;
+
+ if (!g_file_load_contents (file, NULL,
+ &data, &length,
+ NULL, &internal_error))
+ {
+ g_propagate_error (error, internal_error);
+ return FALSE;
+ }
+
+ if (priv->selectors_info->len > 0)
+ g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
+
+ g_free (priv->filename);
+ priv->filename = g_file_get_path (file);
+
+ priv->scanner->input_name = priv->filename;
+ priv->buffer = data;
+ g_scanner_input_text (priv->scanner, data, (guint) length);
+
+ ret = parse_stylesheet (css_provider, error);
+
+ priv->buffer = NULL;
+ g_free (data);
+
+ return ret;
+}
+
+static gboolean
+gtk_css_provider_load_from_path_internal (GtkCssProvider *css_provider,
+ const gchar *path,
+ gboolean reset,
+ GError **error)
+{
+ GtkCssProviderPrivate *priv;
+ GError *internal_error = NULL;
+ GMappedFile *mapped_file;
+ const gchar *data;
+ gsize length;
+ gboolean ret;
+
+ priv = css_provider->priv;
+
+ mapped_file = g_mapped_file_new (path, FALSE, &internal_error);
+
+ if (internal_error)
+ {
+ g_propagate_error (error, internal_error);
+ return FALSE;
+ }
+
+ length = g_mapped_file_get_length (mapped_file);
+ data = g_mapped_file_get_contents (mapped_file);
+
+ if (!data)
+ data = "";
+
+ if (reset)
+ {
+ if (priv->selectors_info->len > 0)
+ g_ptr_array_remove_range (priv->selectors_info, 0, priv->selectors_info->len);
+
+ g_free (priv->filename);
+ priv->filename = g_strdup (path);
+ }
+
+ priv->scanner->input_name = priv->filename;
+ priv->buffer = data;
+ g_scanner_input_text (priv->scanner, data, (guint) length);
+
+ ret = parse_stylesheet (css_provider, error);
+
+ priv->buffer = NULL;
+ g_mapped_file_unref (mapped_file);
+
+ return ret;
+}
+
+/**
+ * gtk_css_provider_load_from_path:
+ * @css_provider: a #GtkCssProvider
+ * @path: the path of a filename to load, in the GLib filename encoding
+ * @error: (out) (allow-none): return location for a #GError, or %NULL
+ *
+ * Loads the data contained in @path into @css_provider, making it clear
+ * any previously loaded information.
+ *
+ * Returns: %TRUE if the data could be loaded.
+ **/
+gboolean
+gtk_css_provider_load_from_path (GtkCssProvider *css_provider,
+ const gchar *path,
+ GError **error)
+{
+ g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ return gtk_css_provider_load_from_path_internal (css_provider, path,
+ TRUE, error);
+}
+
+/**
+ * gtk_css_provider_get_default:
+ *
+ * Returns the provider containing the style settings used as a
+ * fallback for all widgets.
+ *
+ * Returns: (transfer none): The provider used for fallback styling.
+ * This memory is owned by GTK+, and you must not free it.
+ **/
+GtkCssProvider *
+gtk_css_provider_get_default (void)
+{
+ static GtkCssProvider *provider;
+
+ if (G_UNLIKELY (!provider))
+ {
+ const gchar *str =
+ "@define-color fg_color #000; \n"
+ "@define-color bg_color #dcdad5; \n"
+ "@define-color text_color #000; \n"
+ "@define-color base_color #fff; \n"
+ "@define-color selected_bg_color #4b6983; \n"
+ "@define-color selected_fg_color #fff; \n"
+ "@define-color tooltip_bg_color #eee1b3; \n"
+ "@define-color tooltip_fg_color #000; \n"
+ "\n"
+ "*,\n"
+ "GtkTreeView > GtkButton {\n"
+ " background-color: @bg_color;\n"
+ " color: @fg_color;\n"
+ " border-color: shade (@bg_color, 0.6);\n"
+ " padding: 2;\n"
+ " border-width: 0;\n"
+ "}\n"
+ "\n"
+ "*:prelight {\n"
+ " background-color: shade (@bg_color, 1.05);\n"
+ " color: shade (@fg_color, 1.3);\n"
+ "}\n"
+ "\n"
+ "*:selected {\n"
+ " background-color: @selected_bg_color;\n"
+ " color: @selected_fg_color;\n"
+ "}\n"
+ "\n"
+ ".expander:prelight {\n"
+ " color: @selected_fg_color\n"
+ "}\n"
+ "\n"
+ "*:insensitive {\n"
+ " border-color: shade (@bg_color, 0.7);\n"
+ " background-color: shade (@bg_color, 0.9);\n"
+ " color: shade (@bg_color, 0.7);\n"
+ "}\n"
+ "\n"
+ "GtkTreeView, GtkIconView, GtkTextView {\n"
+ " background-color: @base_color;\n"
+ " color: @text_color;\n"
+ "}\n"
+ "\n"
+ "GtkTreeView > row {\n"
+ " background-color: @base_color;\n"
+ " color: @text_color;\n"
+ "}\n"
+ "\n"
+ "GtkTreeView > row:nth-child(odd) { \n"
+ " background-color: shade (@base_color, 0.93); \n"
+ "}\n"
+ "\n"
+ ".tooltip {\n"
+ " background-color: @tooltip_bg_color; \n"
+ " color: @tooltip_fg_color; \n"
+ "}\n"
+ "\n"
+ ".button,\n"
+ ".slider {\n"
+ " border-style: outset; \n"
+ " border-width: 2; \n"
+ "}\n"
+ "\n"
+ ".button:active {\n"
+ " background-color: shade (@bg_color, 0.7);\n"
+ " border-style: inset; \n"
+ "}\n"
+ "\n"
+ ".button:prelight,\n"
+ ".slider:prelight {\n"
+ " background-color: @selected_bg_color;\n"
+ " color: @selected_fg_color;\n"
+ " border-color: shade (@selected_bg_color, 0.7);\n"
+ "}\n"
+ "\n"
+ ".trough {\n"
+ " border-style: inset;\n"
+ " border-width: 1;\n"
+ "}\n"
+ "\n"
+ ".entry {\n"
+ " border-style: inset;\n"
+ " border-width: 2;\n"
+ " background-color: @base_color;\n"
+ " color: @text_color;\n"
+ "}\n"
+ "\n"
+ ".entry:insensitive {\n"
+ " background-color: shade (@base_color, 0.9);\n"
+ " color: shade (@base_color, 0.7);\n"
+ "}\n"
+ ".entry:active {\n"
+ " background-color: #c4c2bd;\n"
+ " color: #000;\n"
+ "}\n"
+ "\n"
+ ".progressbar:prelight,\n"
+ ".entry.progressbar {\n"
+ " background-color: @selected_bg_color;\n"
+ " border-color: shade (@selected_bg_color, 0.7);\n"
+ " color: @selected_fg_color;\n"
+ "}\n"
+ "\n"
+ "GtkCheckButton:hover,\n"
+ "GtkCheckButton:selected,\n"
+ "GtkRadioButton:hover,\n"
+ "GtkRadioButton:selected {\n"
+ " background-color: shade (@bg_color, 1.05);\n"
+ "}\n"
+ "\n"
+ ".check, .radio,\n"
+ ".check:active, .radio:active,\n"
+ ".check:hover, .radio:hover {\n"
+ " background-color: @base_color;\n"
+ " border-color: @fg_color;\n"
+ " color: @text_color;\n"
+ " border-style: solid;\n"
+ " border-width: 1;\n"
+ "}\n"
+ "\n"
+ ".check:selected, .radio:selected {\n"
+ " background-color: @selected_bg_color;\n"
+ " color: @selected_fg_color;\n"
+ "}\n"
+ "\n"
+ ".menu.check, .menu.radio {\n"
+ " color: @fg_color;\n"
+ "}\n"
+ "\n"
+ ".menu:hover {\n"
+ " background-color: @selected_bg_color;\n"
+ " border-style: none;\n"
+ "}\n"
+ "\n"
+ ".popup {\n"
+ " border-style: outset;\n"
+ " border-width: 1;\n"
+ "}\n"
+ "\n"
+ ".viewport {\n"
+ " border-style: inset;\n"
+ " border-width: 2;\n"
+ "}\n"
+ "\n"
+ ".notebook {\n"
+ " border-style: outset;\n"
+ " border-width: 1;\n"
+ "}\n"
+ "\n"
+ ".frame {\n"
+ " border-style: inset;\n"
+ " border-width: 1;\n"
+ "}\n"
+ "\n"
+ ".menu,\n"
+ ".menubar,\n"
+ ".toolbar {\n"
+ " border-style: outset;\n"
+ " border-width: 1;\n"
+ "}\n"
+ "\n"
+ ".menu:hover,\n"
+ ".menubar:hover {\n"
+ " background-color: @selected_bg_color;\n"
+ " color: @selected_fg_color;\n"
+ "}\n"
+ "\n"
+ ".menu .check,\n"
+ ".menu .radio,\n"
+ ".menu .check:active,\n"
+ ".menu .radio:active {\n"
+ " border-style: none;\n"
+ "}\n"
+ "\n"
+ "GtkSpinButton.button {\n"
+ " border-width: 1;\n"
+ "}\n"
+ "\n"
+ ".scale.slider:hover,\n"
+ "GtkSpinButton.button:hover {\n"
+ " background-color: shade (@bg_color, 1.05);\n"
+ " border-color: shade (@bg_color, 0.8);\n"
+ "}\n"
+ "\n"
+ "GtkToggleButton.button:inconsistent {\n"
+ " border-style: outset;\n"
+ " border-width: 1px;\n"
+ " background-color: shade (@bg_color, 0.9);\n"
+ " border-color: shade (@bg_color, 0.7);\n"
+ "}\n"
+ "\n"
+ "GtkLabel:selected {\n"
+ " background-color: shade (@bg_color, 0.9);\n"
+ " color: @fg_color;\n"
+ "}\n"
+ "\n"
+ "GtkLabel:selected:focused {\n"
+ " background-color: @selected_bg_color;\n"
+ " color: @selected_fg_color;\n"
+ "}\n"
+ "\n"
+ ".spinner:active {\n"
+ " transition: 750ms linear loop;\n"
+ "}\n"
+ "\n";
+
+ provider = gtk_css_provider_new ();
+ gtk_css_provider_load_from_data (provider, str, -1, NULL);
+ }
+
+ return provider;
+}
+
+static gchar *
+css_provider_get_theme_dir (void)
+{
+ const gchar *var;
+ gchar *path;
+
+ var = g_getenv ("GTK_DATA_PREFIX");
+
+ if (var)
+ path = g_build_filename (var, "share", "themes", NULL);
+ else
+ path = g_build_filename (GTK_DATA_PREFIX, "share", "themes", NULL);
+
+ return path;
+}
+
+/**
+ * gtk_css_provider_get_named:
+ * @name: A theme name
+ * @variant: variant to load, for example, "dark", or %NULL for the default
+ *
+ * Loads a theme from the usual theme paths
+ *
+ * Returns: (transfer none): a #GtkCssProvider with the theme loaded.
+ * This memory is owned by GTK+, and you must not free it.
+ **/
+GtkCssProvider *
+gtk_css_provider_get_named (const gchar *name,
+ const gchar *variant)
+{
+ static GHashTable *themes = NULL;
+ GtkCssProvider *provider;
+
+ if (G_UNLIKELY (!themes))
+ themes = g_hash_table_new (g_str_hash, g_str_equal);
+
+ provider = g_hash_table_lookup (themes, name);
+
+ if (!provider)
+ {
+ const gchar *home_dir;
+ gchar *subpath, *path = NULL;
+
+ if (variant)
+ subpath = g_strdup_printf ("gtk-3.0" G_DIR_SEPARATOR_S "gtk-%s.css", variant);
+ else
+ subpath = g_strdup ("gtk-3.0" G_DIR_SEPARATOR_S "gtk.css");
+
+ /* First look in the users home directory
+ */
+ home_dir = g_get_home_dir ();
+ if (home_dir)
+ {
+ path = g_build_filename (home_dir, ".themes", name, subpath, NULL);
+
+ if (!g_file_test (path, G_FILE_TEST_EXISTS))
+ {
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ if (!path)
+ {
+ gchar *theme_dir = css_provider_get_theme_dir ();
+ path = g_build_filename (theme_dir, name, subpath, NULL);
+ g_free (theme_dir);
+
+ if (!g_file_test (path, G_FILE_TEST_EXISTS))
+ {
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ if (path)
+ {
+ GError *error = NULL;
+
+ provider = gtk_css_provider_new ();
+ gtk_css_provider_load_from_path (provider, path, &error);
+
+ if (error)
+ {
+ g_warning ("Could not load named theme \"%s\": %s", name, error->message);
+ g_error_free (error);
+
+ g_object_unref (provider);
+ provider = NULL;
+ }
+ else
+ g_hash_table_insert (themes, g_strdup (name), provider);
+ }
+ }
+
+ return provider;
+}
diff --git a/gtk/gtkcssprovider.h b/gtk/gtkcssprovider.h
new file mode 100644
index 0000000000..836abb01df
--- /dev/null
+++ b/gtk/gtkcssprovider.h
@@ -0,0 +1,79 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_CSS_PROVIDER_H__
+#define __GTK_CSS_PROVIDER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CSS_PROVIDER (gtk_css_provider_get_type ())
+#define GTK_CSS_PROVIDER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_CSS_PROVIDER, GtkCssProvider))
+#define GTK_CSS_PROVIDER_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_CSS_PROVIDER, GtkCssProviderClass))
+#define GTK_IS_CSS_PROVIDER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_CSS_PROVIDER))
+#define GTK_IS_CSS_PROVIDER_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GTK_TYPE_CSS_PROVIDER))
+#define GTK_CSS_PROVIDER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_CSS_PROVIDER, GtkCssProviderClass))
+
+#define GTK_CSS_PROVIDER_ERROR (gtk_css_provider_error_quark ())
+
+typedef enum
+{
+ GTK_CSS_PROVIDER_ERROR_FAILED
+} GtkCssProviderError;
+
+GQuark gtk_css_provider_error_quark (void);
+
+typedef struct _GtkCssProvider GtkCssProvider;
+typedef struct _GtkCssProviderClass GtkCssProviderClass;
+
+struct _GtkCssProvider
+{
+ GObject parent_instance;
+ gpointer priv;
+};
+
+struct _GtkCssProviderClass
+{
+ GObjectClass parent_class;
+};
+
+GType gtk_css_provider_get_type (void) G_GNUC_CONST;
+
+GtkCssProvider * gtk_css_provider_new (void);
+
+gboolean gtk_css_provider_load_from_data (GtkCssProvider *css_provider,
+ const gchar *data,
+ gssize length,
+ GError **error);
+gboolean gtk_css_provider_load_from_file (GtkCssProvider *css_provider,
+ GFile *file,
+ GError **error);
+gboolean gtk_css_provider_load_from_path (GtkCssProvider *css_provider,
+ const gchar *path,
+ GError **error);
+
+GtkCssProvider * gtk_css_provider_get_default (void);
+
+GtkCssProvider * gtk_css_provider_get_named (const gchar *name,
+ const gchar *variant);
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_PROVIDER_H__ */
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index cd70918e1e..2e584762e0 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -3419,8 +3419,7 @@ gtk_entry_draw_frame (GtkWidget *widget,
}
style = gtk_widget_get_style (widget);
- state = gtk_widget_has_focus (widget) ?
- GTK_STATE_ACTIVE : gtk_widget_get_state (widget);
+ state = gtk_widget_get_state (widget);
gtk_paint_flat_box (style, cr,
state, GTK_SHADOW_NONE,
@@ -6617,7 +6616,7 @@ gtk_entry_ensure_pixbuf (GtkEntry *entry,
GtkIconInfo *info;
GtkIconTheme *icon_theme;
GtkSettings *settings;
- GtkStateType state;
+ GtkStateFlags state;
GtkWidget *widget;
GdkScreen *screen;
gint width, height;
@@ -6633,8 +6632,8 @@ gtk_entry_ensure_pixbuf (GtkEntry *entry,
case GTK_IMAGE_PIXBUF:
break;
case GTK_IMAGE_STOCK:
- state = gtk_widget_get_state (widget);
- gtk_widget_set_state (widget, GTK_STATE_NORMAL);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_widget_set_state_flags (widget, 0, TRUE);
icon_info->pixbuf = gtk_widget_render_icon (widget,
icon_info->stock_id,
GTK_ICON_SIZE_MENU,
@@ -6644,7 +6643,7 @@ gtk_entry_ensure_pixbuf (GtkEntry *entry,
GTK_STOCK_MISSING_IMAGE,
GTK_ICON_SIZE_MENU,
NULL);
- gtk_widget_set_state (widget, state);
+ gtk_widget_set_state_flags (widget, state, TRUE);
break;
case GTK_IMAGE_ICON_NAME:
@@ -6665,13 +6664,13 @@ gtk_entry_ensure_pixbuf (GtkEntry *entry,
if (icon_info->pixbuf == NULL)
{
- state = gtk_widget_get_state (widget);
- gtk_widget_set_state (widget, GTK_STATE_NORMAL);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_widget_set_state_flags (widget, 0, TRUE);
icon_info->pixbuf = gtk_widget_render_icon (widget,
GTK_STOCK_MISSING_IMAGE,
GTK_ICON_SIZE_MENU,
NULL);
- gtk_widget_set_state (widget, state);
+ gtk_widget_set_state_flags (widget, state, TRUE);
}
}
break;
@@ -6699,13 +6698,13 @@ gtk_entry_ensure_pixbuf (GtkEntry *entry,
if (icon_info->pixbuf == NULL)
{
- state = gtk_widget_get_state (widget);
- gtk_widget_set_state (widget, GTK_STATE_NORMAL);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_widget_set_state_flags (widget, 0, TRUE);
icon_info->pixbuf = gtk_widget_render_icon (widget,
GTK_STOCK_MISSING_IMAGE,
GTK_ICON_SIZE_MENU,
NULL);
- gtk_widget_set_state (widget, state);
+ gtk_widget_set_state_flags (widget, state, TRUE);
}
}
break;
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index e7dd7f56c9..8a100aad0f 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -361,13 +361,36 @@ typedef enum
} GtkShadowType;
/* Widget states */
+
+/**
+ * GtkStateType:
+ *
+ * This type indicates the current state of a widget; the state determines how
+ * the widget is drawn. The #GtkStateType enumeration is also used to
+ * identify different colors in a #GtkStyle for drawing, so states can be
+ * used for subparts of a widget as well as entire widgets.
+ *
+ * @GTK_STATE_NORMAL: State during normal operation.
+ * @GTK_STATE_ACTIVE: State of a currently active widget, such as a depressed button.
+ * @GTK_STATE_PRELIGHT: State indicating that the mouse pointer is over
+ * the widget and the widget will respond to mouse clicks.
+ * @GTK_STATE_SELECTED: State of a selected item, such the selected row in a list.
+ * @GTK_STATE_INSENSITIVE: State indicating that the widget is
+ * unresponsive to user actions.
+ * @GTK_STATE_INCONSISTENT: The widget is inconsistent, such as checkbuttons
+ * or radiobuttons that aren't either set to %TRUE nor %FALSE,
+ * or buttons requiring the user attention.
+ * @GTK_STATE_FOCUSED: The widget has the keyboard focus.
+ */
typedef enum
{
GTK_STATE_NORMAL,
GTK_STATE_ACTIVE,
GTK_STATE_PRELIGHT,
GTK_STATE_SELECTED,
- GTK_STATE_INSENSITIVE
+ GTK_STATE_INSENSITIVE,
+ GTK_STATE_INCONSISTENT,
+ GTK_STATE_FOCUSED
} GtkStateType;
/* Style for toolbars */
@@ -557,6 +580,87 @@ typedef enum
GTK_SCROLL_NATURAL
} GtkScrollablePolicy;
+/**
+ * GtkStateFlags:
+ * @GTK_STATE_FLAG_ACTIVE: Widget is active.
+ * @GTK_STATE_FLAG_PRELIGHT: Widget has a mouse pointer over it.
+ * @GTK_STATE_FLAG_SELECTED: Widget is selected.
+ * @GTK_STATE_FLAG_INSENSITIVE: Widget is insensitive.
+ * @GTK_STATE_FLAG_INCONSISTENT: Widget is inconsistent.
+ * @GTK_STATE_FLAG_FOCUSED: Widget has the keyboard focus.
+ *
+ * Describes a widget state.
+ */
+typedef enum
+{
+ GTK_STATE_FLAG_ACTIVE = 1 << 0,
+ GTK_STATE_FLAG_PRELIGHT = 1 << 1,
+ GTK_STATE_FLAG_SELECTED = 1 << 2,
+ GTK_STATE_FLAG_INSENSITIVE = 1 << 3,
+ GTK_STATE_FLAG_INCONSISTENT = 1 << 4,
+ GTK_STATE_FLAG_FOCUSED = 1 << 5
+} GtkStateFlags;
+
+/**
+ * GtkRegionFlags:
+ * @GTK_REGION_EVEN: Region has an even number within a set.
+ * @GTK_REGION_ODD: Region has an odd number within a set.
+ * @GTK_REGION_FIRST: Region is the first one within a set.
+ * @GTK_REGION_LAST: Region is the last one within a set.
+ * @GTK_REGION_SORTED: Region is part of a sorted area.
+ *
+ * Describes a region within a widget.
+ */
+typedef enum {
+ GTK_REGION_EVEN = 1 << 0,
+ GTK_REGION_ODD = 1 << 1,
+ GTK_REGION_FIRST = 1 << 2,
+ GTK_REGION_LAST = 1 << 3,
+ GTK_REGION_SORTED = 1 << 5
+} GtkRegionFlags;
+
+/**
+ * GtkJunctionSides:
+ * @GTK_JUNCTION_NONE: No junctions.
+ * @GTK_JUNCTION_CORNER_TOPLEFT: Element connects on the top-left corner.
+ * @GTK_JUNCTION_CORNER_TOPRIGHT: Element connects on the top-right corner.
+ * @GTK_JUNCTION_CORNER_BOTTOMLEFT: Element connects on the bottom-left corner.
+ * @GTK_JUNCTION_CORNER_BOTTOMRIGHT: Element connects on the bottom-right corner.
+ * @GTK_JUNCTION_TOP: Element connects on the top side.
+ * @GTK_JUNCTION_BOTTOM: Element connects on the bottom side.
+ * @GTK_JUNCTION_LEFT: Element connects on the left side.
+ * @GTK_JUNCTION_RIGHT: Element connects on the right side.
+ *
+ * Describes how a rendered element connects to adjacent elements.
+ */
+typedef enum {
+ GTK_JUNCTION_NONE = 0,
+ GTK_JUNCTION_CORNER_TOPLEFT = 1 << 0,
+ GTK_JUNCTION_CORNER_TOPRIGHT = 1 << 1,
+ GTK_JUNCTION_CORNER_BOTTOMLEFT = 1 << 2,
+ GTK_JUNCTION_CORNER_BOTTOMRIGHT = 1 << 3,
+ GTK_JUNCTION_TOP = (GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_TOPRIGHT),
+ GTK_JUNCTION_BOTTOM = (GTK_JUNCTION_CORNER_BOTTOMLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT),
+ GTK_JUNCTION_LEFT = (GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMLEFT),
+ GTK_JUNCTION_RIGHT = (GTK_JUNCTION_CORNER_TOPRIGHT | GTK_JUNCTION_CORNER_BOTTOMRIGHT)
+} GtkJunctionSides;
+
+/**
+ * GtkBorderStyle:
+ * @GTK_BORDER_STYLE_NONE: No visible border
+ * @GTK_BORDER_STYLE_SOLID: A solid border
+ * @GTK_BORDER_STYLE_INSET: An inset border
+ * @GTK_BORDER_STYLE_OUTSET: An outset border
+ *
+ * Describes how the border of a UI element should be rendered.
+ */
+typedef enum {
+ GTK_BORDER_STYLE_NONE,
+ GTK_BORDER_STYLE_SOLID,
+ GTK_BORDER_STYLE_INSET,
+ GTK_BORDER_STYLE_OUTSET
+} GtkBorderStyle;
+
G_END_DECLS
diff --git a/gtk/gtkexpander.c b/gtk/gtkexpander.c
index 53eae8cfaa..236ff279f6 100644
--- a/gtk/gtkexpander.c
+++ b/gtk/gtkexpander.c
@@ -977,7 +977,9 @@ gtk_expander_enter_notify (GtkWidget *widget,
expander->priv->prelight = TRUE;
if (expander->priv->label_widget)
- gtk_widget_set_state (expander->priv->label_widget, GTK_STATE_PRELIGHT);
+ gtk_widget_set_state_flags (expander->priv->label_widget,
+ GTK_STATE_FLAG_PRELIGHT,
+ FALSE);
gtk_expander_redraw_expander (expander);
}
@@ -997,7 +999,8 @@ gtk_expander_leave_notify (GtkWidget *widget,
expander->priv->prelight = FALSE;
if (expander->priv->label_widget)
- gtk_widget_set_state (expander->priv->label_widget, GTK_STATE_NORMAL);
+ gtk_widget_unset_state_flags (expander->priv->label_widget,
+ GTK_STATE_FLAG_PRELIGHT);
gtk_expander_redraw_expander (expander);
}
@@ -1913,7 +1916,7 @@ gtk_expander_set_label_widget (GtkExpander *expander,
if (priv->label_widget)
{
- gtk_widget_set_state (priv->label_widget, GTK_STATE_NORMAL);
+ gtk_widget_set_state_flags (priv->label_widget, 0, TRUE);
gtk_widget_unparent (priv->label_widget);
}
@@ -1927,7 +1930,9 @@ gtk_expander_set_label_widget (GtkExpander *expander,
gtk_widget_set_parent (label_widget, widget);
if (priv->prelight)
- gtk_widget_set_state (label_widget, GTK_STATE_PRELIGHT);
+ gtk_widget_set_state_flags (label_widget,
+ GTK_STATE_FLAG_PRELIGHT,
+ FALSE);
}
if (gtk_widget_get_visible (widget))
diff --git a/gtk/gtkgradient.c b/gtk/gtkgradient.c
new file mode 100644
index 0000000000..a19b8fe583
--- /dev/null
+++ b/gtk/gtkgradient.c
@@ -0,0 +1,282 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gtkgradient.h"
+#include "gtkstyleproperties.h"
+#include "gtkintl.h"
+
+/**
+ * SECTION:gtkgradient
+ * @Short_description: Gradients
+ * @Title: GtkGradient
+ *
+ * GtkGradient is a boxed type that represents a gradient.
+ * It is the result of parsing a
+ * <link linkend="gtkcssprovider-gradients">gradient expression</link>.
+ * To obtain the gradient represented by a GtkGradient, it has to
+ * be resolved with gtk_gradient_resolve(), which replaces all
+ * symbolic color references by the colors they refer to (in a given
+ * context) and constructs a #cairo_pattern_t value.
+ *
+ * It is not normally necessary to deal directly with #GtkGradients,
+ * since they are mostly used behind the scenes by #GtkStyleContext and
+ * #GtkCssProvider.
+ */
+
+G_DEFINE_BOXED_TYPE (GtkGradient, gtk_gradient,
+ gtk_gradient_ref, gtk_gradient_unref)
+
+typedef struct ColorStop ColorStop;
+
+struct ColorStop
+{
+ gdouble offset;
+ GtkSymbolicColor *color;
+};
+
+struct _GtkGradient
+{
+ gdouble x0;
+ gdouble y0;
+ gdouble x1;
+ gdouble y1;
+ gdouble radius0;
+ gdouble radius1;
+
+ GArray *stops;
+
+ guint ref_count;
+};
+
+/**
+ * gtk_gradient_new_linear:
+ * @x0: X coordinate of the starting point
+ * @y0: Y coordinate of the starting point
+ * @x1: X coordinate of the end point
+ * @y1: Y coordinate of the end point
+ *
+ * Creates a new linear gradient along the line defined by (x0, y0) and (x1, y1). Before using the gradient
+ * a number of stop colors must be added through gtk_gradient_add_color_stop().
+ *
+ * Returns: A newly created #GtkGradient
+ *
+ * Since: 3.0
+ **/
+GtkGradient *
+gtk_gradient_new_linear (gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1)
+{
+ GtkGradient *gradient;
+
+ gradient = g_slice_new (GtkGradient);
+ gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
+
+ gradient->x0 = x0;
+ gradient->y0 = y0;
+ gradient->x1 = x1;
+ gradient->y1 = y1;
+ gradient->radius0 = 0;
+ gradient->radius1 = 0;
+
+ gradient->ref_count = 1;
+
+ return gradient;
+}
+
+/**
+ * gtk_gradient_new_radial:
+ * @x0: X coordinate of the start circle
+ * @y0: Y coordinate of the start circle
+ * @radius0: radius of the start circle
+ * @x1: X coordinate of the end circle
+ * @y1: Y coordinate of the end circle
+ * @radius1: radius of the end circle
+ *
+ * Creates a new radial gradient along the two circles defined by (x0, y0, radius0) and
+ * (x1, y1, radius1). Before using the gradient a number of stop colors must be added
+ * through gtk_gradient_add_color_stop().
+ *
+ * Returns: A newly created #GtkGradient
+ *
+ * Since: 3.0
+ **/
+GtkGradient *
+gtk_gradient_new_radial (gdouble x0,
+ gdouble y0,
+ gdouble radius0,
+ gdouble x1,
+ gdouble y1,
+ gdouble radius1)
+{
+ GtkGradient *gradient;
+
+ gradient = g_slice_new (GtkGradient);
+ gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
+
+ gradient->x0 = x0;
+ gradient->y0 = y0;
+ gradient->x1 = x1;
+ gradient->y1 = y1;
+ gradient->radius0 = radius0;
+ gradient->radius1 = radius1;
+
+ gradient->ref_count = 1;
+
+ return gradient;
+}
+
+/**
+ * gtk_gradient_add_color_stop:
+ * @gradient: a #GtkGradient
+ * @offset: offset for the color stop
+ * @color: color to use
+ *
+ * Adds a stop color to @gradient.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_gradient_add_color_stop (GtkGradient *gradient,
+ gdouble offset,
+ GtkSymbolicColor *color)
+{
+ ColorStop stop;
+
+ g_return_if_fail (gradient != NULL);
+
+ stop.offset = offset;
+ stop.color = gtk_symbolic_color_ref (color);
+
+ g_array_append_val (gradient->stops, stop);
+}
+
+/**
+ * gtk_gradient_ref:
+ * @gradient: a #GtkGradient
+ *
+ * Increases the reference count of @gradient.
+ *
+ * Returns: The same @gradient
+ *
+ * Since: 3.0
+ **/
+GtkGradient *
+gtk_gradient_ref (GtkGradient *gradient)
+{
+ g_return_val_if_fail (gradient != NULL, NULL);
+
+ gradient->ref_count++;
+
+ return gradient;
+}
+
+/**
+ * gtk_gradient_unref:
+ * @gradient: a #GtkGradient
+ *
+ * Decreases the reference count of @gradient, freeing its memory
+ * if the reference count reaches 0.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_gradient_unref (GtkGradient *gradient)
+{
+ g_return_if_fail (gradient != NULL);
+
+ gradient->ref_count--;
+
+ if (gradient->ref_count == 0)
+ {
+ guint i;
+
+ for (i = 0; i < gradient->stops->len; i++)
+ {
+ ColorStop *stop;
+
+ stop = &g_array_index (gradient->stops, ColorStop, i);
+ gtk_symbolic_color_unref (stop->color);
+ }
+
+ g_array_free (gradient->stops, TRUE);
+ g_slice_free (GtkGradient, gradient);
+ }
+}
+
+/**
+ * gtk_gradient_resolve:
+ * @gradient: a #GtkGradient
+ * @props: #GtkStyleProperties to use when resolving named colors
+ * @resolved_gradient: (out): return location for the resolved pattern
+ *
+ * If @gradient is resolvable, @resolved_gradient will be filled in
+ * with the resolved gradient as a cairo_pattern_t, and %TRUE will
+ * be returned. Generally, if @gradient can't be resolved, it is
+ * due to it being defined on top of a named color that doesn't
+ * exist in @props.
+ *
+ * Returns: %TRUE if the gradient has been resolved
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_gradient_resolve (GtkGradient *gradient,
+ GtkStyleProperties *props,
+ cairo_pattern_t **resolved_gradient)
+{
+ cairo_pattern_t *pattern;
+ guint i;
+
+ g_return_val_if_fail (gradient != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
+ g_return_val_if_fail (resolved_gradient != NULL, FALSE);
+
+ if (gradient->radius0 == 0 && gradient->radius1 == 0)
+ pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
+ gradient->x1, gradient->y1);
+ else
+ pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0,
+ gradient->radius0,
+ gradient->x1, gradient->y1,
+ gradient->radius1);
+
+ for (i = 0; i < gradient->stops->len; i++)
+ {
+ ColorStop *stop;
+ GdkRGBA color;
+
+ stop = &g_array_index (gradient->stops, ColorStop, i);
+
+ if (!gtk_symbolic_color_resolve (stop->color, props, &color))
+ {
+ cairo_pattern_destroy (pattern);
+ return FALSE;
+ }
+
+ cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
+ color.red, color.green,
+ color.blue, color.alpha);
+ }
+
+ *resolved_gradient = pattern;
+ return TRUE;
+}
diff --git a/gtk/gtkgradient.h b/gtk/gtkgradient.h
new file mode 100644
index 0000000000..f097c40fd0
--- /dev/null
+++ b/gtk/gtkgradient.h
@@ -0,0 +1,61 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_GRADIENT_H__
+#define __GTK_GRADIENT_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkstyleproperties.h>
+#include <gtk/gtksymboliccolor.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_GRADIENT (gtk_gradient_get_type ())
+
+GType gtk_gradient_get_type (void) G_GNUC_CONST;
+
+GtkGradient * gtk_gradient_new_linear (gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1);
+GtkGradient * gtk_gradient_new_radial (gdouble x0,
+ gdouble y0,
+ gdouble radius0,
+ gdouble x1,
+ gdouble y1,
+ gdouble radius1);
+
+void gtk_gradient_add_color_stop (GtkGradient *gradient,
+ gdouble offset,
+ GtkSymbolicColor *color);
+
+GtkGradient * gtk_gradient_ref (GtkGradient *gradient);
+void gtk_gradient_unref (GtkGradient *gradient);
+
+gboolean gtk_gradient_resolve (GtkGradient *gradient,
+ GtkStyleProperties *props,
+ cairo_pattern_t **resolved_gradient);
+
+G_END_DECLS
+
+#endif /* __GTK_GRADIENT_H__ */
diff --git a/gtk/gtkiconfactory.c b/gtk/gtkiconfactory.c
index 92fea10119..5b21c62fd6 100644
--- a/gtk/gtkiconfactory.c
+++ b/gtk/gtkiconfactory.c
@@ -796,7 +796,7 @@ icon_size_settings_changed (GtkSettings *settings,
{
icon_size_set_all_from_settings (settings);
- gtk_rc_reset_styles (settings);
+ gtk_style_context_reset_widgets (_gtk_settings_get_screen (settings));
}
static void
@@ -1083,12 +1083,12 @@ gtk_icon_size_get_name (GtkIconSize size)
static GdkPixbuf *find_in_cache (GtkIconSet *icon_set,
- GtkStyle *style,
+ GtkStyleContext *style_context,
GtkTextDirection direction,
GtkStateType state,
GtkIconSize size);
static void add_to_cache (GtkIconSet *icon_set,
- GtkStyle *style,
+ GtkStyleContext *style_context,
GtkTextDirection direction,
GtkStateType state,
GtkIconSize size,
@@ -1102,9 +1102,9 @@ static void clear_cache (GtkIconSet *icon_set,
static GSList* copy_cache (GtkIconSet *icon_set,
GtkIconSet *copy_recipient);
static void attach_to_style (GtkIconSet *icon_set,
- GtkStyle *style);
+ GtkStyleContext *style_context);
static void detach_from_style (GtkIconSet *icon_set,
- GtkStyle *style);
+ GtkStyleContext *style_context);
static void style_dnotify (gpointer data);
struct _GtkIconSet
@@ -1369,12 +1369,8 @@ ensure_filename_pixbuf (GtkIconSet *icon_set,
static GdkPixbuf *
render_icon_name_pixbuf (GtkIconSource *icon_source,
- GtkStyle *style,
- GtkTextDirection direction,
- GtkStateType state,
- GtkIconSize size,
- GtkWidget *widget,
- const char *detail)
+ GtkStyleContext *context,
+ GtkIconSize size)
{
GdkPixbuf *pixbuf;
GdkPixbuf *tmp_pixbuf;
@@ -1386,17 +1382,7 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
gint *sizes, *s, dist;
GError *error = NULL;
- if (widget && gtk_widget_has_screen (widget))
- screen = gtk_widget_get_screen (widget);
- else if (style && style->visual)
- screen = gdk_visual_get_screen (style->visual);
- else
- {
- screen = gdk_screen_get_default ();
- GTK_NOTE (MULTIHEAD,
- g_warning ("Using the default screen for gtk_icon_source_render_icon()"));
- }
-
+ screen = gtk_style_context_get_screen (context);
icon_theme = gtk_icon_theme_get_for_screen (screen);
settings = gtk_settings_get_for_screen (screen);
@@ -1487,9 +1473,7 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
tmp_source.type = GTK_ICON_SOURCE_PIXBUF;
tmp_source.source.pixbuf = tmp_pixbuf;
- pixbuf = gtk_style_render_icon (style, &tmp_source,
- direction, state, -1,
- widget, detail);
+ pixbuf = gtk_render_icon_pixbuf (context, &tmp_source, -1);
if (!pixbuf)
g_warning ("Failed to render icon");
@@ -1501,12 +1485,10 @@ render_icon_name_pixbuf (GtkIconSource *icon_source,
static GdkPixbuf *
find_and_render_icon_source (GtkIconSet *icon_set,
- GtkStyle *style,
+ GtkStyleContext *context,
GtkTextDirection direction,
GtkStateType state,
- GtkIconSize size,
- GtkWidget *widget,
- const char *detail)
+ GtkIconSize size)
{
GSList *failed = NULL;
GdkPixbuf *pixbuf = NULL;
@@ -1535,9 +1517,7 @@ find_and_render_icon_source (GtkIconSet *icon_set,
break;
/* Fall through */
case GTK_ICON_SOURCE_PIXBUF:
- pixbuf = gtk_style_render_icon (style, source,
- direction, state, size,
- widget, detail);
+ pixbuf = gtk_render_icon_pixbuf (context, source, size);
if (!pixbuf)
{
g_warning ("Failed to render icon");
@@ -1546,9 +1526,7 @@ find_and_render_icon_source (GtkIconSet *icon_set,
break;
case GTK_ICON_SOURCE_ICON_NAME:
case GTK_ICON_SOURCE_STATIC_ICON_NAME:
- pixbuf = render_icon_name_pixbuf (source, style,
- direction, state, size,
- widget, detail);
+ pixbuf = render_icon_name_pixbuf (source, context, size);
if (!pixbuf)
failed = g_slist_prepend (failed, source);
break;
@@ -1565,12 +1543,10 @@ find_and_render_icon_source (GtkIconSet *icon_set,
extern GtkIconCache *_builtin_cache;
static GdkPixbuf*
-render_fallback_image (GtkStyle *style,
+render_fallback_image (GtkStyleContext *context,
GtkTextDirection direction,
GtkStateType state,
- GtkIconSize size,
- GtkWidget *widget,
- const char *detail)
+ GtkIconSize size)
{
/* This icon can be used for any direction/state/size */
static GtkIconSource fallback_source = GTK_ICON_SOURCE_INIT (TRUE, TRUE, TRUE);
@@ -1590,13 +1566,69 @@ render_fallback_image (GtkStyle *style,
g_object_unref (pixbuf);
}
- return gtk_style_render_icon (style,
- &fallback_source,
- direction,
- state,
- size,
- widget,
- detail);
+ return gtk_render_icon_pixbuf (context, &fallback_source, size);
+}
+
+/**
+ * gtk_icon_set_render_icon_pixbuf:
+ * @icon_set: a #GtkIconSet
+ * @context: a #GtkStyleContext
+ * @size: (type int): icon size. A size of (GtkIconSize)-1
+ * means render at the size of the source and don't scale.
+ *
+ * Renders an icon using gtk_render_icon_pixbuf(). In most cases,
+ * gtk_widget_render_icon() is better, since it automatically provides
+ * most of the arguments from the current widget settings. This
+ * function never returns %NULL; if the icon can't be rendered
+ * (perhaps because an image file fails to load), a default "missing
+ * image" icon will be returned instead.
+ *
+ * Return value: (transfer full): a #GdkPixbuf to be displayed
+ *
+ * Since: 3.0
+ */
+GdkPixbuf *
+gtk_icon_set_render_icon_pixbuf (GtkIconSet *icon_set,
+ GtkStyleContext *context,
+ GtkIconSize size)
+{
+ GdkPixbuf *icon = NULL;
+ GtkStateFlags flags = 0;
+ GtkStateType state;
+ GtkTextDirection direction;
+
+ g_return_val_if_fail (icon_set != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+
+ flags = gtk_style_context_get_state (context);
+ if (flags & GTK_STATE_FLAG_INSENSITIVE)
+ state = GTK_STATE_INSENSITIVE;
+ else if (flags & GTK_STATE_FLAG_PRELIGHT)
+ state = GTK_STATE_PRELIGHT;
+ else
+ state = GTK_STATE_NORMAL;
+
+ direction = gtk_style_context_get_direction (context);
+
+ if (icon_set->sources)
+ {
+ icon = find_in_cache (icon_set, context, direction, state, size);
+ if (icon)
+ {
+ g_object_ref (icon);
+ return icon;
+ }
+ }
+
+ if (icon_set->sources)
+ icon = find_and_render_icon_source (icon_set, context, direction, state, size);
+
+ if (icon == NULL)
+ icon = render_fallback_image (context, direction, state, size);
+
+ add_to_cache (icon_set, context, direction, state, size, icon);
+
+ return icon;
}
/**
@@ -1622,6 +1654,8 @@ render_fallback_image (GtkStyle *style,
* image" icon will be returned instead.
*
* Return value: (transfer full): a #GdkPixbuf to be displayed
+ *
+ * Deprecated: 3.0: Use gtk_icon_set_render_icon_pixbuf() instead
*/
GdkPixbuf*
gtk_icon_set_render_icon (GtkIconSet *icon_set,
@@ -1633,34 +1667,47 @@ gtk_icon_set_render_icon (GtkIconSet *icon_set,
const char *detail)
{
GdkPixbuf *icon;
+ GtkStyleContext *context = NULL;
+ GtkStateFlags flags = 0;
g_return_val_if_fail (icon_set != NULL, NULL);
g_return_val_if_fail (style == NULL || GTK_IS_STYLE (style), NULL);
- if (icon_set->sources == NULL)
- return render_fallback_image (style, direction, state, size, widget, detail);
-
- if (detail == NULL)
+ if (style && gtk_style_has_context (style))
{
- icon = find_in_cache (icon_set, style, direction,
- state, size);
-
- if (icon)
- {
- g_object_ref (icon);
- return icon;
- }
+ g_object_get (style, "context", &context, NULL);
+ /* g_object_get returns a refed object */
+ if (context)
+ g_object_unref (context);
+ }
+ else if (widget)
+ {
+ context = gtk_widget_get_style_context (widget);
}
+ if (!context)
+ return render_fallback_image (context, direction, state, size);
- icon = find_and_render_icon_source (icon_set, style, direction, state, size,
- widget, detail);
+ gtk_style_context_save (context);
- if (icon == NULL)
- icon = render_fallback_image (style, direction, state, size, widget, detail);
+ switch (state)
+ {
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
+ break;
+ }
+
+ gtk_style_context_set_state (context, flags);
+ gtk_style_context_set_direction (context, direction);
- if (detail == NULL)
- add_to_cache (icon_set, style, direction, state, size, icon);
+ icon = gtk_icon_set_render_icon_pixbuf (icon_set, context, size);
+
+ gtk_style_context_restore (context);
return icon;
}
@@ -2376,7 +2423,7 @@ struct _CachedIcon
/* These must all match to use the cached pixbuf.
* If any don't match, we must re-render the pixbuf.
*/
- GtkStyle *style;
+ GtkStyleContext *style;
GtkTextDirection direction;
GtkStateType state;
GtkIconSize size;
@@ -2398,16 +2445,14 @@ static void
cached_icon_free (CachedIcon *icon)
{
g_object_unref (icon->pixbuf);
-
- if (icon->style)
- g_object_unref (icon->style);
+ g_object_unref (icon->style);
g_free (icon);
}
static GdkPixbuf *
find_in_cache (GtkIconSet *icon_set,
- GtkStyle *style,
+ GtkStyleContext *style_context,
GtkTextDirection direction,
GtkStateType state,
GtkIconSize size)
@@ -2423,7 +2468,7 @@ find_in_cache (GtkIconSet *icon_set,
{
CachedIcon *icon = tmp_list->data;
- if (icon->style == style &&
+ if (icon->style == style_context &&
icon->direction == direction &&
icon->state == state &&
(size == (GtkIconSize)-1 || icon->size == size))
@@ -2448,7 +2493,7 @@ find_in_cache (GtkIconSet *icon_set,
static void
add_to_cache (GtkIconSet *icon_set,
- GtkStyle *style,
+ GtkStyleContext *style_context,
GtkTextDirection direction,
GtkStateType state,
GtkIconSize size,
@@ -2460,26 +2505,16 @@ add_to_cache (GtkIconSet *icon_set,
g_object_ref (pixbuf);
- /* We have to ref the style, since if the style was finalized
- * its address could be reused by another style, creating a
- * really weird bug
- */
-
- if (style)
- g_object_ref (style);
-
icon = g_new (CachedIcon, 1);
icon_set->cache = g_slist_prepend (icon_set->cache, icon);
icon_set->cache_size++;
- icon->style = style;
+ icon->style = g_object_ref (style_context);
icon->direction = direction;
icon->state = state;
icon->size = size;
icon->pixbuf = pixbuf;
-
- if (icon->style)
- attach_to_style (icon_set, icon->style);
+ attach_to_style (icon_set, icon->style);
if (icon_set->cache_size >= NUM_CACHED_ICONS)
{
@@ -2512,7 +2547,7 @@ clear_cache (GtkIconSet *icon_set,
gboolean style_detach)
{
GSList *cache, *tmp_list;
- GtkStyle *last_style = NULL;
+ GtkStyleContext *last_style = NULL;
cache = icon_set->cache;
icon_set->cache = NULL;
@@ -2561,11 +2596,8 @@ copy_cache (GtkIconSet *icon_set,
*icon_copy = *icon;
- if (icon_copy->style)
- {
- attach_to_style (copy_recipient, icon_copy->style);
- g_object_ref (icon_copy->style);
- }
+ attach_to_style (copy_recipient, icon_copy->style);
+ g_object_ref (icon_copy->style);
g_object_ref (icon_copy->pixbuf);
@@ -2580,18 +2612,18 @@ copy_cache (GtkIconSet *icon_set,
}
static void
-attach_to_style (GtkIconSet *icon_set,
- GtkStyle *style)
+attach_to_style (GtkIconSet *icon_set,
+ GtkStyleContext *style_context)
{
GHashTable *table;
- table = g_object_get_qdata (G_OBJECT (style),
+ table = g_object_get_qdata (G_OBJECT (style_context),
g_quark_try_string ("gtk-style-icon-sets"));
if (table == NULL)
{
table = g_hash_table_new (NULL, NULL);
- g_object_set_qdata_full (G_OBJECT (style),
+ g_object_set_qdata_full (G_OBJECT (style_context),
g_quark_from_static_string ("gtk-style-icon-sets"),
table,
style_dnotify);
@@ -2601,12 +2633,12 @@ attach_to_style (GtkIconSet *icon_set,
}
static void
-detach_from_style (GtkIconSet *icon_set,
- GtkStyle *style)
+detach_from_style (GtkIconSet *icon_set,
+ GtkStyleContext *style_context)
{
GHashTable *table;
- table = g_object_get_qdata (G_OBJECT (style),
+ table = g_object_get_qdata (G_OBJECT (style_context),
g_quark_try_string ("gtk-style-icon-sets"));
if (table != NULL)
diff --git a/gtk/gtkiconfactory.h b/gtk/gtkiconfactory.h
index 1019cf6b41..69ab250b0e 100644
--- a/gtk/gtkiconfactory.h
+++ b/gtk/gtkiconfactory.h
@@ -132,7 +132,6 @@ GdkPixbuf* gtk_icon_set_render_icon (GtkIconSet *icon_set,
GtkWidget *widget,
const char *detail);
-
void gtk_icon_set_add_source (GtkIconSet *icon_set,
const GtkIconSource *source);
diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c
index cd55c3f140..2f41849967 100644
--- a/gtk/gtkicontheme.c
+++ b/gtk/gtkicontheme.c
@@ -606,10 +606,7 @@ reset_styles_idle (gpointer user_data)
priv = icon_theme->priv;
if (priv->screen && priv->is_screen_singleton)
- {
- GtkSettings *settings = gtk_settings_get_for_screen (priv->screen);
- gtk_rc_reset_styles (settings);
- }
+ gtk_style_context_reset_widgets (priv->screen);
priv->reset_styles_idle = 0;
@@ -3071,6 +3068,16 @@ gdk_color_to_css (GdkColor *color)
color->blue >> 8);
}
+static gchar *
+gdk_rgba_to_css (GdkRGBA *color)
+{
+ return g_strdup_printf ("rgba(%d,%d,%d,%f)",
+ (gint)(color->red * 255),
+ (gint)(color->green * 255),
+ (gint)(color->blue * 255),
+ color->alpha);
+}
+
static GdkPixbuf *
_gtk_icon_info_load_symbolic_internal (GtkIconInfo *icon_info,
const gchar *css_fg,
@@ -3236,6 +3243,83 @@ gtk_icon_info_load_symbolic (GtkIconInfo *icon_info,
}
/**
+ * gtk_icon_info_load_symbolic_for_context:
+ * @icon_info: a #GtkIconInfo
+ * context: a #GtkStyleContext
+ * @was_symbolic: (allow-none): a #gboolean, returns whether the loaded icon
+ * was a symbolic one and whether the @fg color was applied to it.
+ * @error: (allow-none): location to store error information on failure,
+ * or %NULL.
+ *
+ * Loads an icon, modifying it to match the system colors for the foreground,
+ * success, warning and error colors provided. If the icon is not a symbolic
+ * one, the function will return the result from gtk_icon_info_load_icon().
+ *
+ * This allows loading symbolic icons that will match the system theme.
+ *
+ * See gtk_icon_info_load_symbolic() for more details.
+ *
+ * Return value: (transfer full): a #GdkPixbuf representing the loaded icon
+ *
+ * Since: 3.0
+ **/
+GdkPixbuf *
+gtk_icon_info_load_symbolic_for_context (GtkIconInfo *icon_info,
+ GtkStyleContext *context,
+ gboolean *was_symbolic,
+ GError **error)
+{
+ GdkPixbuf *pixbuf;
+ GdkRGBA *color = NULL;
+ GdkRGBA rgba;
+ gchar *css_fg = NULL, *css_success;
+ gchar *css_warning, *css_error;
+ GtkStateFlags state;
+
+ if (!icon_info->filename ||
+ !g_str_has_suffix (icon_info->filename, "-symbolic.svg"))
+ {
+ if (was_symbolic)
+ *was_symbolic = FALSE;
+ return gtk_icon_info_load_icon (icon_info, error);
+ }
+
+ if (was_symbolic)
+ *was_symbolic = TRUE;
+
+ state = gtk_style_context_get_state (context);
+ gtk_style_context_get (context, state, "color", &color, NULL);
+ if (color)
+ {
+ css_fg = gdk_rgba_to_css (color);
+ gdk_rgba_free (color);
+ }
+
+ css_success = css_warning = css_error = NULL;
+
+ if (gtk_style_context_lookup_color (context, "success_color", &rgba))
+ css_success = gdk_rgba_to_css (&rgba);
+
+ if (gtk_style_context_lookup_color (context, "warning_color", &rgba))
+ css_warning = gdk_rgba_to_css (&rgba);
+
+ if (gtk_style_context_lookup_color (context, "error_color", &rgba))
+ css_error = gdk_rgba_to_css (&rgba);
+
+ pixbuf = _gtk_icon_info_load_symbolic_internal (icon_info,
+ css_fg, css_success,
+ css_warning, css_error,
+ error);
+
+ g_free (css_fg);
+ g_free (css_success);
+ g_free (css_warning);
+ g_free (css_error);
+
+ return pixbuf;
+}
+
+/**
* gtk_icon_info_load_symbolic_for_style:
* @icon_info: a #GtkIconInfo
* @style: a #GtkStyle to take the colors from
@@ -3256,6 +3340,8 @@ gtk_icon_info_load_symbolic (GtkIconInfo *icon_info,
* Return value: (transfer full): a #GdkPixbuf representing the loaded icon
*
* Since: 3.0
+ *
+ * Deprecated: 3.0: Use gtk_icon_info_load_symbolic_for_context() instead
**/
GdkPixbuf *
gtk_icon_info_load_symbolic_for_style (GtkIconInfo *icon_info,
diff --git a/gtk/gtkicontheme.h b/gtk/gtkicontheme.h
index f493ba98a0..9ee87e16f1 100644
--- a/gtk/gtkicontheme.h
+++ b/gtk/gtkicontheme.h
@@ -27,6 +27,7 @@
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gdk/gdk.h>
#include <gtk/gtkstyle.h>
+#include <gtk/gtkstylecontext.h>
G_BEGIN_DECLS
@@ -178,11 +179,17 @@ GdkPixbuf * gtk_icon_info_load_symbolic (GtkIconInfo *icon_info
GdkRGBA *error_color,
gboolean *was_symbolic,
GError **error);
+GdkPixbuf * gtk_icon_info_load_symbolic_for_context (GtkIconInfo *icon_info,
+ GtkStyleContext *context,
+ gboolean *was_symbolic,
+ GError **error);
+#ifndef GTK_DISABLE_DEPRECATED
GdkPixbuf * gtk_icon_info_load_symbolic_for_style (GtkIconInfo *icon_info,
GtkStyle *style,
GtkStateType state,
gboolean *was_symbolic,
GError **error);
+#endif
void gtk_icon_info_set_raw_coordinates (GtkIconInfo *icon_info,
gboolean raw_coordinates);
diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c
index 8a3defdb80..17ce6af25b 100644
--- a/gtk/gtkiconview.c
+++ b/gtk/gtkiconview.c
@@ -5618,7 +5618,7 @@ update_text_cell (GtkIconView *icon_view)
g_object_set (info->cell,
"alignment", PANGO_ALIGN_CENTER,
"wrap-mode", PANGO_WRAP_WORD_CHAR,
- "xalign", 0.0,
+ "xalign", 0.5,
"yalign", 0.0,
NULL);
else
@@ -5626,7 +5626,7 @@ update_text_cell (GtkIconView *icon_view)
"alignment", PANGO_ALIGN_LEFT,
"wrap-mode", PANGO_WRAP_WORD_CHAR,
"xalign", 0.0,
- "yalign", 0.0,
+ "yalign", 0.5,
NULL);
}
}
diff --git a/gtk/gtkimage.c b/gtk/gtkimage.c
index ce0fcbef6a..184451ef23 100644
--- a/gtk/gtkimage.c
+++ b/gtk/gtkimage.c
@@ -1469,15 +1469,15 @@ ensure_pixbuf_for_icon_name (GtkImage *image,
MIN (width, height), flags);
if (info)
{
- GtkStyle *style;
+ GtkStyleContext *context;
gboolean was_symbolic;
- style = gtk_widget_get_style (GTK_WIDGET (image));
+ context = gtk_widget_get_style_context (GTK_WIDGET (image));
priv->data.name.pixbuf =
- gtk_icon_info_load_symbolic_for_style (info,
- style, state,
- &was_symbolic,
- NULL);
+ gtk_icon_info_load_symbolic_for_context (info,
+ context,
+ &was_symbolic,
+ NULL);
priv->was_symbolic = was_symbolic;
gtk_icon_info_free (info);
}
@@ -1544,15 +1544,15 @@ ensure_pixbuf_for_gicon (GtkImage *image,
MIN (width, height), flags);
if (info)
{
- GtkStyle *style;
+ GtkStyleContext *context;
gboolean was_symbolic;
- style = gtk_widget_get_style (GTK_WIDGET (image));
+ context = gtk_widget_get_style_context (GTK_WIDGET (image));
priv->data.gicon.pixbuf =
- gtk_icon_info_load_symbolic_for_style (info,
- style, state,
- &was_symbolic,
- NULL);
+ gtk_icon_info_load_symbolic_for_context (info,
+ context,
+ &was_symbolic,
+ NULL);
priv->was_symbolic = was_symbolic;
gtk_icon_info_free (info);
}
diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c
index d9c70e5b67..1270bb0563 100644
--- a/gtk/gtkimcontextsimple.c
+++ b/gtk/gtkimcontextsimple.c
@@ -692,7 +692,7 @@ check_hex (GtkIMContextSimple *context_simple,
static void
beep_window (GdkWindow *window)
{
- GdkScreen *screen = gdk_window_get_screen (GDK_DRAWABLE (window));
+ GdkScreen *screen = gdk_window_get_screen (window);
gboolean beep;
g_object_get (gtk_settings_get_for_screen (screen),
diff --git a/gtk/gtkimmodule.c b/gtk/gtkimmodule.c
index 788631d2ee..868cfe17c2 100644
--- a/gtk/gtkimmodule.c
+++ b/gtk/gtkimmodule.c
@@ -681,9 +681,9 @@ _gtk_im_module_get_default_context_id (GdkWindow *client_window)
/* Check if the certain immodule is set in XSETTINGS.
*/
- if (GDK_IS_DRAWABLE (client_window))
+ if (GDK_IS_WINDOW (client_window))
{
- screen = gdk_window_get_screen (GDK_DRAWABLE (client_window));
+ screen = gdk_window_get_screen (client_window);
settings = gtk_settings_get_for_screen (screen);
g_object_get (G_OBJECT (settings), "gtk-im-module", &tmp, NULL);
if (tmp)
diff --git a/gtk/gtkimmulticontext.c b/gtk/gtkimmulticontext.c
index a8357babfe..f2454fcb4a 100644
--- a/gtk/gtkimmulticontext.c
+++ b/gtk/gtkimmulticontext.c
@@ -297,7 +297,7 @@ gtk_im_multicontext_set_client_window (GtkIMContext *context,
if (window)
{
- screen = gdk_window_get_screen (GDK_DRAWABLE (window));
+ screen = gdk_window_get_screen (window);
settings = gtk_settings_get_for_screen (screen);
connected = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (settings),
diff --git a/gtk/gtkinfobar.c b/gtk/gtkinfobar.c
index 86859786e0..a7879606b4 100644
--- a/gtk/gtkinfobar.c
+++ b/gtk/gtkinfobar.c
@@ -495,19 +495,21 @@ gtk_info_bar_update_colors (GtkInfoBar *info_bar)
{
GtkWidget *widget = GTK_WIDGET (info_bar);
GtkInfoBarPrivate *priv = info_bar->priv;
- GdkColor info_default_border_color = { 0, 0xb800, 0xad00, 0x9d00 };
- GdkColor info_default_fill_color = { 0, 0xff00, 0xff00, 0xbf00 };
- GdkColor warning_default_border_color = { 0, 0xb000, 0x7a00, 0x2b00 };
- GdkColor warning_default_fill_color = { 0, 0xfc00, 0xaf00, 0x3e00 };
- GdkColor question_default_border_color = { 0, 0x6200, 0x7b00, 0xd960 };
- GdkColor question_default_fill_color = { 0, 0x8c00, 0xb000, 0xd700 };
- GdkColor error_default_border_color = { 0, 0xa800, 0x2700, 0x2700 };
- GdkColor error_default_fill_color = { 0, 0xf000, 0x3800, 0x3800 };
- GdkColor other_default_border_color = { 0, 0xb800, 0xad00, 0x9d00 };
- GdkColor other_default_fill_color = { 0, 0xff00, 0xff00, 0xbf00 };
- GdkColor *fg, *bg;
- GdkColor sym_fg, sym_bg;
- GtkStyle *style;
+ GdkRGBA info_default_border_color = { 0.71, 0.67, 0.61, 1.0 };
+ GdkRGBA info_default_fill_color = { 0.99, 0.99, 0.74, 1.0 };
+ GdkRGBA warning_default_border_color = { 0.68, 0.47, 0.16, 1.0 };
+ GdkRGBA warning_default_fill_color = { 0.98, 0.68, 0.24, 1.0 };
+ GdkRGBA question_default_border_color = { 0.38, 0.48, 0.84, 1.0 };
+ GdkRGBA question_default_fill_color = { 0.54, 0.68, 0.83, 1.0 };
+ GdkRGBA error_default_border_color = { 0.65, 0.15, 0.15, 1.0 };
+ GdkRGBA error_default_fill_color = { 0.93, 0.21, 0.21, 1.0 };
+ GdkRGBA other_default_border_color = { 0.71, 0.67, 0.61, 1.0 };
+ GdkRGBA other_default_fill_color = { 0.99, 0.99, 0.74, 1.0 };
+ GdkRGBA *fg, *bg;
+ GdkRGBA sym_fg, sym_bg;
+ GdkRGBA *color, *bg_color;
+ GtkStyleContext *context;
+
const char* fg_color_name[] = {
"info_fg_color",
"warning_fg_color",
@@ -523,10 +525,10 @@ gtk_info_bar_update_colors (GtkInfoBar *info_bar)
"other_bg_color"
};
- style = gtk_widget_get_style (widget);
+ context = gtk_widget_get_style_context (widget);
- if (gtk_style_lookup_color (style, fg_color_name[priv->message_type], &sym_fg) &&
- gtk_style_lookup_color (style, bg_color_name[priv->message_type], &sym_bg))
+ if (gtk_style_context_lookup_color (context, fg_color_name[priv->message_type], &sym_fg) &&
+ gtk_style_context_lookup_color (context, bg_color_name[priv->message_type], &sym_bg))
{
fg = &sym_fg;
bg = &sym_bg;
@@ -567,10 +569,17 @@ gtk_info_bar_update_colors (GtkInfoBar *info_bar)
}
}
- if (!gdk_color_equal (bg, &style->bg[GTK_STATE_NORMAL]))
- gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, bg);
- if (!gdk_color_equal (fg, &style->fg[GTK_STATE_NORMAL]))
- gtk_widget_modify_fg (widget, GTK_STATE_NORMAL, fg);
+ gtk_style_context_get (context, 0,
+ "color", &color,
+ "background-color", &bg_color,
+ NULL);
+ if (!gdk_rgba_equal (bg_color, bg))
+ gtk_widget_override_background_color (widget, 0, bg);
+ if (!gdk_rgba_equal (color, fg))
+ gtk_widget_override_color (widget, 0, fg);
+
+ gdk_rgba_free (color);
+ gdk_rgba_free (bg_color);
}
static void
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 39655b8518..55bc6c335e 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -221,8 +221,7 @@ static void gtk_label_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void gtk_label_state_changed (GtkWidget *widget,
GtkStateType state);
-static void gtk_label_style_set (GtkWidget *widget,
- GtkStyle *previous_style);
+static void gtk_label_style_updated (GtkWidget *widget);
static void gtk_label_direction_changed (GtkWidget *widget,
GtkTextDirection previous_dir);
static gint gtk_label_draw (GtkWidget *widget,
@@ -407,7 +406,7 @@ gtk_label_class_init (GtkLabelClass *class)
widget_class->destroy = gtk_label_destroy;
widget_class->size_allocate = gtk_label_size_allocate;
widget_class->state_changed = gtk_label_state_changed;
- widget_class->style_set = gtk_label_style_set;
+ widget_class->style_updated = gtk_label_style_updated;
widget_class->query_tooltip = gtk_label_query_tooltip;
widget_class->direction_changed = gtk_label_direction_changed;
widget_class->draw = gtk_label_draw;
@@ -3806,8 +3805,7 @@ gtk_label_state_changed (GtkWidget *widget,
}
static void
-gtk_label_style_set (GtkWidget *widget,
- GtkStyle *previous_style)
+gtk_label_style_updated (GtkWidget *widget)
{
GtkLabel *label = GTK_LABEL (widget);
@@ -4065,7 +4063,8 @@ gtk_label_draw (GtkWidget *widget,
GtkLabelPrivate *priv = label->priv;
GtkLabelSelectionInfo *info = priv->select_info;
GtkAllocation allocation;
- GtkStyle *style;
+ GtkStyleContext *context;
+ GtkStateFlags state;
GdkWindow *window;
gint x, y;
@@ -4073,22 +4072,22 @@ gtk_label_draw (GtkWidget *widget,
if (priv->text && (*priv->text != '\0'))
{
+ GdkRGBA *bg_color, *fg_color;
+
get_layout_location (label, &x, &y);
- style = gtk_widget_get_style (widget);
+ context = gtk_widget_get_style_context (widget);
window = gtk_widget_get_window (widget);
gtk_widget_get_allocation (widget, &allocation);
cairo_translate (cr, -allocation.x, -allocation.y);
- gtk_paint_layout (style,
- cr,
- gtk_widget_get_state (widget),
- FALSE,
- widget,
- "label",
- x, y,
- priv->layout);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_set_state (context, state);
+
+ gtk_render_layout (context, cr,
+ x, y,
+ priv->layout);
if (info &&
(info->selection_anchor != info->selection_end))
@@ -4121,19 +4120,28 @@ gtk_label_draw (GtkWidget *widget,
gdk_cairo_region (cr, clip);
cairo_clip (cr);
- state = GTK_STATE_SELECTED;
- if (!gtk_widget_has_focus (widget))
- state = GTK_STATE_ACTIVE;
+ state = GTK_STATE_FLAG_SELECTED;
+
+ if (gtk_widget_has_focus (widget))
+ state |= GTK_STATE_FLAG_FOCUSED;
- gdk_cairo_set_source_color (cr, &style->base[state]);
+ gtk_style_context_get (context, state,
+ "background-color", &bg_color,
+ "color", &fg_color,
+ NULL);
+
+ gdk_cairo_set_source_rgba (cr, bg_color);
cairo_paint (cr);
- gdk_cairo_set_source_color (cr, &style->text[state]);
+ gdk_cairo_set_source_rgba (cr, fg_color);
cairo_move_to (cr, x, y);
_gtk_pango_fill_layout (cr, priv->layout);
cairo_restore (cr);
cairo_region_destroy (clip);
+
+ gdk_rgba_free (bg_color);
+ gdk_rgba_free (fg_color);
}
else if (info)
{
@@ -4143,7 +4151,6 @@ gtk_label_draw (GtkWidget *widget,
cairo_region_t *clip;
GdkRectangle rect;
GdkColor *text_color;
- GdkColor *base_color;
GdkColor *link_color;
GdkColor *visited_link_color;
@@ -4156,6 +4163,8 @@ gtk_label_draw (GtkWidget *widget,
if (active_link)
{
+ GdkRGBA *bg_color;
+
range[0] = active_link->start;
range[1] = active_link->end;
@@ -4174,12 +4183,17 @@ gtk_label_draw (GtkWidget *widget,
text_color = visited_link_color;
else
text_color = link_color;
+
if (info->link_clicked)
- base_color = &style->base[GTK_STATE_ACTIVE];
+ state = GTK_STATE_FLAG_ACTIVE;
else
- base_color = &style->base[GTK_STATE_PRELIGHT];
+ state = GTK_STATE_FLAG_PRELIGHT;
- gdk_cairo_set_source_color (cr, base_color);
+ gtk_style_context_get (context, state,
+ "background-color", &bg_color,
+ NULL);
+
+ gdk_cairo_set_source_rgba (cr, bg_color);
cairo_paint (cr);
gdk_cairo_set_source_color (cr, text_color);
@@ -4188,6 +4202,7 @@ gtk_label_draw (GtkWidget *widget,
gdk_color_free (link_color);
gdk_color_free (visited_link_color);
+ gdk_rgba_free (bg_color);
cairo_restore (cr);
}
@@ -4203,9 +4218,12 @@ gtk_label_draw (GtkWidget *widget,
1);
cairo_region_get_extents (clip, &rect);
- gtk_paint_focus (style, cr, gtk_widget_get_state (widget),
- widget, "label",
- rect.x, rect.y, rect.width, rect.height);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_set_state (context, state);
+
+ gtk_render_focus (context, cr,
+ rect.x, rect.y,
+ rect.width, rect.height);
cairo_region_destroy (clip);
}
@@ -5509,8 +5527,9 @@ gtk_label_get_selection_bounds (GtkLabel *label,
* Gets the #PangoLayout used to display the label.
* The layout is useful to e.g. convert text positions to
* pixel positions, in combination with gtk_label_get_layout_offsets().
- * The returned layout is owned by the label so need not be
- * freed by the caller.
+ * The returned layout is owned by the @label so need not be
+ * freed by the caller. The @label is free to recreate its layout at
+ * any time, so it should be considered read-only.
*
* Return value: (transfer none): the #PangoLayout for this label
**/
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index 1ba0ae767a..ac2bbd05ad 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -66,8 +66,6 @@
#include "gtkmenu.h"
#include "gdk/gdkkeysyms.h"
-#include "gdk/gdkprivate.h" /* for GDK_WINDOW_DESTROYED */
-
#ifdef G_OS_WIN32
static HMODULE gtk_dll;
@@ -798,7 +796,6 @@ do_post_parse_initialization (int *argc,
g_type_init ();
_gtk_accel_map_init ();
- _gtk_rc_init ();
/* Set the 'initialized' flag.
*/
@@ -1658,7 +1655,7 @@ gtk_main_do_event (GdkEvent *event)
{
/* The app may paint with a previously allocated cairo_t,
which will draw directly to the window. We can't catch cairo
- drap operatoins to automatically flush the window, thus we
+ draw operations to automatically flush the window, thus we
need to explicitly flush any outstanding moves or double
buffering */
gdk_window_flush (event->any.window);
@@ -1667,7 +1664,6 @@ gtk_main_do_event (GdkEvent *event)
break;
case GDK_PROPERTY_NOTIFY:
- case GDK_NO_EXPOSE:
case GDK_FOCUS_CHANGE:
case GDK_CONFIGURE:
case GDK_MAP:
@@ -2384,7 +2380,7 @@ gtk_get_event_widget (GdkEvent *event)
widget = NULL;
if (event && event->any.window &&
- (event->type == GDK_DESTROY || !GDK_WINDOW_DESTROYED (event->any.window)))
+ (event->type == GDK_DESTROY || !gdk_window_is_destroyed (event->any.window)))
{
gdk_window_get_user_data (event->any.window, &widget_ptr);
widget = widget_ptr;
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index 9279037c7c..fa60a7c91d 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -70,6 +70,7 @@ VOID:ENUM,INT
VOID:ENUM,INT,BOOLEAN
VOID:ENUM,BOXED
VOID:ENUM,STRING
+VOID:FLAGS
VOID:INT
VOID:INT,BOOLEAN
VOID:INT,INT
diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c
index 13998ee1b9..e678198a39 100644
--- a/gtk/gtkmenu.c
+++ b/gtk/gtkmenu.c
@@ -91,8 +91,8 @@ struct _GtkMenuPrivate
gchar *title;
/* Arrow states */
- GtkStateType lower_arrow_state;
- GtkStateType upper_arrow_state;
+ GtkStateFlags lower_arrow_state;
+ GtkStateFlags upper_arrow_state;
/* navigation region */
int navigation_x;
@@ -217,8 +217,7 @@ static void gtk_menu_handle_scrolling (GtkMenu *menu,
gboolean motion);
static void gtk_menu_set_tearoff_hints (GtkMenu *menu,
gint width);
-static void gtk_menu_style_set (GtkWidget *widget,
- GtkStyle *previous_style);
+static void gtk_menu_style_updated (GtkWidget *widget);
static gboolean gtk_menu_focus (GtkWidget *widget,
GtkDirectionType direction);
static gint gtk_menu_get_popup_delay (GtkMenuShell *menu_shell);
@@ -478,7 +477,7 @@ gtk_menu_class_init (GtkMenuClass *class)
widget_class->show_all = gtk_menu_show_all;
widget_class->enter_notify_event = gtk_menu_enter_notify;
widget_class->leave_notify_event = gtk_menu_leave_notify;
- widget_class->style_set = gtk_menu_style_set;
+ widget_class->style_updated = gtk_menu_style_updated;
widget_class->focus = gtk_menu_focus;
widget_class->can_activate_accel = gtk_menu_real_can_activate_accel;
widget_class->grab_notify = gtk_menu_grab_notify;
@@ -1002,6 +1001,7 @@ static void
gtk_menu_init (GtkMenu *menu)
{
GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+ GtkStyleContext *context;
menu->parent_menu_item = NULL;
menu->old_active_menu_item = NULL;
@@ -1046,11 +1046,11 @@ gtk_menu_init (GtkMenu *menu)
menu->upper_arrow_prelight = FALSE;
menu->lower_arrow_prelight = FALSE;
- priv->upper_arrow_state = GTK_STATE_NORMAL;
- priv->lower_arrow_state = GTK_STATE_NORMAL;
-
priv->have_layout = FALSE;
priv->monitor_num = -1;
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (menu));
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENU);
}
static void
@@ -1192,9 +1192,9 @@ gtk_menu_attach_to_widget (GtkMenu *menu,
g_object_set_data_full (G_OBJECT (attach_widget), I_(ATTACHED_MENUS), list,
(GDestroyNotify) g_list_free);
- if (gtk_widget_get_state (GTK_WIDGET (menu)) != GTK_STATE_NORMAL)
- gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
-
+ if (gtk_widget_get_state_flags (GTK_WIDGET (menu)) != 0)
+ gtk_widget_set_state_flags (GTK_WIDGET (menu), 0, TRUE);
+
/* we don't need to set the style here, since
* we are a toplevel widget.
*/
@@ -2313,19 +2313,18 @@ gtk_menu_reorder_child (GtkMenu *menu,
}
static void
-gtk_menu_style_set (GtkWidget *widget,
- GtkStyle *previous_style)
+gtk_menu_style_updated (GtkWidget *widget)
{
if (gtk_widget_get_realized (widget))
{
GtkMenu *menu = GTK_MENU (widget);
- GtkStyle *style;
+ GtkStyleContext *context;
- style = gtk_widget_get_style (widget);
+ context = gtk_widget_get_style_context (widget);
- gtk_style_set_background (style, menu->bin_window, GTK_STATE_NORMAL);
- gtk_style_set_background (style, menu->view_window, GTK_STATE_NORMAL);
- gtk_style_set_background (style, gtk_widget_get_window (widget), GTK_STATE_NORMAL);
+ gtk_style_context_set_background (context, menu->bin_window);
+ gtk_style_context_set_background (context, menu->view_window);
+ gtk_style_context_set_background (context, gtk_widget_get_window (widget));
}
}
@@ -2365,10 +2364,29 @@ get_arrows_border (GtkMenu *menu,
}
static void
+get_menu_border (GtkWidget *widget,
+ GtkBorder *border)
+{
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder *border_width;
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+
+ gtk_style_context_get (context, state,
+ "border-width", &border_width,
+ NULL);
+
+ *border = *border_width;
+ gtk_border_free (border_width);
+}
+
+static void
gtk_menu_realize (GtkWidget *widget)
{
GtkAllocation allocation;
- GtkStyle *style;
+ GtkStyleContext *context;
GdkWindow *window;
GdkWindowAttr attributes;
gint attributes_mask;
@@ -2379,7 +2397,7 @@ gtk_menu_realize (GtkWidget *widget)
GList *children;
guint vertical_padding;
guint horizontal_padding;
- GtkBorder arrow_border;
+ GtkBorder arrow_border, border;
g_return_if_fail (GTK_IS_MENU (widget));
@@ -2408,9 +2426,10 @@ gtk_menu_realize (GtkWidget *widget)
gtk_widget_set_window (widget, window);
gdk_window_set_user_data (window, widget);
+ get_menu_border (widget, &border);
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
+ context = gtk_widget_get_style_context (widget);
- style = gtk_widget_get_style (widget);
gtk_widget_style_get (GTK_WIDGET (menu),
"vertical-padding", &vertical_padding,
"horizontal-padding", &horizontal_padding,
@@ -2418,16 +2437,21 @@ gtk_menu_realize (GtkWidget *widget)
gtk_widget_get_allocation (widget, &allocation);
- attributes.x = border_width + style->xthickness + horizontal_padding;
- attributes.y = border_width + style->ythickness + vertical_padding;
- attributes.width = MAX (1, allocation.width - attributes.x * 2);
- attributes.height = MAX (1, allocation.height - attributes.y * 2);
+ attributes.x = border_width + border.left + horizontal_padding;
+ attributes.y = border_width + border.top + vertical_padding;
+ attributes.width = allocation.width -
+ (2 * (border_width + horizontal_padding)) - border.left - border.right;
+ attributes.height = allocation.height -
+ (2 * (border_width + vertical_padding)) - border.top - border.bottom;
get_arrows_border (menu, &arrow_border);
attributes.y += arrow_border.top;
attributes.height -= arrow_border.top;
attributes.height -= arrow_border.bottom;
+ attributes.width = MAX (1, attributes.width);
+ attributes.height = MAX (1, attributes.height);
+
menu->view_window = gdk_window_new (window,
&attributes, attributes_mask);
gdk_window_set_user_data (menu->view_window, menu);
@@ -2436,8 +2460,13 @@ gtk_menu_realize (GtkWidget *widget)
attributes.x = 0;
attributes.y = 0;
- attributes.width = MAX (1, allocation.width - (border_width + style->xthickness + horizontal_padding) * 2);
- attributes.height = MAX (1, priv->requested_height - (border_width + style->ythickness + vertical_padding) * 2);
+ attributes.width = allocation.width + (2 * (border_width + horizontal_padding)) +
+ border.left + border.right;
+ attributes.height = priv->requested_height - (2 * (border_width + vertical_padding)) +
+ border.top + border.bottom;
+
+ attributes.width = MAX (1, attributes.width);
+ attributes.height = MAX (1, attributes.height);
menu->bin_window = gdk_window_new (menu->view_window,
&attributes, attributes_mask);
@@ -2452,10 +2481,9 @@ gtk_menu_realize (GtkWidget *widget)
gtk_widget_set_parent_window (child, menu->bin_window);
}
- gtk_widget_style_attach (widget);
- gtk_style_set_background (style, menu->bin_window, GTK_STATE_NORMAL);
- gtk_style_set_background (style, menu->view_window, GTK_STATE_NORMAL);
- gtk_style_set_background (style, window, GTK_STATE_NORMAL);
+ gtk_style_context_set_background (context, menu->bin_window);
+ gtk_style_context_set_background (context, menu->view_window);
+ gtk_style_context_set_background (context, window);
if (GTK_MENU_SHELL (widget)->active_menu_item)
gtk_menu_scroll_item_visible (GTK_MENU_SHELL (widget),
@@ -2625,14 +2653,14 @@ gtk_menu_size_allocate (GtkWidget *widget,
GtkWidget *child;
GtkAllocation child_allocation;
GtkMenuPrivate *priv;
- GtkStyle *style;
GList *children;
gint x, y, i;
gint width, height;
guint border_width;
guint vertical_padding;
guint horizontal_padding;
-
+ GtkBorder border;
+
g_return_if_fail (GTK_IS_MENU (widget));
g_return_if_fail (allocation != NULL);
@@ -2642,12 +2670,12 @@ gtk_menu_size_allocate (GtkWidget *widget,
gtk_widget_set_allocation (widget, allocation);
- style = gtk_widget_get_style (widget);
-
gtk_widget_style_get (GTK_WIDGET (menu),
"vertical-padding", &vertical_padding,
"horizontal-padding", &horizontal_padding,
NULL);
+
+ get_menu_border (widget, &border);
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
g_free (priv->heights);
@@ -2658,15 +2686,17 @@ gtk_menu_size_allocate (GtkWidget *widget,
NULL);
/* refresh our cached height request */
- priv->requested_height = (border_width + vertical_padding + style->ythickness) * 2;
+ priv->requested_height = (2 * (border_width + vertical_padding)) +
+ border.top + border.bottom;
for (i = 0; i < priv->heights_length; i++)
priv->requested_height += priv->heights[i];
- x = border_width + style->xthickness + horizontal_padding;
- y = border_width + style->ythickness + vertical_padding;
-
- width = MAX (1, allocation->width - x * 2);
- height = MAX (1, allocation->height - y * 2);
+ x = border_width + border.left + horizontal_padding;
+ y = border_width + border.top + vertical_padding;
+ width = allocation->width - (2 * (border_width + horizontal_padding)) -
+ border.left - border.right;
+ height = allocation->height - (2 * (border_width + vertical_padding)) -
+ border.top - border.bottom;
if (menu_shell->active)
gtk_menu_scroll_to (menu, menu->scroll_offset);
@@ -2681,6 +2711,9 @@ gtk_menu_size_allocate (GtkWidget *widget,
height -= arrow_border.bottom;
}
+ width = MAX (1, width);
+ height = MAX (1, height);
+
if (gtk_widget_get_realized (widget))
{
gdk_window_move_resize (gtk_widget_get_window (widget),
@@ -2801,14 +2834,12 @@ get_arrows_visible_area (GtkMenu *menu,
gint *arrow_space)
{
GtkArrowPlacement arrow_placement;
- GtkStyle *style;
GtkWidget *widget = GTK_WIDGET (menu);
guint border_width;
guint vertical_padding;
guint horizontal_padding;
gint scroll_arrow_height;
-
- style = gtk_widget_get_style (widget);
+ GtkBorder menu_border;
gtk_widget_style_get (widget,
"vertical-padding", &vertical_padding,
@@ -2817,9 +2848,10 @@ get_arrows_visible_area (GtkMenu *menu,
"arrow-placement", &arrow_placement,
NULL);
+ get_menu_border (widget, &menu_border);
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
- border->x = border_width + style->xthickness + horizontal_padding;
- border->y = border_width + style->ythickness + vertical_padding;
+ border->x = border_width + menu_border.left + horizontal_padding;
+ border->y = border_width + menu_border.top + vertical_padding;
border->width = gdk_window_get_width (gtk_widget_get_window (widget));
border->height = gdk_window_get_height (gtk_widget_get_window (widget));
@@ -2867,7 +2899,7 @@ get_arrows_visible_area (GtkMenu *menu,
lower->x = lower->y = lower->width = lower->height = 0;
}
- *arrow_space = scroll_arrow_height - 2 * style->ythickness;
+ *arrow_space = scroll_arrow_height - menu_border.top - menu_border.bottom;
}
static gboolean
@@ -2876,20 +2908,23 @@ gtk_menu_draw (GtkWidget *widget,
{
GtkMenu *menu;
GtkMenuPrivate *priv;
- GtkStyle *style;
+ GtkStyleContext *context;
GdkRectangle border;
GdkRectangle upper;
GdkRectangle lower;
GdkWindow *window;
gint arrow_space;
-
+ GtkStateFlags state;
+ GtkBorder menu_border;
+
menu = GTK_MENU (widget);
priv = gtk_menu_get_private (menu);
-
- style = gtk_widget_get_style (widget);
+ context = gtk_widget_get_style_context (widget);
window = gtk_widget_get_window (widget);
+ state = gtk_widget_get_state_flags (widget);
get_arrows_visible_area (menu, &border, &upper, &lower, &arrow_space);
+ get_menu_border (widget, &menu_border);
if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
{
@@ -2899,62 +2934,57 @@ gtk_menu_draw (GtkWidget *widget,
gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL);
arrow_size = arrow_scaling * arrow_space;
- gtk_paint_box (style,
- cr,
- GTK_STATE_NORMAL,
- GTK_SHADOW_OUT,
- widget, "menu",
- 0, 0,
- gtk_widget_get_allocated_width (widget),
- gtk_widget_get_allocated_height (widget));
+ gtk_render_background (context, cr, 0, 0,
+ gtk_widget_get_allocated_width (widget),
+ gtk_widget_get_allocated_height (widget));
+ gtk_render_frame (context, cr, 0, 0,
+ gtk_widget_get_allocated_width (widget),
+ gtk_widget_get_allocated_height (widget));
+
+ gtk_style_context_save (context);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
if (menu->upper_arrow_visible && !menu->tearoff_active)
{
- gtk_paint_box (style,
- cr,
- priv->upper_arrow_state,
- GTK_SHADOW_OUT,
- widget, "menu_scroll_arrow_up",
- upper.x,
- upper.y,
- upper.width,
- upper.height);
-
- gtk_paint_arrow (style,
- cr,
- priv->upper_arrow_state,
- GTK_SHADOW_OUT,
- widget, "menu_scroll_arrow_up",
- GTK_ARROW_UP,
- TRUE,
+ gtk_style_context_save (context);
+ gtk_style_context_set_state (context, priv->upper_arrow_state);
+
+ gtk_render_background (context, cr,
+ upper.x, upper.y,
+ upper.width, upper.height);
+ gtk_render_frame (context, cr,
+ upper.x, upper.y,
+ upper.width, upper.height);
+
+ gtk_render_arrow (context, cr, 0,
upper.x + (upper.width - arrow_size) / 2,
- upper.y + style->ythickness + (arrow_space - arrow_size) / 2,
- arrow_size, arrow_size);
+ upper.y + menu_border.top + (arrow_space - arrow_size) / 2,
+ arrow_size);
+
+ gtk_style_context_restore (context);
}
if (menu->lower_arrow_visible && !menu->tearoff_active)
{
- gtk_paint_box (style,
- cr,
- priv->lower_arrow_state,
- GTK_SHADOW_OUT,
- widget, "menu_scroll_arrow_down",
- lower.x,
- lower.y,
- lower.width,
- lower.height);
-
- gtk_paint_arrow (style,
- cr,
- priv->lower_arrow_state,
- GTK_SHADOW_OUT,
- widget, "menu_scroll_arrow_down",
- GTK_ARROW_DOWN,
- TRUE,
- lower.x + (lower.width - arrow_size) / 2,
- lower.y + style->ythickness + (arrow_space - arrow_size) / 2,
- arrow_size, arrow_size);
+ gtk_style_context_save (context);
+ gtk_style_context_set_state (context, priv->lower_arrow_state);
+
+ gtk_render_background (context, cr,
+ lower.x, lower.y,
+ lower.width, lower.height);
+ gtk_render_frame (context, cr,
+ lower.x, lower.y,
+ lower.width, lower.height);
+
+ gtk_render_arrow (context, cr, G_PI,
+ lower.x + (lower.width - arrow_size) / 2,
+ lower.y + menu_border.top + (arrow_space - arrow_size) / 2,
+ arrow_size);
+
+ gtk_style_context_restore (context);
}
+
+ gtk_style_context_restore (context);
}
if (gtk_cairo_should_draw_window (cr, menu->bin_window))
@@ -2972,13 +3002,12 @@ gtk_menu_draw (GtkWidget *widget,
y -= arrow_border.top;
}
- gtk_paint_box (style,
- cr,
- GTK_STATE_NORMAL,
- GTK_SHADOW_OUT,
- widget, "menu",
- - border.x, y,
- border.width, border.height);
+ gtk_render_background (context, cr,
+ - border.x, y,
+ border.width, border.height);
+ gtk_render_frame (context, cr,
+ - border.x, y,
+ border.width, border.height);
cairo_restore (cr);
}
@@ -3007,7 +3036,6 @@ gtk_menu_get_preferred_width (GtkWidget *widget,
GtkMenu *menu;
GtkMenuShell *menu_shell;
GtkMenuPrivate *priv;
- GtkStyle *style;
GtkWidget *child;
GList *children;
guint max_toggle_size;
@@ -3016,13 +3044,12 @@ gtk_menu_get_preferred_width (GtkWidget *widget,
guint border_width;
gint child_min, child_nat;
gint min_width, nat_width;
+ GtkBorder border;
menu = GTK_MENU (widget);
menu_shell = GTK_MENU_SHELL (widget);
priv = gtk_menu_get_private (menu);
- style = gtk_widget_get_style (GTK_WIDGET (widget));
-
min_width = nat_width = 0;
max_toggle_size = 0;
@@ -3073,16 +3100,31 @@ gtk_menu_get_preferred_width (GtkWidget *widget,
gtk_menu_get_n_columns (menu) == 1 &&
!priv->no_toggle_size)
{
+ GtkStyleContext *context;
+ GtkWidgetPath *menu_path, *check_path;
guint toggle_spacing;
guint indicator_size;
- gtk_style_get (style,
- GTK_TYPE_CHECK_MENU_ITEM,
- "toggle-spacing", &toggle_spacing,
- "indicator-size", &indicator_size,
- NULL);
+ context = gtk_widget_get_style_context (widget);
+ menu_path = gtk_widget_path_copy (gtk_style_context_get_path (context));
+
+ /* Create a GtkCheckMenuItem path, only to query indicator spacing */
+ check_path = gtk_widget_path_copy (menu_path);
+ gtk_widget_path_append_type (check_path, GTK_TYPE_CHECK_MENU_ITEM);
+
+ gtk_style_context_set_path (context, check_path);
+ gtk_widget_path_free (check_path);
+
+ gtk_style_context_get_style (context,
+ "toggle-spacing", &toggle_spacing,
+ "indicator-size", &indicator_size,
+ NULL);
max_toggle_size = indicator_size + toggle_spacing;
+
+ /* Restore real widget path */
+ gtk_style_context_set_path (context, menu_path);
+ gtk_widget_path_free (menu_path);
}
min_width += 2 * max_toggle_size + max_accel_width;
@@ -3095,10 +3137,13 @@ gtk_menu_get_preferred_width (GtkWidget *widget,
"horizontal-padding", &horizontal_padding,
NULL);
+ get_menu_border (widget, &border);
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
- min_width += (border_width + horizontal_padding + style->xthickness) * 2;
- nat_width += (border_width + horizontal_padding + style->xthickness) * 2;
-
+ min_width += (2 * (border_width + horizontal_padding)) +
+ border.left + border.right;
+ nat_width += (2 * (border_width + horizontal_padding)) +
+ border.top + border.bottom;
+
menu->toggle_size = max_toggle_size;
priv->accel_size = max_accel_width;
@@ -3934,7 +3979,7 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
if (touchscreen_mode)
menu->upper_arrow_prelight = in_arrow;
- if (priv->upper_arrow_state != GTK_STATE_INSENSITIVE)
+ if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
gboolean arrow_pressed = FALSE;
@@ -4008,14 +4053,15 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
* menu, so check if the button isn't insensitive before
* changing it to something else.
*/
- if (priv->upper_arrow_state != GTK_STATE_INSENSITIVE)
+ if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
- GtkStateType arrow_state = GTK_STATE_NORMAL;
+ GtkStateFlags arrow_state = 0;
if (arrow_pressed)
- arrow_state = GTK_STATE_ACTIVE;
- else if (menu->upper_arrow_prelight)
- arrow_state = GTK_STATE_PRELIGHT;
+ arrow_state |= GTK_STATE_FLAG_ACTIVE;
+
+ if (menu->upper_arrow_prelight)
+ arrow_state |= GTK_STATE_FLAG_PRELIGHT;
if (arrow_state != priv->upper_arrow_state)
{
@@ -4042,7 +4088,7 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
if (touchscreen_mode)
menu->lower_arrow_prelight = in_arrow;
- if (priv->lower_arrow_state != GTK_STATE_INSENSITIVE)
+ if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
gboolean arrow_pressed = FALSE;
@@ -4116,14 +4162,15 @@ gtk_menu_handle_scrolling (GtkMenu *menu,
* menu, so check if the button isn't insensitive before
* changing it to something else.
*/
- if (priv->lower_arrow_state != GTK_STATE_INSENSITIVE)
+ if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0)
{
- GtkStateType arrow_state = GTK_STATE_NORMAL;
+ GtkStateFlags arrow_state = 0;
if (arrow_pressed)
- arrow_state = GTK_STATE_ACTIVE;
- else if (menu->lower_arrow_prelight)
- arrow_state = GTK_STATE_PRELIGHT;
+ arrow_state |= GTK_STATE_FLAG_ACTIVE;
+
+ if (menu->lower_arrow_prelight)
+ arrow_state |= GTK_STATE_FLAG_PRELIGHT;
if (arrow_state != priv->lower_arrow_state)
{
@@ -4536,14 +4583,14 @@ gtk_menu_position (GtkMenu *menu,
}
else
{
- GtkStyle *style = gtk_widget_get_style (widget);
gint space_left, space_right, space_above, space_below;
gint needed_width;
gint needed_height;
- gint xthickness = style->xthickness;
- gint ythickness = style->ythickness;
+ GtkBorder border;
gboolean rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
+ get_menu_border (widget, &border);
+
/* The placement of popup menus horizontally works like this (with
* RTL in parentheses)
*
@@ -4572,9 +4619,9 @@ gtk_menu_position (GtkMenu *menu,
/* position horizontally */
/* the amount of space we need to position the menu. Note the
- * menu is offset "xthickness" pixels
+ * menu is offset "thickness" pixels
*/
- needed_width = requisition.width - xthickness;
+ needed_width = requisition.width - border.left;
if (needed_width <= space_left ||
needed_width <= space_right)
@@ -4583,12 +4630,12 @@ gtk_menu_position (GtkMenu *menu,
(!rtl && needed_width > space_right))
{
/* position left */
- x = x + xthickness - requisition.width + 1;
+ x = x + border.left - requisition.width + 1;
}
else
{
/* position right */
- x = x - xthickness;
+ x = x - border.right;
}
/* x is clamped on-screen further down */
@@ -4627,15 +4674,15 @@ gtk_menu_position (GtkMenu *menu,
/* Position vertically. The algorithm is the same as above, but
* simpler because we don't have to take RTL into account.
*/
- needed_height = requisition.height - ythickness;
+ needed_height = requisition.height - border.top;
if (needed_height <= space_above ||
needed_height <= space_below)
{
if (needed_height <= space_below)
- y = y - ythickness;
+ y = y - border.top;
else
- y = y + ythickness - requisition.height + 1;
+ y = y + border.bottom - requisition.height + 1;
y = CLAMP (y, monitor.y,
monitor.y + monitor.height - requisition.height);
@@ -4747,8 +4794,7 @@ gtk_menu_scroll_to (GtkMenu *menu,
{
GtkMenuPrivate *priv;
GtkAllocation allocation;
- GtkBorder arrow_border;
- GtkStyle *style;
+ GtkBorder arrow_border, border;
GtkWidget *widget;
gint x, y;
gint view_width, view_height;
@@ -4776,23 +4822,23 @@ gtk_menu_scroll_to (GtkMenu *menu,
view_width = allocation.width;
view_height = allocation.height;
- style = gtk_widget_get_style (widget);
-
gtk_widget_style_get (GTK_WIDGET (menu),
"vertical-padding", &vertical_padding,
"horizontal-padding", &horizontal_padding,
NULL);
+ get_menu_border (widget, &border);
double_arrows = get_double_arrows (menu);
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
- view_width -= (border_width + style->xthickness + horizontal_padding) * 2;
- view_height -= (border_width + style->ythickness + vertical_padding) * 2;
- menu_height = priv->requested_height - (border_width + style->ythickness + vertical_padding) * 2;
+ view_width -= (2 * (border_width + horizontal_padding)) + border.left + border.right;
+ view_height -= (2 * (border_width + vertical_padding)) + border.top + border.bottom;
+ menu_height = priv->requested_height - (2 * (border_width + vertical_padding)) -
+ border.top - border.bottom;
- x = border_width + style->xthickness + horizontal_padding;
- y = border_width + style->ythickness + vertical_padding;
+ x = border_width + border.left + horizontal_padding;
+ y = border_width + border.top + vertical_padding;
if (double_arrows && !menu->tearoff_active)
{
@@ -4801,8 +4847,8 @@ gtk_menu_scroll_to (GtkMenu *menu,
(offset < 0 && menu->scroll_offset < 0))
{
GtkMenuPrivate *priv = gtk_menu_get_private (menu);
- GtkStateType upper_arrow_previous_state = priv->upper_arrow_state;
- GtkStateType lower_arrow_previous_state = priv->lower_arrow_state;
+ GtkStateFlags upper_arrow_previous_state = priv->upper_arrow_state;
+ GtkStateFlags lower_arrow_previous_state = priv->lower_arrow_state;
if (!menu->upper_arrow_visible || !menu->lower_arrow_visible)
gtk_widget_queue_draw (GTK_WIDGET (menu));
@@ -4815,23 +4861,35 @@ gtk_menu_scroll_to (GtkMenu *menu,
view_height -= arrow_border.bottom;
if (offset <= 0)
- priv->upper_arrow_state = GTK_STATE_INSENSITIVE;
- else if (priv->upper_arrow_state == GTK_STATE_INSENSITIVE)
- priv->upper_arrow_state = menu->upper_arrow_prelight ?
- GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
+ priv->upper_arrow_state |= GTK_STATE_FLAG_INSENSITIVE;
+ else
+ {
+ priv->upper_arrow_state &= ~(GTK_STATE_FLAG_INSENSITIVE);
+
+ if (menu->upper_arrow_prelight)
+ priv->upper_arrow_state |= GTK_STATE_FLAG_PRELIGHT;
+ else
+ priv->upper_arrow_state &= ~(GTK_STATE_FLAG_PRELIGHT);
+ }
if (offset >= menu_height - view_height)
- priv->lower_arrow_state = GTK_STATE_INSENSITIVE;
- else if (priv->lower_arrow_state == GTK_STATE_INSENSITIVE)
- priv->lower_arrow_state = menu->lower_arrow_prelight ?
- GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
+ priv->lower_arrow_state |= GTK_STATE_FLAG_INSENSITIVE;
+ else
+ {
+ priv->lower_arrow_state &= ~(GTK_STATE_FLAG_INSENSITIVE);
+
+ if (menu->lower_arrow_prelight)
+ priv->lower_arrow_state |= GTK_STATE_FLAG_PRELIGHT;
+ else
+ priv->lower_arrow_state &= ~(GTK_STATE_FLAG_PRELIGHT);
+ }
if ((priv->upper_arrow_state != upper_arrow_previous_state) ||
(priv->lower_arrow_state != lower_arrow_previous_state))
gtk_widget_queue_draw (GTK_WIDGET (menu));
- if (upper_arrow_previous_state != GTK_STATE_INSENSITIVE &&
- priv->upper_arrow_state == GTK_STATE_INSENSITIVE)
+ if ((upper_arrow_previous_state & GTK_STATE_FLAG_INSENSITIVE) == 0 &&
+ (priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) != 0)
{
/* At the upper border, possibly remove timeout */
if (menu->scroll_step < 0)
@@ -4841,8 +4899,8 @@ gtk_menu_scroll_to (GtkMenu *menu,
}
}
- if (lower_arrow_previous_state != GTK_STATE_INSENSITIVE &&
- priv->lower_arrow_state == GTK_STATE_INSENSITIVE)
+ if ((lower_arrow_previous_state & GTK_STATE_FLAG_INSENSITIVE) == 0 &&
+ (priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) != 0)
{
/* At the lower border, possibly remove timeout */
if (menu->scroll_step > 0)
diff --git a/gtk/gtkmenubar.c b/gtk/gtkmenubar.c
index a28622a65f..bb482844d7 100644
--- a/gtk/gtkmenubar.c
+++ b/gtk/gtkmenubar.c
@@ -214,9 +214,14 @@ gtk_menu_bar_class_init (GtkMenuBarClass *class)
static void
gtk_menu_bar_init (GtkMenuBar *menu_bar)
{
+ GtkStyleContext *context;
+
menu_bar->priv = G_TYPE_INSTANCE_GET_PRIVATE (menu_bar,
GTK_TYPE_MENU_BAR,
GtkMenuBarPrivate);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (menu_bar));
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENUBAR);
}
GtkWidget*
@@ -345,11 +350,18 @@ gtk_menu_bar_size_request (GtkWidget *widget,
if (get_shadow_type (menu_bar) != GTK_SHADOW_NONE)
{
- GtkStyle *style;
+ GtkStyleContext *context;
+ GtkBorder *border;
+
+ context = gtk_widget_get_style_context (widget);
+
+ gtk_style_context_get (context, 0,
+ "border-width", &border,
+ NULL);
- style = gtk_widget_get_style (widget);
- requisition->width += style->xthickness * 2;
- requisition->height += style->ythickness * 2;
+ requisition->width += border->left + border->right;
+ requisition->height += border->top + border->bottom;
+ gtk_border_free (border);
}
}
}
@@ -424,11 +436,18 @@ gtk_menu_bar_size_allocate (GtkWidget *widget,
if (get_shadow_type (menu_bar) != GTK_SHADOW_NONE)
{
- GtkStyle *style;
+ GtkStyleContext *context;
+ GtkBorder *border;
- style = gtk_widget_get_style (widget);
- child_allocation.x += style->xthickness;
- child_allocation.y += style->ythickness;
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_get (context, 0,
+ "border-width", &border,
+ NULL);
+
+ child_allocation.x += border->left;
+ child_allocation.y += border->top;
+
+ gtk_border_free (border);
}
if (priv->pack_direction == GTK_PACK_DIRECTION_LTR ||
@@ -539,18 +558,26 @@ static gint
gtk_menu_bar_draw (GtkWidget *widget,
cairo_t *cr)
{
+ GtkStyleContext *context;
+ GtkStateFlags state;
int border;
border = gtk_container_get_border_width (GTK_CONTAINER (widget));
+ context = gtk_widget_get_style_context (widget);
+
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_set_state (context, state);
+
+ if (get_shadow_type (GTK_MENU_BAR (widget)) != GTK_SHADOW_NONE)
+ gtk_render_background (context, cr,
+ border, border,
+ gtk_widget_get_allocated_width (widget) - border * 2,
+ gtk_widget_get_allocated_height (widget) - border * 2);
- gtk_paint_box (gtk_widget_get_style (widget),
- cr,
- gtk_widget_get_state (widget),
- get_shadow_type (GTK_MENU_BAR (widget)),
- widget, "menubar",
- border, border,
- gtk_widget_get_allocated_width (widget) - border * 2,
- gtk_widget_get_allocated_height (widget) - border * 2);
+ gtk_render_frame (context, cr,
+ border, border,
+ gtk_widget_get_allocated_width (widget) - border * 2,
+ gtk_widget_get_allocated_height (widget) - border * 2);
GTK_WIDGET_CLASS (gtk_menu_bar_parent_class)->draw (widget, cr);
diff --git a/gtk/gtkmenuitem.c b/gtk/gtkmenuitem.c
index d0dd5e9032..7a94bed278 100644
--- a/gtk/gtkmenuitem.c
+++ b/gtk/gtkmenuitem.c
@@ -1626,7 +1626,8 @@ gtk_real_menu_item_select (GtkMenuItem *menu_item)
_gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), TRUE);
}
- gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
+ gtk_widget_set_state_flags (GTK_WIDGET (menu_item),
+ GTK_STATE_FLAG_PRELIGHT, FALSE);
gtk_widget_queue_draw (GTK_WIDGET (menu_item));
}
@@ -1636,7 +1637,8 @@ gtk_real_menu_item_deselect (GtkMenuItem *menu_item)
if (menu_item->submenu)
_gtk_menu_item_popdown_submenu (GTK_WIDGET (menu_item));
- gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
+ gtk_widget_unset_state_flags (GTK_WIDGET (menu_item),
+ GTK_STATE_FLAG_PRELIGHT);
gtk_widget_queue_draw (GTK_WIDGET (menu_item));
}
diff --git a/gtk/gtkmodifierstyle.c b/gtk/gtkmodifierstyle.c
new file mode 100644
index 0000000000..a8f4d3d729
--- /dev/null
+++ b/gtk/gtkmodifierstyle.c
@@ -0,0 +1,287 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gtkmodifierstyle.h"
+#include "gtkintl.h"
+
+typedef struct GtkModifierStylePrivate GtkModifierStylePrivate;
+typedef struct StylePropertyValue StylePropertyValue;
+
+struct GtkModifierStylePrivate
+{
+ GtkStyleProperties *style;
+ GHashTable *color_properties;
+};
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0 };
+
+static void gtk_modifier_style_provider_init (GtkStyleProviderIface *iface);
+static void gtk_modifier_style_finalize (GObject *object);
+
+G_DEFINE_TYPE_EXTENDED (GtkModifierStyle, gtk_modifier_style, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
+ gtk_modifier_style_provider_init));
+
+static void
+gtk_modifier_style_class_init (GtkModifierStyleClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_modifier_style_finalize;
+
+ signals[CHANGED] =
+ g_signal_new (I_("changed"),
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (object_class, sizeof (GtkModifierStylePrivate));
+}
+
+static void
+gtk_modifier_style_init (GtkModifierStyle *modifier_style)
+{
+ GtkModifierStylePrivate *priv;
+
+ priv = modifier_style->priv = G_TYPE_INSTANCE_GET_PRIVATE (modifier_style,
+ GTK_TYPE_MODIFIER_STYLE,
+ GtkModifierStylePrivate);
+
+ priv->color_properties = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) gdk_rgba_free);
+ priv->style = gtk_style_properties_new ();
+}
+
+static GtkStyleProperties *
+gtk_modifier_style_get_style (GtkStyleProvider *provider,
+ GtkWidgetPath *path)
+{
+ GtkModifierStylePrivate *priv;
+
+ priv = GTK_MODIFIER_STYLE (provider)->priv;
+ return g_object_ref (priv->style);
+}
+
+static gboolean
+gtk_modifier_style_get_style_property (GtkStyleProvider *provider,
+ GtkWidgetPath *path,
+ GtkStateFlags state,
+ GParamSpec *pspec,
+ GValue *value)
+{
+ GtkModifierStylePrivate *priv;
+ GdkColor *color;
+ gchar *str;
+
+ /* Reject non-color types for now */
+ if (pspec->value_type != GDK_TYPE_COLOR)
+ return FALSE;
+
+ priv = GTK_MODIFIER_STYLE (provider)->priv;
+ str = g_strdup_printf ("-%s-%s",
+ g_type_name (pspec->owner_type),
+ pspec->name);
+
+ color = g_hash_table_lookup (priv->color_properties, str);
+ g_free (str);
+
+ if (!color)
+ return FALSE;
+
+ g_value_set_boxed (value, color);
+ return TRUE;
+}
+
+static void
+gtk_modifier_style_provider_init (GtkStyleProviderIface *iface)
+{
+ iface->get_style = gtk_modifier_style_get_style;
+ iface->get_style_property = gtk_modifier_style_get_style_property;
+}
+
+static void
+gtk_modifier_style_finalize (GObject *object)
+{
+ GtkModifierStylePrivate *priv;
+
+ priv = GTK_MODIFIER_STYLE (object)->priv;
+ g_hash_table_destroy (priv->color_properties);
+ g_object_unref (priv->style);
+
+ G_OBJECT_CLASS (gtk_modifier_style_parent_class)->finalize (object);
+}
+
+GtkModifierStyle *
+gtk_modifier_style_new (void)
+{
+ return g_object_new (GTK_TYPE_MODIFIER_STYLE, NULL);
+}
+
+static void
+modifier_style_set_color (GtkModifierStyle *style,
+ const gchar *prop,
+ GtkStateFlags state,
+ const GdkRGBA *color)
+{
+ GtkModifierStylePrivate *priv;
+ GdkRGBA *old_color;
+
+ g_return_if_fail (GTK_IS_MODIFIER_STYLE (style));
+
+ priv = style->priv;
+ gtk_style_properties_get (priv->style, state,
+ prop, &old_color,
+ NULL);
+
+ if ((!color && !old_color) ||
+ (color && old_color && gdk_rgba_equal (color, old_color)))
+ {
+ gdk_rgba_free (old_color);
+ return;
+ }
+
+ if (color)
+ gtk_style_properties_set (priv->style, state,
+ prop, color,
+ NULL);
+ else
+ gtk_style_properties_unset_property (priv->style, prop, state);
+
+ g_signal_emit (style, signals[CHANGED], 0);
+ gdk_rgba_free (old_color);
+}
+
+void
+gtk_modifier_style_set_background_color (GtkModifierStyle *style,
+ GtkStateFlags state,
+ const GdkRGBA *color)
+{
+ g_return_if_fail (GTK_IS_MODIFIER_STYLE (style));
+
+ modifier_style_set_color (style, "background-color", state, color);
+}
+
+void
+gtk_modifier_style_set_color (GtkModifierStyle *style,
+ GtkStateFlags state,
+ const GdkRGBA *color)
+{
+ g_return_if_fail (GTK_IS_MODIFIER_STYLE (style));
+
+ modifier_style_set_color (style, "color", state, color);
+}
+
+void
+gtk_modifier_style_set_font (GtkModifierStyle *style,
+ const PangoFontDescription *font_desc)
+{
+ GtkModifierStylePrivate *priv;
+ PangoFontDescription *old_font;
+
+ g_return_if_fail (GTK_IS_MODIFIER_STYLE (style));
+
+ priv = style->priv;
+ gtk_style_properties_get (priv->style, 0,
+ "font", &old_font,
+ NULL);
+
+ if ((!old_font && !font_desc) ||
+ (old_font && font_desc &&
+ pango_font_description_equal (old_font, font_desc)))
+ return;
+
+ if (font_desc)
+ gtk_style_properties_set (priv->style, 0,
+ "font", font_desc,
+ NULL);
+ else
+ gtk_style_properties_unset_property (priv->style, "font", 0);
+
+ g_signal_emit (style, signals[CHANGED], 0);
+}
+
+void
+gtk_modifier_style_map_color (GtkModifierStyle *style,
+ const gchar *name,
+ const GdkRGBA *color)
+{
+ GtkModifierStylePrivate *priv;
+ GtkSymbolicColor *symbolic_color = NULL;
+
+ g_return_if_fail (GTK_IS_MODIFIER_STYLE (style));
+ g_return_if_fail (name != NULL);
+
+ priv = style->priv;
+
+ if (color)
+ symbolic_color = gtk_symbolic_color_new_literal (color);
+
+ gtk_style_properties_map_color (priv->style,
+ name, symbolic_color);
+
+ g_signal_emit (style, signals[CHANGED], 0);
+}
+
+void
+gtk_modifier_style_set_color_property (GtkModifierStyle *style,
+ GType widget_type,
+ const gchar *prop_name,
+ const GdkColor *color)
+{
+ GtkModifierStylePrivate *priv;
+ const GdkColor *old_color;
+ gchar *str;
+
+ g_return_if_fail (GTK_IS_MODIFIER_STYLE (style));
+ g_return_if_fail (g_type_is_a (widget_type, GTK_TYPE_WIDGET));
+ g_return_if_fail (prop_name != NULL);
+
+ priv = style->priv;
+ str = g_strdup_printf ("-%s-%s", g_type_name (widget_type), prop_name);
+
+ old_color = g_hash_table_lookup (priv->color_properties, str);
+
+ if ((!color && !old_color) ||
+ (color && old_color && gdk_color_equal (color, old_color)))
+ {
+ g_free (str);
+ return;
+ }
+
+ if (color)
+ g_hash_table_insert (priv->color_properties, str,
+ gdk_color_copy (color));
+ else
+ g_hash_table_remove (priv->color_properties, str);
+
+ g_signal_emit (style, signals[CHANGED], 0);
+ g_free (str);
+}
diff --git a/gtk/gtkmodifierstyle.h b/gtk/gtkmodifierstyle.h
new file mode 100644
index 0000000000..6275b7c657
--- /dev/null
+++ b/gtk/gtkmodifierstyle.h
@@ -0,0 +1,74 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_MODIFIER_STYLE_H__
+#define __GTK_MODIFIER_STYLE_H__
+
+#include <glib-object.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_MODIFIER_STYLE (gtk_modifier_style_get_type ())
+#define GTK_MODIFIER_STYLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_MODIFIER_STYLE, GtkModifierStyle))
+#define GTK_MODIFIER_STYLE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_MODIFIER_STYLE, GtkModifierStyleClass))
+#define GTK_IS_MODIFIER_STYLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_MODIFIER_STYLE))
+#define GTK_IS_MODIFIER_STYLE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GTK_TYPE_MODIFIER_STYLE))
+#define GTK_MODIFIER_STYLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_MODIFIER_STYLE, GtkModifierStyleClass))
+
+typedef struct _GtkModifierStyle GtkModifierStyle;
+typedef struct _GtkModifierStyleClass GtkModifierStyleClass;
+
+struct _GtkModifierStyle
+{
+ GObject parent_object;
+ gpointer priv;
+};
+
+struct _GtkModifierStyleClass
+{
+ GObjectClass parent_class;
+};
+
+GType gtk_modifier_style_get_type (void) G_GNUC_CONST;
+
+GtkModifierStyle * gtk_modifier_style_new (void);
+
+void gtk_modifier_style_set_background_color (GtkModifierStyle *style,
+ GtkStateFlags state,
+ const GdkRGBA *color);
+void gtk_modifier_style_set_color (GtkModifierStyle *style,
+ GtkStateFlags state,
+ const GdkRGBA *color);
+void gtk_modifier_style_set_font (GtkModifierStyle *style,
+ const PangoFontDescription *font_desc);
+
+void gtk_modifier_style_map_color (GtkModifierStyle *style,
+ const gchar *name,
+ const GdkRGBA *color);
+
+void gtk_modifier_style_set_color_property (GtkModifierStyle *style,
+ GType widget_type,
+ const gchar *prop_name,
+ const GdkColor *color);
+
+G_END_DECLS
+
+#endif /* __GTK_MODIFIER_STYLE_H__ */
diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c
index 5a765eaf6a..f5a4db5cc9 100644
--- a/gtk/gtknotebook.c
+++ b/gtk/gtknotebook.c
@@ -462,7 +462,9 @@ static void gtk_notebook_paint (GtkWidget *widget,
cairo_t *cr);
static void gtk_notebook_draw_tab (GtkNotebook *notebook,
GtkNotebookPage *page,
- cairo_t *cr);
+ cairo_t *cr,
+ guint position,
+ gboolean is_last);
static void gtk_notebook_draw_arrow (GtkNotebook *notebook,
cairo_t *cr,
GtkNotebookArrow arrow);
@@ -2447,7 +2449,7 @@ gtk_notebook_draw (GtkWidget *widget,
gtk_notebook_draw_tab (notebook,
priv->cur_page,
- cr);
+ cr, 0, FALSE);
cairo_restore (cr);
@@ -4649,7 +4651,7 @@ gtk_notebook_remove_tab_label (GtkNotebook *notebook,
page->mnemonic_activate_signal);
page->mnemonic_activate_signal = 0;
- gtk_widget_set_state (page->tab_label, GTK_STATE_NORMAL);
+ gtk_widget_set_state_flags (page->tab_label, 0, TRUE);
gtk_widget_unparent (page->tab_label);
page->tab_label = NULL;
}
@@ -4895,8 +4897,8 @@ gtk_notebook_paint (GtkWidget *widget,
gint x, y;
guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
gint gap_x = 0, gap_width = 0, step = STEP_PREV;
- gboolean is_rtl;
- gint tab_pos;
+ gboolean is_rtl, cur_page_end;
+ gint tab_pos, i, cur_page_pos;
notebook = GTK_NOTEBOOK (widget);
priv = notebook->priv;
@@ -4965,7 +4967,7 @@ gtk_notebook_paint (GtkWidget *widget,
gap_x = priv->cur_page->allocation.x - allocation.x - border_width;
gap_width = priv->cur_page->allocation.width;
- step = is_rtl ? STEP_NEXT : STEP_PREV;
+ step = is_rtl ? STEP_PREV : STEP_NEXT;
break;
case GTK_POS_LEFT:
case GTK_POS_RIGHT:
@@ -4987,6 +4989,8 @@ gtk_notebook_paint (GtkWidget *widget,
showarrow = FALSE;
children = gtk_notebook_search_page (notebook, NULL, step, TRUE);
+ i = 0;
+
while (children)
{
page = children->data;
@@ -4996,8 +5000,18 @@ gtk_notebook_paint (GtkWidget *widget,
continue;
if (!gtk_widget_get_mapped (page->tab_label))
showarrow = TRUE;
- else if (page != priv->cur_page)
- gtk_notebook_draw_tab (notebook, page, cr);
+ else
+ {
+ if (page != priv->cur_page)
+ gtk_notebook_draw_tab (notebook, page, cr, i, children != NULL);
+ else
+ {
+ cur_page_pos = i;
+ cur_page_end = (children != NULL);
+ }
+
+ i++;
+ }
}
if (showarrow && priv->scrollable)
@@ -5013,18 +5027,22 @@ gtk_notebook_paint (GtkWidget *widget,
}
if (priv->operation != DRAG_OPERATION_REORDER)
- gtk_notebook_draw_tab (notebook, priv->cur_page, cr);
+ gtk_notebook_draw_tab (notebook, priv->cur_page, cr, cur_page_pos, cur_page_end);
}
static void
gtk_notebook_draw_tab (GtkNotebook *notebook,
GtkNotebookPage *page,
- cairo_t *cr)
+ cairo_t *cr,
+ guint position,
+ gboolean is_last)
{
GtkNotebookPrivate *priv;
GtkStateType state_type;
GtkWidget *widget;
-
+ GtkStyleContext *context;
+ GtkRegionFlags flags = 0;
+
if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
!gtk_widget_get_mapped (page->tab_label) ||
(page->allocation.width == 0) || (page->allocation.height == 0))
@@ -5038,6 +5056,20 @@ gtk_notebook_draw_tab (GtkNotebook *notebook,
else
state_type = GTK_STATE_ACTIVE;
+ if ((position + 1) % 2 == 0)
+ flags |= GTK_REGION_EVEN;
+ else
+ flags |= GTK_REGION_ODD;
+
+ if (position == 0)
+ flags |= GTK_REGION_FIRST;
+
+ if (is_last)
+ flags |= GTK_REGION_LAST;
+
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_add_region (context, "tab", flags);
+
gtk_paint_extension (gtk_widget_get_style (widget), cr,
state_type, GTK_SHADOW_OUT,
widget, "tab",
@@ -5063,6 +5095,8 @@ gtk_notebook_draw_tab (GtkNotebook *notebook,
allocation.width + 2 * focus_width,
allocation.height + 2 * focus_width);
}
+
+ gtk_style_context_remove_region (context, "tab");
}
static void
@@ -6171,9 +6205,9 @@ gtk_notebook_update_tab_states (GtkNotebook *notebook)
if (page->tab_label)
{
if (page == priv->cur_page)
- gtk_widget_set_state (page->tab_label, GTK_STATE_NORMAL);
- else
- gtk_widget_set_state (page->tab_label, GTK_STATE_ACTIVE);
+ gtk_widget_set_state_flags (page->tab_label, GTK_STATE_FLAG_ACTIVE, TRUE);
+ else
+ gtk_widget_set_state_flags (page->tab_label, 0, TRUE);
}
}
}
diff --git a/gtk/gtkplug-x11.c b/gtk/gtkplug-x11.c
index ca3af73c27..df98e93e29 100644
--- a/gtk/gtkplug-x11.c
+++ b/gtk/gtkplug-x11.c
@@ -57,7 +57,7 @@ static void xembed_set_info (GdkWindow *window,
GdkNativeWindow
_gtk_plug_windowing_get_id (GtkPlug *plug)
{
- return GDK_WINDOW_XWINDOW (gtk_widget_get_window (GTK_WIDGET (plug)));
+ return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (plug)));
}
void
@@ -146,7 +146,7 @@ xembed_set_info (GdkWindow *window,
buffer[1] = flags;
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
- GDK_WINDOW_XWINDOW (window),
+ GDK_WINDOW_XID (window),
xembed_info_atom, xembed_info_atom, 32,
PropModeReplace,
(unsigned char *)buffer, 2);
@@ -280,7 +280,7 @@ _gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent,
* Probably need check in _gtk_plug_add_to_socket
*/
- if (xre->parent != GDK_WINDOW_XWINDOW (priv->socket_window))
+ if (xre->parent != GDK_WINDOW_XID (priv->socket_window))
{
GtkWidget *widget = GTK_WIDGET (plug);
@@ -297,7 +297,7 @@ _gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent,
* be invisible to the app.
*/
- if (xre->parent == GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)))
+ if (xre->parent == GDK_WINDOW_XID (gdk_screen_get_root_window (screen)))
{
GTK_NOTE (PLUGSOCKET, g_message ("GtkPlug: calling gtk_plug_send_delete_event()"));
_gtk_plug_send_delete_event (widget);
@@ -309,7 +309,7 @@ _gtk_plug_windowing_filter_func (GdkXEvent *gdk_xevent,
goto done;
}
- if (xre->parent != GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)))
+ if (xre->parent != GDK_WINDOW_XID (gdk_screen_get_root_window (screen)))
{
/* Start of embedding protocol */
diff --git a/gtk/gtkradiobutton.c b/gtk/gtkradiobutton.c
index ff607cea56..424d8505f4 100644
--- a/gtk/gtkradiobutton.c
+++ b/gtk/gtkradiobutton.c
@@ -217,7 +217,7 @@ gtk_radio_button_init (GtkRadioButton *radio_button)
priv->group = g_slist_prepend (NULL, radio_button);
_gtk_button_set_depressed (GTK_BUTTON (radio_button), TRUE);
- gtk_widget_set_state (GTK_WIDGET (radio_button), GTK_STATE_ACTIVE);
+ gtk_widget_set_state_flags (GTK_WIDGET (radio_button), GTK_STATE_FLAG_ACTIVE, TRUE);
}
static void
@@ -790,7 +790,7 @@ gtk_radio_button_clicked (GtkButton *button)
GtkRadioButtonPrivate *priv = radio_button->priv;
GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
GtkToggleButton *tmp_button;
- GtkStateType new_state;
+ GtkStateFlags new_state = 0;
GSList *tmp_list;
gint toggled;
gboolean depressed;
@@ -818,14 +818,19 @@ gtk_radio_button_clicked (GtkButton *button)
if (!tmp_button)
{
- new_state = (button->priv->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE);
+ if (button->priv->in_button)
+ new_state |= GTK_STATE_FLAG_PRELIGHT;
+
+ new_state |= GTK_STATE_FLAG_ACTIVE;
}
else
{
toggled = TRUE;
_gtk_toggle_button_set_active (toggle_button,
!gtk_toggle_button_get_active (toggle_button));
- new_state = (button->priv->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL);
+
+ if (button->priv->in_button)
+ new_state |= GTK_STATE_FLAG_PRELIGHT;
}
}
else
@@ -847,7 +852,10 @@ gtk_radio_button_clicked (GtkButton *button)
}
}
- new_state = (button->priv->in_button ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE);
+ if (button->priv->in_button)
+ new_state |= GTK_STATE_FLAG_PRELIGHT;
+
+ new_state |= GTK_STATE_FLAG_ACTIVE;
}
if (gtk_toggle_button_get_inconsistent (toggle_button))
@@ -857,8 +865,8 @@ gtk_radio_button_clicked (GtkButton *button)
else
depressed = gtk_toggle_button_get_active (toggle_button);
- if (gtk_widget_get_state (GTK_WIDGET (button)) != new_state)
- gtk_widget_set_state (GTK_WIDGET (button), new_state);
+ if (gtk_widget_get_state_flags (GTK_WIDGET (button)) != new_state)
+ gtk_widget_set_state_flags (GTK_WIDGET (button), new_state, TRUE);
if (toggled)
{
@@ -883,9 +891,8 @@ gtk_radio_button_draw_indicator (GtkCheckButton *check_button,
GtkWidget *child;
GtkButton *button;
GtkToggleButton *toggle_button;
- GtkStateType state_type;
- GtkShadowType shadow_type;
- GtkStyle *style;
+ GtkStyleContext *context;
+ GtkStateFlags state = 0;
GdkWindow *window;
gint x, y;
gint indicator_size, indicator_spacing;
@@ -897,10 +904,10 @@ gtk_radio_button_draw_indicator (GtkCheckButton *check_button,
widget = GTK_WIDGET (check_button);
button = GTK_BUTTON (check_button);
toggle_button = GTK_TOGGLE_BUTTON (check_button);
+ context = gtk_widget_get_style_context (widget);
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
- style = gtk_widget_get_style (widget);
gtk_widget_style_get (widget,
"interior-focus", &interior_focus,
"focus-line-width", &focus_width,
@@ -921,37 +928,34 @@ gtk_radio_button_draw_indicator (GtkCheckButton *check_button,
x += focus_width + focus_pad;
if (gtk_toggle_button_get_inconsistent (toggle_button))
- shadow_type = GTK_SHADOW_ETCHED_IN;
+ state |= GTK_STATE_FLAG_INCONSISTENT;
else if (gtk_toggle_button_get_active (toggle_button))
- shadow_type = GTK_SHADOW_IN;
- else
- shadow_type = GTK_SHADOW_OUT;
+ state |= GTK_STATE_FLAG_ACTIVE;
if (button->priv->activate_timeout || (button->priv->button_down && button->priv->in_button))
- state_type = GTK_STATE_ACTIVE;
- else if (button->priv->in_button)
- state_type = GTK_STATE_PRELIGHT;
+ state |= GTK_STATE_FLAG_SELECTED;
+
+ if (button->priv->in_button)
+ state |= GTK_STATE_FLAG_PRELIGHT;
else if (!gtk_widget_is_sensitive (widget))
- state_type = GTK_STATE_INSENSITIVE;
- else
- state_type = GTK_STATE_NORMAL;
+ state |= GTK_STATE_FLAG_INSENSITIVE;
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
x = allocation.width - (indicator_size + x);
- if (gtk_widget_get_state (widget) == GTK_STATE_PRELIGHT)
- {
- gtk_paint_flat_box (style, cr,
- GTK_STATE_PRELIGHT,
- GTK_SHADOW_ETCHED_OUT,
- widget, "checkbutton",
- border_width, border_width,
- allocation.width - (2 * border_width),
- allocation.height - (2 * border_width));
- }
+ gtk_style_context_save (context);
+ gtk_style_context_set_state (context, state);
+
+ if (state & GTK_STATE_FLAG_PRELIGHT)
+ gtk_render_background (context, cr,
+ border_width, border_width,
+ allocation.width - (2 * border_width),
+ allocation.height - (2 * border_width));
+
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_CHECK);
+
+ gtk_render_option (context, cr,
+ x, y, indicator_size, indicator_size);
- gtk_paint_option (style, cr,
- state_type, shadow_type,
- widget, "radiobutton",
- x, y, indicator_size, indicator_size);
+ gtk_style_context_restore (context);
}
diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c
index 27e8a7424e..3345e88a32 100644
--- a/gtk/gtkrange.c
+++ b/gtk/gtkrange.c
@@ -2956,7 +2956,9 @@ gtk_range_adjustment_value_changed (GtkAdjustment *adjustment,
gtk_widget_queue_draw (GTK_WIDGET (range));
/* setup a timer to ensure the range isn't lagging too much behind the scroll position */
if (!priv->repaint_id)
- priv->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS, 181, force_repaint, range, NULL);
+ priv->repaint_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS,
+ 181, force_repaint,
+ range, NULL);
}
/* Note that we don't round off to priv->round_digits here.
@@ -4076,8 +4078,8 @@ initial_timeout (gpointer data)
g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
priv->timer->timeout_id = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
- second_timeout,
- range);
+ second_timeout,
+ range);
/* remove self */
return FALSE;
}
@@ -4099,8 +4101,8 @@ gtk_range_add_step_timer (GtkRange *range,
priv->timer = g_new (GtkRangeStepTimer, 1);
priv->timer->timeout_id = gdk_threads_add_timeout (timeout,
- initial_timeout,
- range);
+ initial_timeout,
+ range);
priv->timer->step = step;
gtk_range_scroll (range, priv->timer->step);
@@ -4143,8 +4145,8 @@ gtk_range_reset_update_timer (GtkRange *range)
gtk_range_remove_update_timer (range);
priv->update_timeout_id = gdk_threads_add_timeout (UPDATE_DELAY,
- update_timeout,
- range);
+ update_timeout,
+ range);
}
static void
diff --git a/gtk/gtkrc.c b/gtk/gtkrc.c
index 84efc27a06..54cf2fcd76 100644
--- a/gtk/gtkrc.c
+++ b/gtk/gtkrc.c
@@ -541,9 +541,11 @@ gtk_rc_add_initial_default_files (void)
* gtk_rc_add_default_file:
* @filename: the pathname to the file. If @filename is not absolute, it
* is searched in the current directory.
- *
+ *
* Adds a file to the list of files to be parsed at the
* end of gtk_init().
+ *
+ * Deprecated:3.0: Use #GtkStyleContext with a custom #GtkStyleProvider instead
**/
void
gtk_rc_add_default_file (const gchar *filename)
@@ -571,9 +573,11 @@ gtk_rc_add_default_file (const gchar *filename)
/**
* gtk_rc_set_default_files:
* @filenames: A %NULL-terminated list of filenames.
- *
+ *
* Sets the list of files that GTK+ will read at the
* end of gtk_init().
+ *
+ * Deprecated:3.0: Use #GtkStyleContext with a custom #GtkStyleProvider instead
**/
void
gtk_rc_set_default_files (gchar **filenames)
@@ -608,6 +612,8 @@ gtk_rc_set_default_files (gchar **filenames)
* Return value: (transfer none): A %NULL-terminated array of filenames.
* This memory is owned by GTK+ and must not be freed by the application.
* If you want to store this information, you should make a copy.
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
**/
gchar **
gtk_rc_get_default_files (void)
@@ -926,6 +932,7 @@ gtk_rc_parse_string (const gchar *rc_string)
g_return_if_fail (rc_string != NULL);
+#if 0
rc_file = g_new (GtkRcFile, 1);
rc_file->is_string = TRUE;
rc_file->name = g_strdup (rc_string);
@@ -938,6 +945,7 @@ gtk_rc_parse_string (const gchar *rc_string)
for (tmp_list = rc_contexts; tmp_list; tmp_list = tmp_list->next)
gtk_rc_context_parse_string (tmp_list->data, rc_string);
+#endif
}
static GtkRcFile *
@@ -1117,10 +1125,12 @@ gtk_rc_parse (const gchar *filename)
g_return_if_fail (filename != NULL);
+#if 0
add_to_rc_file_list (&global_rc_files, filename, TRUE);
for (tmp_list = rc_contexts; tmp_list; tmp_list = tmp_list->next)
gtk_rc_context_parse_file (tmp_list->data, filename, GTK_PATH_PRIO_RC, TRUE);
+#endif
}
/* Handling of RC styles */
@@ -1960,17 +1970,19 @@ sort_and_dereference_sets (GSList *styles)
/**
* gtk_rc_get_style:
* @widget: a #GtkWidget
- *
+ *
* Finds all matching RC styles for a given widget,
- * composites them together, and then creates a
+ * composites them together, and then creates a
* #GtkStyle representing the composite appearance.
- * (GTK+ actually keeps a cache of previously
+ * (GTK+ actually keeps a cache of previously
* created styles, so a new style may not be
* created.)
- *
+ *
* Returns: the resulting style. No refcount is added
* to the returned style, so if you want to save this
* style around, you should add a reference yourself.
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
**/
GtkStyle *
gtk_rc_get_style (GtkWidget *widget)
@@ -2078,16 +2090,18 @@ gtk_rc_get_style (GtkWidget *widget)
* |[
* gtk_widget_path (widget, NULL, &path, NULL);
* gtk_widget_class_path (widget, NULL, &class_path, NULL);
- * gtk_rc_get_style_by_paths (gtk_widget_get_settings (widget),
+ * gtk_rc_get_style_by_paths (gtk_widget_get_settings (widget),
* path, class_path,
* G_OBJECT_TYPE (widget));
* ]|
- *
+ *
* Return value: (transfer none): A style created by matching with the
* supplied paths, or %NULL if nothing matching was specified and the
* default style should be used. The returned value is owned by GTK+
* as part of an internal cache, so you must call g_object_ref() on
* the returned value if you want to keep a reference to it.
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
**/
GtkStyle *
gtk_rc_get_style_by_paths (GtkSettings *settings,
@@ -2167,6 +2181,11 @@ gtk_rc_get_style_by_paths (GtkSettings *settings,
return NULL;
}
+/**
+ * gtk_rc_scanner_new:
+ *
+ * Deprecated:3.0: Use #GtkCssProvider instead
+ */
GScanner*
gtk_rc_scanner_new (void)
{
@@ -2183,6 +2202,7 @@ gtk_rc_parse_any (GtkRcContext *context,
guint i;
gboolean done;
+#if 0
scanner = gtk_rc_scanner_new ();
if (input_fd >= 0)
@@ -2264,6 +2284,7 @@ gtk_rc_parse_any (GtkRcContext *context,
}
g_scanner_destroy (scanner);
+#endif
}
static guint
@@ -3705,6 +3726,13 @@ gtk_rc_parse_engine (GtkRcContext *context,
return result;
}
+/**
+ * gtk_rc_parse_state:
+ * @scanner:
+ * @state:
+ *
+ * Deprecated:3.0: Use #GtkCssProvider instead
+ */
guint
gtk_rc_parse_state (GScanner *scanner,
GtkStateType *state)
@@ -3756,6 +3784,13 @@ gtk_rc_parse_state (GScanner *scanner,
return G_TOKEN_NONE;
}
+/**
+ * gtk_rc_parse_priority:
+ * @scanner:
+ * @priority:
+ *
+ * Deprecated:3.0: Use #GtkCssProvider instead
+ */
guint
gtk_rc_parse_priority (GScanner *scanner,
GtkPathPriorityType *priority)
@@ -3819,6 +3854,8 @@ gtk_rc_parse_priority (GScanner *scanner,
*
* Returns: %G_TOKEN_NONE if parsing succeeded, otherwise the token
* that was expected but not found
+ *
+ * Deprecated:3.0: Use #GtkCssProvider instead
*/
guint
gtk_rc_parse_color (GScanner *scanner,
@@ -3841,6 +3878,8 @@ gtk_rc_parse_color (GScanner *scanner,
* that was expected but not found
*
* Since: 2.12
+ *
+ * Deprecated:3.0: Use #GtkCssProvider instead
*/
guint
gtk_rc_parse_color_full (GScanner *scanner,
diff --git a/gtk/gtkrc.h b/gtk/gtkrc.h
index 598906dd80..65635eebb9 100644
--- a/gtk/gtkrc.h
+++ b/gtk/gtkrc.h
@@ -131,6 +131,8 @@ gboolean _gtk_rc_match_widget_class (GSList *list,
gchar *path,
gchar *path_reversed);
+#if !defined(GTK_DISABLE_DEPRECATED) || defined(GTK_COMPILATION)
+
void gtk_rc_add_default_file (const gchar *filename);
void gtk_rc_set_default_files (gchar **filenames);
gchar** gtk_rc_get_default_files (void);
@@ -217,6 +219,8 @@ guint gtk_rc_parse_state (GScanner *scanner,
guint gtk_rc_parse_priority (GScanner *scanner,
GtkPathPriorityType *priority);
+#endif
+
/* rc properties
* (structure forward declared in gtkstyle.h)
*/
diff --git a/gtk/gtkscrollbar.c b/gtk/gtkscrollbar.c
index d04ed7c387..df4ad75b9c 100644
--- a/gtk/gtkscrollbar.c
+++ b/gtk/gtkscrollbar.c
@@ -118,6 +118,10 @@ gtk_scrollbar_class_init (GtkScrollbarClass *class)
static void
gtk_scrollbar_init (GtkScrollbar *scrollbar)
{
+ GtkStyleContext *context;
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (scrollbar));
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_SCROLLBAR);
}
static void
diff --git a/gtk/gtkselection.c b/gtk/gtkselection.c
index 70b3c055c8..4148f37b23 100644
--- a/gtk/gtkselection.c
+++ b/gtk/gtkselection.c
@@ -2567,7 +2567,7 @@ _gtk_selection_incr_event (GdkWindow *window,
#ifdef DEBUG_SELECTION
g_message ("INCR: put %d bytes (offset = %d) into window 0x%lx , property %ld",
num_bytes, info->conversions[i].offset,
- GDK_WINDOW_XWINDOW(info->requestor), event->atom);
+ GDK_WINDOW_XID(info->requestor), event->atom);
#endif
bytes_per_item = gtk_selection_bytes_per_item (info->conversions[i].data.format);
diff --git a/gtk/gtksettings.c b/gtk/gtksettings.c
index 9b622254c2..f84bc9b283 100644
--- a/gtk/gtksettings.c
+++ b/gtk/gtksettings.c
@@ -30,6 +30,9 @@
#include "gtkwidget.h"
#include "gtktypeutils.h"
#include "gtkprivate.h"
+#include "gtkcssprovider.h"
+#include "gtksymboliccolor.h"
+#include "gtkversion.h"
#ifdef GDK_WINDOWING_X11
#include "x11/gdkx.h"
@@ -179,6 +182,8 @@ enum {
};
/* --- prototypes --- */
+static void gtk_settings_provider_iface_init (GtkStyleProviderIface *iface);
+
static void gtk_settings_finalize (GObject *object);
static void gtk_settings_get_property (GObject *object,
guint property_id,
@@ -203,6 +208,7 @@ static void settings_update_font_options (GtkSettings *setting
static gboolean settings_update_fontconfig (GtkSettings *settings);
#endif
static void settings_update_color_scheme (GtkSettings *settings);
+static void settings_update_theme (GtkSettings *settings);
static void merge_color_scheme (GtkSettings *settings,
const GValue *value,
@@ -221,7 +227,9 @@ static GSList *object_list = NULL;
static guint class_n_properties = 0;
-G_DEFINE_TYPE (GtkSettings, gtk_settings, G_TYPE_OBJECT)
+G_DEFINE_TYPE_EXTENDED (GtkSettings, gtk_settings, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
+ gtk_settings_provider_iface_init));
/* --- functions --- */
static void
@@ -1246,6 +1254,78 @@ gtk_settings_class_init (GtkSettingsClass *class)
g_assert (result == PROP_IM_STATUS_STYLE);
}
+static GtkStyleProperties *
+gtk_settings_get_style (GtkStyleProvider *provider,
+ GtkWidgetPath *path)
+{
+ PangoFontDescription *font_desc;
+ gchar *font_name, *color_scheme;
+ GtkSettings *settings;
+ GtkStyleProperties *props;
+ gchar **colors;
+ guint i;
+
+ settings = GTK_SETTINGS (provider);
+ props = gtk_style_properties_new ();
+
+ g_object_get (settings,
+ "gtk-font-name", &font_name,
+ "gtk-color-scheme", &color_scheme,
+ NULL);
+
+ colors = g_strsplit_set (color_scheme, "\n;", -1);
+
+ for (i = 0; colors[i]; i++)
+ {
+ GtkSymbolicColor *color;
+ gchar *name, *pos;
+ GdkRGBA col;
+
+ if (!*colors[i])
+ continue;
+
+ name = colors[i];
+ pos = strchr (colors[i], ':');
+
+ if (!pos)
+ continue;
+
+ /* Set NUL after color name */
+ *pos = '\0';
+ pos++;
+
+ /* Find start of color string */
+ while (*pos == ' ')
+ pos++;
+
+ if (!*pos || !gdk_rgba_parse (&col, pos))
+ continue;
+
+ color = gtk_symbolic_color_new_literal (&col);
+ gtk_style_properties_map_color (props, name, color);
+ gtk_symbolic_color_unref (color);
+ }
+
+ font_desc = pango_font_description_from_string (font_name);
+
+ gtk_style_properties_set (props, 0,
+ "font", font_desc,
+ NULL);
+
+ pango_font_description_free (font_desc);
+ g_strfreev (colors);
+ g_free (color_scheme);
+ g_free (font_name);
+
+ return props;
+}
+
+static void
+gtk_settings_provider_iface_init (GtkStyleProviderIface *iface)
+{
+ iface->get_style = gtk_settings_get_style;
+}
+
static void
gtk_settings_finalize (GObject *object)
{
@@ -1265,6 +1345,46 @@ gtk_settings_finalize (GObject *object)
G_OBJECT_CLASS (gtk_settings_parent_class)->finalize (object);
}
+static void
+settings_init_style (GtkSettings *settings)
+{
+ static GtkCssProvider *css_provider = NULL;
+ GtkCssProvider *default_provider;
+
+ /* Add provider for user file */
+ if (G_UNLIKELY (!css_provider))
+ {
+ gchar *css_path;
+
+ css_provider = gtk_css_provider_new ();
+ css_path = g_build_filename (g_get_user_config_dir (),
+ "gtk-3.0",
+ "gtk.css",
+ NULL);
+
+ if (g_file_test (css_path,
+ G_FILE_TEST_IS_REGULAR))
+ gtk_css_provider_load_from_path (css_provider, css_path, NULL);
+
+ g_free (css_path);
+ }
+
+ gtk_style_context_add_provider_for_screen (settings->screen,
+ GTK_STYLE_PROVIDER (css_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+
+ default_provider = gtk_css_provider_get_default ();
+ gtk_style_context_add_provider_for_screen (settings->screen,
+ GTK_STYLE_PROVIDER (default_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_FALLBACK);
+
+ gtk_style_context_add_provider_for_screen (settings->screen,
+ GTK_STYLE_PROVIDER (settings),
+ GTK_STYLE_PROVIDER_PRIORITY_SETTINGS);
+
+ settings_update_theme (settings);
+}
+
/**
* gtk_settings_get_for_screen:
* @screen: a #GdkScreen.
@@ -1290,7 +1410,7 @@ gtk_settings_get_for_screen (GdkScreen *screen)
g_object_set_data_full (G_OBJECT (screen), I_("gtk-settings"),
settings, g_object_unref);
- gtk_rc_reparse_all_for_settings (settings, TRUE);
+ settings_init_style (settings);
settings_update_double_click (settings);
#ifdef GDK_WINDOWING_X11
settings_update_cursor_theme (settings);
@@ -1440,6 +1560,10 @@ gtk_settings_notify (GObject *object,
break;
case PROP_COLOR_SCHEME:
settings_update_color_scheme (settings);
+ gtk_style_context_reset_widgets (settings->screen);
+ break;
+ case PROP_THEME_NAME:
+ settings_update_theme (settings);
break;
#ifdef GDK_WINDOWING_X11
case PROP_XFT_DPI:
@@ -1448,18 +1572,18 @@ gtk_settings_notify (GObject *object,
* widgets with gtk_widget_style_set(), and also causes more
* recomputation than necessary.
*/
- gtk_rc_reset_styles (GTK_SETTINGS (object));
+ gtk_style_context_reset_widgets (settings->screen);
break;
case PROP_XFT_ANTIALIAS:
case PROP_XFT_HINTING:
case PROP_XFT_HINTSTYLE:
case PROP_XFT_RGBA:
settings_update_font_options (settings);
- gtk_rc_reset_styles (GTK_SETTINGS (object));
+ gtk_style_context_reset_widgets (settings->screen);
break;
case PROP_FONTCONFIG_TIMESTAMP:
if (settings_update_fontconfig (settings))
- gtk_rc_reset_styles (GTK_SETTINGS (object));
+ gtk_style_context_reset_widgets (settings->screen);
break;
case PROP_CURSOR_THEME_NAME:
case PROP_CURSOR_THEME_SIZE:
@@ -2478,6 +2602,54 @@ settings_update_color_scheme (GtkSettings *settings)
}
}
+static void
+settings_update_theme (GtkSettings *settings)
+{
+ static GQuark quark_theme_name = 0;
+ GtkCssProvider *provider, *new_provider = NULL;
+ gboolean prefer_dark_theme;
+ gchar *theme_name;
+
+ if (G_UNLIKELY (!quark_theme_name))
+ quark_theme_name = g_quark_from_static_string ("gtk-settings-theme-name");
+
+ provider = g_object_get_qdata (G_OBJECT (settings), quark_theme_name);
+
+ g_object_get (settings,
+ "gtk-theme-name", &theme_name,
+ "gtk-application-prefer-dark-theme", &prefer_dark_theme,
+ NULL);
+
+ if (theme_name && *theme_name)
+ {
+ gchar *variant = NULL;
+
+ if (prefer_dark_theme)
+ variant = "dark";
+
+ new_provider = gtk_css_provider_get_named (theme_name, variant);
+ g_free (theme_name);
+ }
+
+ if (new_provider != provider)
+ {
+ if (provider)
+ gtk_style_context_remove_provider_for_screen (settings->screen,
+ GTK_STYLE_PROVIDER (provider));
+
+ if (new_provider)
+ {
+ gtk_style_context_add_provider_for_screen (settings->screen,
+ GTK_STYLE_PROVIDER (new_provider),
+ GTK_STYLE_PROVIDER_PRIORITY_THEME);
+ g_object_ref (new_provider);
+ }
+
+ g_object_set_qdata_full (G_OBJECT (settings), quark_theme_name,
+ new_provider, (GDestroyNotify) g_object_unref);
+ }
+}
+
static gboolean
add_color_to_hash (gchar *name,
GdkColor *color,
@@ -2708,3 +2880,9 @@ get_color_scheme (GtkSettings *settings)
return g_string_free (string, FALSE);
}
+
+GdkScreen *
+_gtk_settings_get_screen (GtkSettings *settings)
+{
+ return settings->screen;
+}
diff --git a/gtk/gtksettings.h b/gtk/gtksettings.h
index 36fb9a5db9..6958db0f56 100644
--- a/gtk/gtksettings.h
+++ b/gtk/gtksettings.h
@@ -133,6 +133,8 @@ gboolean _gtk_settings_parse_convert (GtkRcPropertyParser parser,
GParamSpec *pspec,
GValue *dest_value);
+GdkScreen *_gtk_settings_get_screen (GtkSettings *settings);
+
G_END_DECLS
diff --git a/gtk/gtksocket-x11.c b/gtk/gtksocket-x11.c
index b408fa6bcb..963d832b85 100644
--- a/gtk/gtksocket-x11.c
+++ b/gtk/gtksocket-x11.c
@@ -39,7 +39,8 @@
#include "gtkdnd.h"
#include "gtkdebug.h"
-#include "x11/gdkx.h"
+#include "gdk/x11/gdkx.h"
+#include "gdk/gdkprivate.h"
#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
@@ -57,7 +58,7 @@ static gboolean xembed_get_info (GdkWindow *gdk_window,
GdkNativeWindow
_gtk_socket_windowing_get_id (GtkSocket *socket)
{
- return GDK_WINDOW_XWINDOW (gtk_widget_get_window (GTK_WIDGET (socket)));
+ return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket)));
}
void
@@ -69,7 +70,7 @@ _gtk_socket_windowing_realize_window (GtkSocket *socket)
window = gtk_widget_get_window (GTK_WIDGET (socket));
XGetWindowAttributes (GDK_WINDOW_XDISPLAY (window),
- GDK_WINDOW_XWINDOW (window),
+ GDK_WINDOW_XID (window),
&xattrs);
/* Sooooo, it turns out that mozilla, as per the gtk2xt code selects
@@ -81,7 +82,7 @@ _gtk_socket_windowing_realize_window (GtkSocket *socket)
this for GtkSocket, so we unselect it here, fixing the crashes in
firefox. */
XSelectInput (GDK_WINDOW_XDISPLAY (window),
- GDK_WINDOW_XWINDOW (window),
+ GDK_WINDOW_XID (window),
(xattrs.your_event_mask & ~ButtonPressMask) |
SubstructureNotifyMask | SubstructureRedirectMask);
}
@@ -90,7 +91,7 @@ void
_gtk_socket_windowing_end_embedding_toplevel (GtkSocket *socket)
{
gtk_window_remove_embedded_xid (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (socket))),
- GDK_WINDOW_XWINDOW (socket->plug_window));
+ GDK_WINDOW_XID (socket->plug_window));
}
void
@@ -105,7 +106,7 @@ _gtk_socket_windowing_size_request (GtkSocket *socket)
socket->request_height = 1;
if (XGetWMNormalHints (GDK_WINDOW_XDISPLAY (socket->plug_window),
- GDK_WINDOW_XWINDOW (socket->plug_window),
+ GDK_WINDOW_XID (socket->plug_window),
&hints, &supplied))
{
if (hints.flags & PMinSize)
@@ -134,8 +135,8 @@ _gtk_socket_windowing_send_key_event (GtkSocket *socket,
memset (&xkey, 0, sizeof (xkey));
xkey.type = (gdk_event->type == GDK_KEY_PRESS) ? KeyPress : KeyRelease;
- xkey.window = GDK_WINDOW_XWINDOW (socket->plug_window);
- xkey.root = GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen));
+ xkey.window = GDK_WINDOW_XID (socket->plug_window);
+ xkey.root = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
xkey.subwindow = None;
xkey.time = gdk_event->key.time;
xkey.x = 0;
@@ -148,7 +149,7 @@ _gtk_socket_windowing_send_key_event (GtkSocket *socket,
gdk_error_trap_push ();
XSendEvent (GDK_WINDOW_XDISPLAY (socket->plug_window),
- GDK_WINDOW_XWINDOW (socket->plug_window),
+ GDK_WINDOW_XID (socket->plug_window),
False,
(mask_key_presses ? KeyPressMask : NoEventMask),
(XEvent *)&xkey);
@@ -220,8 +221,8 @@ _gtk_socket_windowing_send_configure_event (GtkSocket *socket)
memset (&xconfigure, 0, sizeof (xconfigure));
xconfigure.type = ConfigureNotify;
- xconfigure.event = GDK_WINDOW_XWINDOW (socket->plug_window);
- xconfigure.window = GDK_WINDOW_XWINDOW (socket->plug_window);
+ xconfigure.event = GDK_WINDOW_XID (socket->plug_window);
+ xconfigure.window = GDK_WINDOW_XID (socket->plug_window);
/* The ICCCM says that synthetic events should have root relative
* coordinates. We still aren't really ICCCM compliant, since
@@ -243,7 +244,7 @@ _gtk_socket_windowing_send_configure_event (GtkSocket *socket)
gdk_error_trap_push ();
XSendEvent (GDK_WINDOW_XDISPLAY (socket->plug_window),
- GDK_WINDOW_XWINDOW (socket->plug_window),
+ GDK_WINDOW_XID (socket->plug_window),
False, NoEventMask, (XEvent *)&xconfigure);
gdk_error_trap_pop_ignored ();
}
@@ -252,7 +253,7 @@ void
_gtk_socket_windowing_select_plug_window_input (GtkSocket *socket)
{
XSelectInput (GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (socket))),
- GDK_WINDOW_XWINDOW (socket->plug_window),
+ GDK_WINDOW_XID (socket->plug_window),
StructureNotifyMask | PropertyChangeMask);
}
@@ -283,13 +284,13 @@ _gtk_socket_windowing_embed_notify (GtkSocket *socket)
gdk_error_trap_push ();
XFixesChangeSaveSet (GDK_DISPLAY_XDISPLAY (display),
- GDK_WINDOW_XWINDOW (socket->plug_window),
+ GDK_WINDOW_XID (socket->plug_window),
SetModeInsert, SaveSetRoot, SaveSetUnmap);
gdk_error_trap_pop_ignored ();
#endif
_gtk_xembed_send_message (socket->plug_window,
XEMBED_EMBEDDED_NOTIFY, 0,
- GDK_WINDOW_XWINDOW (gtk_widget_get_window (GTK_WIDGET (socket))),
+ GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (socket))),
socket->xembed_version);
}
@@ -309,7 +310,7 @@ xembed_get_info (GdkWindow *window,
gdk_error_trap_push ();
status = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display),
- GDK_WINDOW_XWINDOW (window),
+ GDK_WINDOW_XID (window),
xembed_info_atom,
0, 2, False,
xembed_info_atom, &type, &format,
@@ -508,7 +509,7 @@ _gtk_socket_windowing_filter_func (GdkXEvent *gdk_xevent,
/* Note that we get destroy notifies both from SubstructureNotify on
* our window and StructureNotify on socket->plug_window
*/
- if (socket->plug_window && (xdwe->window == GDK_WINDOW_XWINDOW (socket->plug_window)))
+ if (socket->plug_window && (xdwe->window == GDK_WINDOW_XID (socket->plug_window)))
{
gboolean result;
@@ -554,7 +555,7 @@ _gtk_socket_windowing_filter_func (GdkXEvent *gdk_xevent,
break;
case PropertyNotify:
if (socket->plug_window &&
- xevent->xproperty.window == GDK_WINDOW_XWINDOW (socket->plug_window))
+ xevent->xproperty.window == GDK_WINDOW_XID (socket->plug_window))
{
GdkDragProtocol protocol;
@@ -615,7 +616,7 @@ _gtk_socket_windowing_filter_func (GdkXEvent *gdk_xevent,
GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - ReparentNotify received"));
if (!socket->plug_window &&
- xre->parent == GDK_WINDOW_XWINDOW (window))
+ xre->parent == GDK_WINDOW_XID (window))
{
_gtk_socket_add_window (socket, xre->window, FALSE);
@@ -629,8 +630,8 @@ _gtk_socket_windowing_filter_func (GdkXEvent *gdk_xevent,
else
{
if (socket->plug_window &&
- xre->window == GDK_WINDOW_XWINDOW (socket->plug_window) &&
- xre->parent != GDK_WINDOW_XWINDOW (window))
+ xre->window == GDK_WINDOW_XID (socket->plug_window) &&
+ xre->parent != GDK_WINDOW_XID (window))
{
gboolean result;
@@ -650,7 +651,7 @@ _gtk_socket_windowing_filter_func (GdkXEvent *gdk_xevent,
}
case UnmapNotify:
if (socket->plug_window &&
- xevent->xunmap.window == GDK_WINDOW_XWINDOW (socket->plug_window))
+ xevent->xunmap.window == GDK_WINDOW_XID (socket->plug_window))
{
GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - Unmap notify"));
diff --git a/gtk/gtkspinner.c b/gtk/gtkspinner.c
index 2af87b061f..65bf7e2353 100644
--- a/gtk/gtkspinner.c
+++ b/gtk/gtkspinner.c
@@ -62,20 +62,11 @@ enum {
struct _GtkSpinnerPrivate
{
- guint current;
- guint num_steps;
- guint cycle_duration;
gboolean active;
- guint timeout;
};
-static void gtk_spinner_dispose (GObject *gobject);
-static void gtk_spinner_map (GtkWidget *widget);
-static void gtk_spinner_unmap (GtkWidget *widget);
static gboolean gtk_spinner_draw (GtkWidget *widget,
cairo_t *cr);
-static void gtk_spinner_style_set (GtkWidget *widget,
- GtkStyle *prev_style);
static void gtk_spinner_get_property (GObject *object,
guint param_id,
GValue *value,
@@ -106,15 +97,11 @@ gtk_spinner_class_init (GtkSpinnerClass *klass)
gobject_class = G_OBJECT_CLASS(klass);
g_type_class_add_private (gobject_class, sizeof (GtkSpinnerPrivate));
- gobject_class->dispose = gtk_spinner_dispose;
gobject_class->get_property = gtk_spinner_get_property;
gobject_class->set_property = gtk_spinner_set_property;
widget_class = GTK_WIDGET_CLASS(klass);
- widget_class->map = gtk_spinner_map;
- widget_class->unmap = gtk_spinner_unmap;
widget_class->draw = gtk_spinner_draw;
- widget_class->style_set = gtk_spinner_style_set;
widget_class->get_accessible = gtk_spinner_get_accessible;
widget_class->get_preferred_width = gtk_spinner_get_preferred_width;
widget_class->get_preferred_height = gtk_spinner_get_preferred_height;
@@ -140,6 +127,8 @@ gtk_spinner_class_init (GtkSpinnerClass *klass)
* (see the #GtkSpinner:cycle-duration style property).
*
* Since: 2.20
+ *
+ * Deprecated: 3.0
*/
gtk_widget_class_install_style_property (widget_class,
g_param_spec_uint ("num-steps",
@@ -156,6 +145,8 @@ gtk_spinner_class_init (GtkSpinnerClass *klass)
* The duration in milliseconds for the spinner to complete a full cycle.
*
* Since: 2.20
+ *
+ * Deprecated: 3.0
*/
gtk_widget_class_install_style_property (widget_class,
g_param_spec_uint ("cycle-duration",
@@ -207,16 +198,17 @@ static void
gtk_spinner_init (GtkSpinner *spinner)
{
GtkSpinnerPrivate *priv;
+ GtkStyleContext *context;
priv = G_TYPE_INSTANCE_GET_PRIVATE (spinner,
GTK_TYPE_SPINNER,
GtkSpinnerPrivate);
- priv->current = 0;
- priv->timeout = 0;
-
spinner->priv = priv;
gtk_widget_set_has_window (GTK_WIDGET (spinner), FALSE);
+
+ context = gtk_widget_get_style_context (GTK_WIDGET (spinner));
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_SPINNER);
}
static void
@@ -247,123 +239,22 @@ static gboolean
gtk_spinner_draw (GtkWidget *widget,
cairo_t *cr)
{
- GtkStateType state_type;
GtkSpinnerPrivate *priv;
+ GtkStyleContext *context;
+ GtkStateFlags state;
priv = GTK_SPINNER (widget)->priv;
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
- state_type = GTK_STATE_NORMAL;
- if (!gtk_widget_is_sensitive (widget))
- state_type = GTK_STATE_INSENSITIVE;
-
- gtk_paint_spinner (gtk_widget_get_style (widget),
- cr,
- state_type,
- widget,
- "spinner",
- priv->current,
- 0, 0,
- gtk_widget_get_allocated_width (widget),
- gtk_widget_get_allocated_height (widget));
+ gtk_style_context_set_state (context, state);
+ gtk_render_activity (context, cr, 0, 0,
+ gtk_widget_get_allocated_width (widget),
+ gtk_widget_get_allocated_height (widget));
return FALSE;
}
-static gboolean
-gtk_spinner_timeout (gpointer data)
-{
- GtkSpinnerPrivate *priv;
-
- priv = GTK_SPINNER (data)->priv;
-
- if (priv->current + 1 >= priv->num_steps)
- priv->current = 0;
- else
- priv->current++;
-
- gtk_widget_queue_draw (GTK_WIDGET (data));
-
- return TRUE;
-}
-
-static void
-gtk_spinner_add_timeout (GtkSpinner *spinner)
-{
- GtkSpinnerPrivate *priv;
-
- priv = spinner->priv;
-
- g_assert (priv->timeout == 0);
- priv->timeout = gdk_threads_add_timeout ((guint) priv->cycle_duration / priv->num_steps, gtk_spinner_timeout, spinner);
-}
-
-static void
-gtk_spinner_remove_timeout (GtkSpinner *spinner)
-{
- GtkSpinnerPrivate *priv;
-
- priv = spinner->priv;
-
- g_source_remove (priv->timeout);
- priv->timeout = 0;
-}
-
-static void
-gtk_spinner_map (GtkWidget *widget)
-{
- GtkSpinner *spinner = GTK_SPINNER (widget);
- GtkSpinnerPrivate *priv = spinner->priv;
-
- GTK_WIDGET_CLASS (gtk_spinner_parent_class)->map (widget);
-
- if (priv->active && priv->timeout == 0)
- gtk_spinner_add_timeout (spinner);
-}
-
-static void
-gtk_spinner_unmap (GtkWidget *widget)
-{
- GtkSpinner *spinner = GTK_SPINNER (widget);
- GtkSpinnerPrivate *priv = spinner->priv;
-
- if (priv->timeout != 0)
- gtk_spinner_remove_timeout (spinner);
-
- GTK_WIDGET_CLASS (gtk_spinner_parent_class)->unmap (widget);
-}
-
-static void
-gtk_spinner_style_set (GtkWidget *widget,
- GtkStyle *prev_style)
-{
- GtkSpinnerPrivate *priv;
-
- priv = GTK_SPINNER (widget)->priv;
-
- gtk_widget_style_get (GTK_WIDGET (widget),
- "num-steps", &(priv->num_steps),
- "cycle-duration", &(priv->cycle_duration),
- NULL);
-
- if (priv->current > priv->num_steps)
- priv->current = 0;
-}
-
-static void
-gtk_spinner_dispose (GObject *gobject)
-{
- GtkSpinnerPrivate *priv;
-
- priv = GTK_SPINNER (gobject)->priv;
-
- if (priv->timeout != 0)
- {
- gtk_spinner_remove_timeout (GTK_SPINNER (gobject));
- }
-
- G_OBJECT_CLASS (gtk_spinner_parent_class)->dispose (gobject);
-}
-
static void
gtk_spinner_set_active (GtkSpinner *spinner,
gboolean active)
@@ -378,16 +269,12 @@ gtk_spinner_set_active (GtkSpinner *spinner,
g_object_notify (G_OBJECT (spinner), "active");
- if (active &&
- gtk_widget_get_realized (GTK_WIDGET (spinner)) &&
- priv->timeout == 0)
- {
- gtk_spinner_add_timeout (spinner);
- }
- else if (!active && priv->timeout != 0)
- {
- gtk_spinner_remove_timeout (spinner);
- }
+ if (active)
+ gtk_widget_set_state_flags (GTK_WIDGET (spinner),
+ GTK_STATE_FLAG_ACTIVE, FALSE);
+ else
+ gtk_widget_unset_state_flags (GTK_WIDGET (spinner),
+ GTK_STATE_FLAG_ACTIVE);
}
}
diff --git a/gtk/gtkstatusicon.c b/gtk/gtkstatusicon.c
index 678cb8caab..f3bf6383b4 100644
--- a/gtk/gtkstatusicon.c
+++ b/gtk/gtkstatusicon.c
@@ -1701,8 +1701,16 @@ gtk_status_icon_color_changed (GtkTrayIcon *tray,
if (name)
{
+ GdkRGBA rgba;
+
g_object_get (priv->tray_icon, pspec->name, &color, NULL);
- gtk_widget_modify_symbolic_color (priv->image, name, &color);
+
+ rgba.red = color.red / 65535.;
+ rgba.green = color.green / 65535.;
+ rgba.blue = color.blue / 65535.;
+ rgba.alpha = 1;
+
+ gtk_widget_override_symbolic_color (priv->image, name, &rgba);
}
}
diff --git a/gtk/gtkstyle.c b/gtk/gtkstyle.c
index a5566e2984..ff7686a6ab 100644
--- a/gtk/gtkstyle.c
+++ b/gtk/gtkstyle.c
@@ -41,6 +41,7 @@
#include "gtkintl.h"
#include "gtkdebug.h"
#include "gtkspinner.h"
+#include "gtkborder.h"
/**
@@ -59,6 +60,10 @@
*
* Usually applications should not need to use or modify the #GtkStyle of their
* widgets.
+ *
+ * <warning>
+ * In GTK+ 3.0, GtkStyle has been deprecated and replaced by #GtkStyleContext.
+ * </warning>
*/
@@ -78,10 +83,27 @@ typedef struct _GtkStylePrivate GtkStylePrivate;
struct _GtkStylePrivate {
GSList *color_hashes;
+ GtkStyleContext *context;
+ gulong context_changed_id;
+};
+
+enum {
+ PROP_0,
+ PROP_CONTEXT
};
/* --- prototypes --- */
static void gtk_style_finalize (GObject *object);
+static void gtk_style_constructed (GObject *object);
+static void gtk_style_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_style_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
static void gtk_style_realize (GtkStyle *style,
GdkVisual *visual);
static void gtk_style_real_realize (GtkStyle *style);
@@ -314,6 +336,9 @@ static void hls_to_rgb (gdouble *h,
static void style_unrealize_cursors (GtkStyle *style);
+static void transform_detail_string (const gchar *detail,
+ GtkStyleContext *context);
+
/*
* Data for default check and radio buttons
*/
@@ -450,6 +475,9 @@ gtk_style_class_init (GtkStyleClass *klass)
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gtk_style_finalize;
+ object_class->set_property = gtk_style_set_property;
+ object_class->get_property = gtk_style_get_property;
+ object_class->constructed = gtk_style_constructed;
klass->clone = gtk_style_real_clone;
klass->copy = gtk_style_real_copy;
@@ -482,6 +510,14 @@ gtk_style_class_init (GtkStyleClass *klass)
g_type_class_add_private (object_class, sizeof (GtkStylePrivate));
+ g_object_class_install_property (object_class,
+ PROP_CONTEXT,
+ g_param_spec_object ("context",
+ P_("Style context"),
+ P_("GtkStyleContext to get style from"),
+ GTK_TYPE_STYLE_CONTEXT,
+ G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
+
/**
* GtkStyle::realize:
* @style: the object which received the signal
@@ -587,9 +623,199 @@ gtk_style_finalize (GObject *object)
if (style->rc_style)
g_object_unref (style->rc_style);
+ if (priv->context)
+ {
+ if (priv->context_changed_id)
+ g_signal_handler_disconnect (priv->context, priv->context_changed_id);
+
+ g_object_unref (priv->context);
+ }
+
G_OBJECT_CLASS (gtk_style_parent_class)->finalize (object);
}
+static void
+gtk_style_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkStylePrivate *priv;
+
+ priv = GTK_STYLE_GET_PRIVATE (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ priv->context = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_style_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkStylePrivate *priv;
+
+ priv = GTK_STYLE_GET_PRIVATE (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ g_value_set_object (value, priv->context);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_color (GtkStyle *style,
+ GtkStyleContext *context,
+ GtkStateType state,
+ GtkRcFlags prop)
+{
+ GtkStateFlags flags;
+ GdkRGBA *color = NULL;
+ GdkColor *dest = { 0 }; /* Shut up gcc */
+
+ switch (state)
+ {
+ case GTK_STATE_ACTIVE:
+ flags = GTK_STATE_FLAG_ACTIVE;
+ break;
+ case GTK_STATE_PRELIGHT:
+ flags = GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags = GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags = GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
+ flags = 0;
+ }
+
+ switch (prop)
+ {
+ case GTK_RC_BG:
+ gtk_style_context_get (context, flags,
+ "background-color", &color,
+ NULL);
+ dest = &style->bg[state];
+ break;
+ case GTK_RC_FG:
+ gtk_style_context_get (context, flags,
+ "color", &color,
+ NULL);
+ dest = &style->fg[state];
+ break;
+ case GTK_RC_TEXT:
+ gtk_style_context_get (context, flags,
+ "color", &color,
+ NULL);
+ dest = &style->text[state];
+ break;
+ case GTK_RC_BASE:
+ gtk_style_context_get (context, flags,
+ "background-color", &color,
+ NULL);
+ dest = &style->base[state];
+ break;
+ }
+
+ if (color)
+ {
+ dest->pixel = 0;
+ dest->red = CLAMP ((guint) (color->red * 65535), 0, 65535);
+ dest->green = CLAMP ((guint) (color->green * 65535), 0, 65535);
+ dest->blue = CLAMP ((guint) (color->blue * 65535), 0, 65535);
+ gdk_rgba_free (color);
+ }
+}
+
+static void
+gtk_style_update_from_context (GtkStyle *style)
+{
+ GtkStylePrivate *priv;
+ GtkStateType state;
+ GtkBorder *padding;
+
+ priv = GTK_STYLE_GET_PRIVATE (style);
+
+ for (state = GTK_STATE_NORMAL; state <= GTK_STATE_INSENSITIVE; state++)
+ {
+ if (gtk_style_context_has_class (priv->context, "entry"))
+ {
+ gtk_style_context_save (priv->context);
+ gtk_style_context_remove_class (priv->context, "entry");
+ set_color (style, priv->context, state, GTK_RC_BG);
+ set_color (style, priv->context, state, GTK_RC_FG);
+ gtk_style_context_restore (priv->context);
+
+ set_color (style, priv->context, state, GTK_RC_BASE);
+ set_color (style, priv->context, state, GTK_RC_TEXT);
+ }
+ else
+ {
+ gtk_style_context_save (priv->context);
+ gtk_style_context_add_class (priv->context, "entry");
+ set_color (style, priv->context, state, GTK_RC_BASE);
+ set_color (style, priv->context, state, GTK_RC_TEXT);
+ gtk_style_context_restore (priv->context);
+
+ set_color (style, priv->context, state, GTK_RC_BG);
+ set_color (style, priv->context, state, GTK_RC_FG);
+ }
+ }
+
+ if (style->font_desc)
+ pango_font_description_free (style->font_desc);
+
+ gtk_style_context_get (priv->context, 0,
+ "font", &style->font_desc,
+ "padding", &padding,
+ NULL);
+
+ if (padding)
+ {
+ style->xthickness = padding->left;
+ style->ythickness = padding->top;
+
+ gtk_border_free (padding);
+ }
+}
+
+static void
+style_context_changed (GtkStyleContext *context,
+ gpointer user_data)
+{
+ gtk_style_update_from_context (GTK_STYLE (user_data));
+}
+
+static void
+gtk_style_constructed (GObject *object)
+{
+ GtkStylePrivate *priv;
+
+ priv = GTK_STYLE_GET_PRIVATE (object);
+
+ if (priv->context)
+ {
+ gtk_style_update_from_context (GTK_STYLE (object));
+
+ priv->context_changed_id = g_signal_connect (priv->context, "changed",
+ G_CALLBACK (style_context_changed), object);
+ }
+}
/**
* gtk_style_copy:
@@ -598,6 +824,8 @@ gtk_style_finalize (GObject *object)
* Creates a copy of the passed in #GtkStyle object.
*
* Returns: (transfer full): a copy of @style
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
*/
GtkStyle*
gtk_style_copy (GtkStyle *style)
@@ -637,6 +865,8 @@ gtk_style_duplicate (GtkStyle *style)
* @returns: a new #GtkStyle.
*
* Creates a new #GtkStyle.
+ *
+ * Deprecated: 3.0: Use #GtkStyleContext
**/
GtkStyle*
gtk_style_new (void)
@@ -648,6 +878,16 @@ gtk_style_new (void)
return style;
}
+gboolean
+gtk_style_has_context (GtkStyle *style)
+{
+ GtkStylePrivate *priv;
+
+ priv = GTK_STYLE_GET_PRIVATE (style);
+
+ return priv->context != NULL;
+}
+
/**
* gtk_style_attach:
* @style: a #GtkStyle.
@@ -667,6 +907,8 @@ gtk_style_new (void)
* If the style is newly created, the style parameter
* will be unref'ed, and the new style will have
* a reference count belonging to the caller.
+ *
+ * Deprecated:3.0: Use gtk_widget_style_attach() instead
*/
GtkStyle*
gtk_style_attach (GtkStyle *style,
@@ -743,14 +985,17 @@ gtk_style_attach (GtkStyle *style,
*
* Detaches a style from a window. If the style is not attached
* to any windows anymore, it is unrealized. See gtk_style_attach().
- *
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
*/
void
gtk_style_detach (GtkStyle *style)
{
g_return_if_fail (GTK_IS_STYLE (style));
- g_return_if_fail (style->attach_count > 0);
-
+
+ if (style->attach_count == 0)
+ return;
+
style->attach_count -= 1;
if (style->attach_count == 0)
{
@@ -788,26 +1033,22 @@ gtk_style_realize (GtkStyle *style,
* otherwise %NULL.
*
* Return value: icon set of @stock_id
+ *
+ * Deprecated:3.0: Use gtk_style_context_lookup_icon_set() instead
*/
GtkIconSet*
gtk_style_lookup_icon_set (GtkStyle *style,
const char *stock_id)
{
- GSList *iter;
+ GtkStylePrivate *priv;
g_return_val_if_fail (GTK_IS_STYLE (style), NULL);
g_return_val_if_fail (stock_id != NULL, NULL);
-
- iter = style->icon_factories;
- while (iter != NULL)
- {
- GtkIconSet *icon_set = gtk_icon_factory_lookup (GTK_ICON_FACTORY (iter->data),
- stock_id);
- if (icon_set)
- return icon_set;
-
- iter = g_slist_next (iter);
- }
+
+ priv = GTK_STYLE_GET_PRIVATE (style);
+
+ if (priv->context)
+ return gtk_style_context_lookup_icon_set (priv->context, stock_id);
return gtk_icon_factory_lookup_default (stock_id);
}
@@ -827,6 +1068,8 @@ gtk_style_lookup_icon_set (GtkStyle *style,
* Return value: %TRUE if the mapping was found.
*
* Since: 2.10
+ *
+ * Deprecated:3.0: Use gtk_style_context_lookup_color() instead
**/
gboolean
gtk_style_lookup_color (GtkStyle *style,
@@ -834,7 +1077,8 @@ gtk_style_lookup_color (GtkStyle *style,
GdkColor *color)
{
GtkStylePrivate *priv;
- GSList *iter;
+ gboolean result;
+ GdkRGBA rgba;
g_return_val_if_fail (GTK_IS_STYLE (style), FALSE);
g_return_val_if_fail (color_name != NULL, FALSE);
@@ -842,21 +1086,20 @@ gtk_style_lookup_color (GtkStyle *style,
priv = GTK_STYLE_GET_PRIVATE (style);
- for (iter = priv->color_hashes; iter != NULL; iter = iter->next)
- {
- GHashTable *hash = iter->data;
- GdkColor *mapping = g_hash_table_lookup (hash, color_name);
+ if (!priv->context)
+ return FALSE;
- if (mapping)
- {
- color->red = mapping->red;
- color->green = mapping->green;
- color->blue = mapping->blue;
- return TRUE;
- }
+ result = gtk_style_context_lookup_color (priv->context, color_name, &rgba);
+
+ if (color)
+ {
+ color->red = (guint16) (rgba.red * 65535);
+ color->green = (guint16) (rgba.green * 65535);
+ color->blue = (guint16) (rgba.blue * 65535);
+ color->pixel = 0;
}
- return FALSE;
+ return result;
}
/**
@@ -867,6 +1110,8 @@ gtk_style_lookup_color (GtkStyle *style,
*
* Sets the background of @window to the background color or pixmap
* specified by @style for the given state.
+ *
+ * Deprecated:3.0: Use gtk_style_context_set_background() instead
*/
void
gtk_style_set_background (GtkStyle *style,
@@ -883,7 +1128,13 @@ gtk_style_set_background (GtkStyle *style,
static GtkStyle *
gtk_style_real_clone (GtkStyle *style)
{
- return g_object_new (G_OBJECT_TYPE (style), NULL);
+ GtkStylePrivate *priv;
+
+ priv = GTK_STYLE_GET_PRIVATE (style);
+
+ return g_object_new (G_OBJECT_TYPE (style),
+ "context", priv->context,
+ NULL);
}
static void
@@ -1349,6 +1600,8 @@ gtk_style_real_set_background (GtkStyle *style,
*
* Return value: (transfer full): a newly-created #GdkPixbuf
* containing the rendered icon
+ *
+ * Deprecated:3.0: Use gtk_render_icon_pixbuf() instead
*/
GdkPixbuf *
gtk_style_render_icon (GtkStyle *style,
@@ -1386,6 +1639,8 @@ gtk_style_render_icon (GtkStyle *style,
* @y:
* @width:
* @height:
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
*/
void
gtk_style_apply_default_background (GtkStyle *style,
@@ -1428,54 +1683,6 @@ out:
}
static GdkPixbuf *
-scale_or_ref (GdkPixbuf *src,
- gint width,
- gint height)
-{
- if (width == gdk_pixbuf_get_width (src) &&
- height == gdk_pixbuf_get_height (src))
- {
- return g_object_ref (src);
- }
- else
- {
- return gdk_pixbuf_scale_simple (src,
- width, height,
- GDK_INTERP_BILINEAR);
- }
-}
-
-static gboolean
-lookup_icon_size (GtkStyle *style,
- GtkWidget *widget,
- GtkIconSize size,
- gint *width,
- gint *height)
-{
- GdkScreen *screen;
- GtkSettings *settings;
-
- if (widget && gtk_widget_has_screen (widget))
- {
- screen = gtk_widget_get_screen (widget);
- settings = gtk_settings_get_for_screen (screen);
- }
- else if (style && style->visual)
- {
- screen = gdk_visual_get_screen (style->visual);
- settings = gtk_settings_get_for_screen (screen);
- }
- else
- {
- settings = gtk_settings_get_default ();
- GTK_NOTE (MULTIHEAD,
- g_warning ("Using the default screen for gtk_default_render_icon()"));
- }
-
- return gtk_icon_size_lookup_for_settings (settings, size, width, height);
-}
-
-static GdkPixbuf *
gtk_default_render_icon (GtkStyle *style,
const GtkIconSource *source,
GtkTextDirection direction,
@@ -1484,65 +1691,46 @@ gtk_default_render_icon (GtkStyle *style,
GtkWidget *widget,
const gchar *detail)
{
- gint width = 1;
- gint height = 1;
- GdkPixbuf *scaled;
- GdkPixbuf *stated;
- GdkPixbuf *base_pixbuf;
-
- /* Oddly, style can be NULL in this function, because
- * GtkIconSet can be used without a style and if so
- * it uses this function.
- */
-
- base_pixbuf = gtk_icon_source_get_pixbuf (source);
-
- g_return_val_if_fail (base_pixbuf != NULL, NULL);
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
+ GdkPixbuf *pixbuf;
- if (size != (GtkIconSize) -1 && !lookup_icon_size(style, widget, size, &width, &height))
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- g_warning (G_STRLOC ": invalid icon size '%d'", size);
- return NULL;
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
- /* If the size was wildcarded, and we're allowed to scale, then scale; otherwise,
- * leave it alone.
- */
- if (size != (GtkIconSize)-1 && gtk_icon_source_get_size_wildcarded (source))
- scaled = scale_or_ref (base_pixbuf, width, height);
- else
- scaled = g_object_ref (base_pixbuf);
+ if (!context)
+ return NULL;
+
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
- /* If the state was wildcarded, then generate a state. */
- if (gtk_icon_source_get_state_wildcarded (source))
+ switch (state)
{
- if (state == GTK_STATE_INSENSITIVE)
- {
- stated = gdk_pixbuf_copy (scaled);
-
- gdk_pixbuf_saturate_and_pixelate (scaled, stated,
- 0.8, TRUE);
-
- g_object_unref (scaled);
- }
- else if (state == GTK_STATE_PRELIGHT)
- {
- stated = gdk_pixbuf_copy (scaled);
-
- gdk_pixbuf_saturate_and_pixelate (scaled, stated,
- 1.2, FALSE);
-
- g_object_unref (scaled);
- }
- else
- {
- stated = scaled;
- }
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
+ break;
}
- else
- stated = scaled;
-
- return stated;
+
+ gtk_style_context_set_state (context, flags);
+
+ pixbuf = gtk_render_icon_pixbuf (context, source, size);
+
+ gtk_style_context_restore (context);
+
+ return pixbuf;
}
static void
@@ -1600,7 +1788,168 @@ _cairo_draw_point (cairo_t *cr,
}
static void
-gtk_default_draw_hline (GtkStyle *style,
+transform_detail_string (const gchar *detail,
+ GtkStyleContext *context)
+{
+ if (!detail)
+ return;
+
+ if (strcmp (detail, "arrow") == 0)
+ gtk_style_context_add_class (context, "arrow");
+ else if (strcmp (detail, "button") == 0)
+ gtk_style_context_add_class (context, "button");
+ else if (strcmp (detail, "buttondefault") == 0)
+ {
+ gtk_style_context_add_class (context, "button");
+ gtk_style_context_add_class (context, "default");
+ }
+ else if (strcmp (detail, "calendar") == 0)
+ gtk_style_context_add_class (context, "calendar");
+ else if (strcmp (detail, "cellcheck") == 0)
+ {
+ gtk_style_context_add_class (context, "cell");
+ gtk_style_context_add_class (context, "check");
+ }
+ else if (strcmp (detail, "cellradio") == 0)
+ {
+ gtk_style_context_add_class (context, "cell");
+ gtk_style_context_add_class (context, "radio");
+ }
+ else if (strcmp (detail, "checkbutton") == 0)
+ gtk_style_context_add_class (context, "check");
+ else if (strcmp (detail, "check") == 0)
+ {
+ gtk_style_context_add_class (context, "check");
+ gtk_style_context_add_class (context, "menu");
+ }
+ else if (strcmp (detail, "radiobutton") == 0)
+ {
+ gtk_style_context_add_class (context, "radio");
+ }
+ else if (strcmp (detail, "option") == 0)
+ {
+ gtk_style_context_add_class (context, "radio");
+ gtk_style_context_add_class (context, "menu");
+ }
+ else if (strcmp (detail, "entry") == 0 ||
+ strcmp (detail, "entry_bg") == 0)
+ gtk_style_context_add_class (context, "entry");
+ else if (strcmp (detail, "expander") == 0)
+ gtk_style_context_add_class (context, "expander");
+ else if (strcmp (detail, "tooltip") == 0)
+ gtk_style_context_add_class (context, "tooltip");
+ else if (strcmp (detail, "frame") == 0)
+ gtk_style_context_add_class (context, "frame");
+ else if (strcmp (detail, "scrolled_window") == 0)
+ gtk_style_context_add_class (context, "scrolled-window");
+ else if (strcmp (detail, "viewport") == 0 ||
+ strcmp (detail, "viewportbin") == 0)
+ gtk_style_context_add_class (context, "viewport");
+ else if (strncmp (detail, "trough", 6) == 0)
+ gtk_style_context_add_class (context, "trough");
+ else if (strcmp (detail, "spinbutton") == 0)
+ gtk_style_context_add_class (context, "spinbutton");
+ else if (strcmp (detail, "spinbutton_up") == 0)
+ {
+ gtk_style_context_add_class (context, "spinbutton");
+ gtk_style_context_add_class (context, "button");
+ gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
+ }
+ else if (strcmp (detail, "spinbutton_down") == 0)
+ {
+ gtk_style_context_add_class (context, "spinbutton");
+ gtk_style_context_add_class (context, "button");
+ gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
+ }
+ else if ((detail[0] == 'h' || detail[0] == 'v') &&
+ strncmp (&detail[1], "scrollbar_", 10) == 0)
+ {
+ gtk_style_context_add_class (context, "button");
+ gtk_style_context_add_class (context, "scrollbar");
+ }
+ else if (strcmp (detail, "slider") == 0)
+ {
+ gtk_style_context_add_class (context, "slider");
+ gtk_style_context_add_class (context, "scrollbar");
+ }
+ else if (strcmp (detail, "vscale") == 0 ||
+ strcmp (detail, "hscale") == 0)
+ {
+ gtk_style_context_add_class (context, "slider");
+ gtk_style_context_add_class (context, "scale");
+ }
+ else if (strcmp (detail, "menuitem") == 0)
+ {
+ gtk_style_context_add_class (context, "menuitem");
+ gtk_style_context_add_class (context, "menu");
+ }
+ else if (strcmp (detail, "menu") == 0)
+ {
+ gtk_style_context_add_class (context, "popup");
+ gtk_style_context_add_class (context, "menu");
+ }
+ else if (strcmp (detail, "accellabel") == 0)
+ gtk_style_context_add_class (context, "accelerator");
+ else if (strcmp (detail, "menubar") == 0)
+ gtk_style_context_add_class (context, "menubar");
+ else if (strcmp (detail, "base") == 0)
+ gtk_style_context_add_class (context, "background");
+ else if (strcmp (detail, "bar") == 0 ||
+ strcmp (detail, "progressbar") == 0)
+ gtk_style_context_add_class (context, "progressbar");
+ else if (strcmp (detail, "toolbar") == 0)
+ gtk_style_context_add_class (context, "toolbar");
+ else if (strcmp (detail, "handlebox_bin") == 0)
+ gtk_style_context_add_class (context, "dock");
+ else if (strcmp (detail, "notebook") == 0)
+ gtk_style_context_add_class (context, "notebook");
+ else if (strcmp (detail, "tab") == 0)
+ {
+ gtk_style_context_add_class (context, "notebook");
+ gtk_style_context_add_region (context, GTK_STYLE_REGION_TAB, 0);
+ }
+ else if (g_str_has_prefix (detail, "cell"))
+ {
+ GtkRegionFlags row, col;
+ gboolean ruled = FALSE;
+ GStrv tokens;
+ guint i;
+
+ tokens = g_strsplit (detail, "_", -1);
+ row = col = 0;
+ i = 0;
+
+ while (tokens[i])
+ {
+ if (strcmp (tokens[i], "even") == 0)
+ row |= GTK_REGION_EVEN;
+ else if (strcmp (tokens[i], "odd") == 0)
+ row |= GTK_REGION_ODD;
+ else if (strcmp (tokens[i], "start") == 0)
+ col |= GTK_REGION_FIRST;
+ else if (strcmp (tokens[i], "end") == 0)
+ col |= GTK_REGION_LAST;
+ else if (strcmp (tokens[i], "ruled") == 0)
+ ruled = TRUE;
+ else if (strcmp (tokens[i], "sorted") == 0)
+ col |= GTK_REGION_SORTED;
+
+ i++;
+ }
+
+ if (!ruled)
+ row &= ~(GTK_REGION_EVEN | GTK_REGION_ODD);
+
+ gtk_style_context_add_class (context, "cell");
+ gtk_style_context_add_region (context, "row", row);
+ gtk_style_context_add_region (context, "column", col);
+
+ g_strfreev (tokens);
+ }
+}
+
+static void
+gtk_default_draw_hline (GtkStyle *style,
cairo_t *cr,
GtkStateType state_type,
GtkWidget *widget,
@@ -1609,36 +1958,30 @@ gtk_default_draw_hline (GtkStyle *style,
gint x2,
gint y)
{
- gint thickness_light;
- gint thickness_dark;
- gint i;
-
- thickness_light = style->ythickness / 2;
- thickness_dark = style->ythickness - thickness_light;
-
- cairo_set_line_width (cr, 1.0);
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
- if (detail && !strcmp (detail, "label"))
- {
- if (state_type == GTK_STATE_INSENSITIVE)
- _cairo_draw_line (cr, &style->white, x1 + 1, y + 1, x2 + 1, y + 1);
- _cairo_draw_line (cr, &style->fg[state_type], x1, y, x2, y);
- }
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
else
{
- for (i = 0; i < thickness_dark; i++)
- {
- _cairo_draw_line (cr, &style->dark[state_type], x1, y + i, x2 - i - 1, y + i);
- _cairo_draw_line (cr, &style->light[state_type], x2 - i, y + i, x2, y + i);
- }
-
- y += thickness_dark;
- for (i = 0; i < thickness_light; i++)
- {
- _cairo_draw_line (cr, &style->dark[state_type], x1, y + i, x1 + thickness_light - i - 1, y + i);
- _cairo_draw_line (cr, &style->light[state_type], x1 + thickness_light - i, y + i, x2, y + i);
- }
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
+
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
+
+ cairo_save (cr);
+
+ gtk_render_line (context, cr,
+ x1, y, x2, y);
+
+ cairo_restore (cr);
+
+ gtk_style_context_restore (context);
}
@@ -1652,31 +1995,29 @@ gtk_default_draw_vline (GtkStyle *style,
gint y2,
gint x)
{
- gint thickness_light;
- gint thickness_dark;
- gint i;
-
- thickness_light = style->xthickness / 2;
- thickness_dark = style->xthickness - thickness_light;
-
- cairo_set_line_width (cr, 1.0);
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
- for (i = 0; i < thickness_dark; i++)
- {
- _cairo_draw_line (cr, &style->dark[state_type],
- x + i, y1, x + i, y2 - i - 1);
- _cairo_draw_line (cr, &style->light[state_type],
- x + i, y2 - i, x + i, y2);
- }
-
- x += thickness_dark;
- for (i = 0; i < thickness_light; i++)
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- _cairo_draw_line (cr, &style->dark[state_type],
- x + i, y1, x + i, y1 + thickness_light - i - 1);
- _cairo_draw_line (cr, &style->light[state_type],
- x + i, y1 + thickness_light - i, x + i, y2);
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
+
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
+
+ cairo_save (cr);
+
+ gtk_render_line (context, cr,
+ x, y1, x, y2);
+
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -1838,299 +2179,32 @@ gtk_default_draw_shadow (GtkStyle *style,
gint width,
gint height)
{
- GdkColor *gc1 = NULL;
- GdkColor *gc2 = NULL;
- gint thickness_light;
- gint thickness_dark;
- gint i;
-
- cairo_set_line_width (cr, 1.0);
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
- if (shadow_type == GTK_SHADOW_IN)
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- if (detail && strcmp (detail, "buttondefault") == 0)
- {
- _cairo_draw_rectangle (cr, &style->black, FALSE,
- x, y, width - 1, height - 1);
-
- return;
- }
- if (detail && strcmp (detail, "trough") == 0)
- {
- draw_thin_shadow (style, cr, state_type,
- x, y, width, height);
-
- return;
- }
- if (GTK_IS_SPIN_BUTTON (widget) &&
- detail && strcmp (detail, "spinbutton") == 0)
- {
- draw_spinbutton_shadow (style, cr, state_type,
- get_direction (widget), x, y, width, height);
-
- return;
- }
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
- if (shadow_type == GTK_SHADOW_OUT && detail && strcmp (detail, "menu") == 0)
- {
- draw_menu_shadow (style, cr, state_type, x, y, width, height);
- return;
- }
-
- switch (shadow_type)
- {
- case GTK_SHADOW_NONE:
- return;
- case GTK_SHADOW_IN:
- case GTK_SHADOW_ETCHED_IN:
- gc1 = &style->light[state_type];
- gc2 = &style->dark[state_type];
- break;
- case GTK_SHADOW_OUT:
- case GTK_SHADOW_ETCHED_OUT:
- gc1 = &style->dark[state_type];
- gc2 = &style->light[state_type];
- break;
- }
-
- switch (shadow_type)
- {
- case GTK_SHADOW_NONE:
- break;
-
- case GTK_SHADOW_IN:
- /* Light around right and bottom edge */
-
- if (style->ythickness > 0)
- _cairo_draw_line (cr, gc1,
- x, y + height - 1, x + width - 1, y + height - 1);
- if (style->xthickness > 0)
- _cairo_draw_line (cr, gc1,
- x + width - 1, y, x + width - 1, y + height - 1);
-
- if (style->ythickness > 1)
- _cairo_draw_line (cr, &style->bg[state_type],
- x + 1, y + height - 2, x + width - 2, y + height - 2);
- if (style->xthickness > 1)
- _cairo_draw_line (cr, &style->bg[state_type],
- x + width - 2, y + 1, x + width - 2, y + height - 2);
-
- /* Dark around left and top */
-
- if (style->ythickness > 1)
- _cairo_draw_line (cr, &style->black,
- x + 1, y + 1, x + width - 2, y + 1);
- if (style->xthickness > 1)
- _cairo_draw_line (cr, &style->black,
- x + 1, y + 1, x + 1, y + height - 2);
-
- if (style->ythickness > 0)
- _cairo_draw_line (cr, gc2,
- x, y, x + width - 1, y);
- if (style->xthickness > 0)
- _cairo_draw_line (cr, gc2,
- x, y, x, y + height - 1);
- break;
-
- case GTK_SHADOW_OUT:
- /* Dark around right and bottom edge */
-
- if (style->ythickness > 0)
- {
- if (style->ythickness > 1)
- {
- _cairo_draw_line (cr, gc1,
- x + 1, y + height - 2, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &style->black,
- x, y + height - 1, x + width - 1, y + height - 1);
- }
- else
- {
- _cairo_draw_line (cr, gc1,
- x + 1, y + height - 1, x + width - 1, y + height - 1);
- }
- }
+ gtk_style_context_save (context);
- if (style->xthickness > 0)
- {
- if (style->xthickness > 1)
- {
- _cairo_draw_line (cr, gc1,
- x + width - 2, y + 1, x + width - 2, y + height - 2);
-
- _cairo_draw_line (cr, &style->black,
- x + width - 1, y, x + width - 1, y + height - 1);
- }
- else
- {
- _cairo_draw_line (cr, gc1,
- x + width - 1, y + 1, x + width - 1, y + height - 1);
- }
- }
-
- /* Light around top and left */
-
- if (style->ythickness > 0)
- _cairo_draw_line (cr, gc2,
- x, y, x + width - 2, y);
- if (style->xthickness > 0)
- _cairo_draw_line (cr, gc2,
- x, y, x, y + height - 2);
+ if (detail)
+ transform_detail_string (detail, context);
- if (style->ythickness > 1)
- _cairo_draw_line (cr, &style->bg[state_type],
- x + 1, y + 1, x + width - 3, y + 1);
- if (style->xthickness > 1)
- _cairo_draw_line (cr, &style->bg[state_type],
- x + 1, y + 1, x + 1, y + height - 3);
- break;
-
- case GTK_SHADOW_ETCHED_IN:
- case GTK_SHADOW_ETCHED_OUT:
- if (style->xthickness > 0)
- {
- if (style->xthickness > 1)
- {
- thickness_light = 1;
- thickness_dark = 1;
-
- for (i = 0; i < thickness_dark; i++)
- {
- _cairo_draw_line (cr, gc1,
- x + width - i - 1,
- y + i,
- x + width - i - 1,
- y + height - i - 1);
- _cairo_draw_line (cr, gc2,
- x + i,
- y + i,
- x + i,
- y + height - i - 2);
- }
-
- for (i = 0; i < thickness_light; i++)
- {
- _cairo_draw_line (cr, gc1,
- x + thickness_dark + i,
- y + thickness_dark + i,
- x + thickness_dark + i,
- y + height - thickness_dark - i - 1);
- _cairo_draw_line (cr, gc2,
- x + width - thickness_light - i - 1,
- y + thickness_dark + i,
- x + width - thickness_light - i - 1,
- y + height - thickness_light - 1);
- }
- }
- else
- {
- _cairo_draw_line (cr,
- &style->dark[state_type],
- x, y, x, y + height);
- _cairo_draw_line (cr,
- &style->dark[state_type],
- x + width, y, x + width, y + height);
- }
- }
+ cairo_save (cr);
- if (style->ythickness > 0)
- {
- if (style->ythickness > 1)
- {
- thickness_light = 1;
- thickness_dark = 1;
-
- for (i = 0; i < thickness_dark; i++)
- {
- _cairo_draw_line (cr, gc1,
- x + i,
- y + height - i - 1,
- x + width - i - 1,
- y + height - i - 1);
-
- _cairo_draw_line (cr, gc2,
- x + i,
- y + i,
- x + width - i - 2,
- y + i);
- }
-
- for (i = 0; i < thickness_light; i++)
- {
- _cairo_draw_line (cr, gc1,
- x + thickness_dark + i,
- y + thickness_dark + i,
- x + width - thickness_dark - i - 2,
- y + thickness_dark + i);
-
- _cairo_draw_line (cr, gc2,
- x + thickness_dark + i,
- y + height - thickness_light - i - 1,
- x + width - thickness_light - 1,
- y + height - thickness_light - i - 1);
- }
- }
- else
- {
- _cairo_draw_line (cr,
- &style->dark[state_type],
- x, y, x + width, y);
- _cairo_draw_line (cr,
- &style->dark[state_type],
- x, y + height, x + width, y + height);
- }
- }
-
- break;
- }
+ gtk_render_frame (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ (gdouble) width,
+ (gdouble) height);
- if (shadow_type == GTK_SHADOW_IN &&
- GTK_IS_SPIN_BUTTON (widget) &&
- detail && strcmp (detail, "entry") == 0)
- {
- if (get_direction (widget) == GTK_TEXT_DIR_LTR)
- {
- _cairo_draw_line (cr,
- &style->base[state_type],
- x + width - 1, y + 2,
- x + width - 1, y + height - 3);
- _cairo_draw_line (cr,
- &style->base[state_type],
- x + width - 2, y + 2,
- x + width - 2, y + height - 3);
- /* draw point */
- _cairo_draw_point (cr,
- &style->black,
- x + width - 1, y + 1);
- _cairo_draw_point (cr,
- &style->bg[state_type],
- x + width - 1, y + height - 2);
- }
- else
- {
- _cairo_draw_line (cr,
- &style->base[state_type],
- x, y + 2,
- x, y + height - 3);
- _cairo_draw_line (cr,
- &style->base[state_type],
- x + 1, y + 2,
- x + 1, y + height - 3);
-
- _cairo_draw_point (cr,
- &style->black,
- x, y + 1);
-
- _cairo_draw_line (cr,
- &style->bg[state_type],
- x, y + height - 2,
- x + 1, y + height - 2);
- _cairo_draw_point (cr,
- &style->light[state_type],
- x, y + height - 1);
- }
- }
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -2259,16 +2333,79 @@ gtk_default_draw_arrow (GtkStyle *style,
gint width,
gint height)
{
- calculate_arrow_geometry (arrow_type, &x, &y, &width, &height);
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
+ gdouble angle, size;
+
+ if (arrow_type == GTK_ARROW_NONE)
+ return;
+
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
+ {
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
+ }
+
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
+
+ switch (arrow_type)
+ {
+ case GTK_ARROW_UP:
+ angle = 0;
+ size = width;
+ break;
+ case GTK_ARROW_RIGHT:
+ angle = G_PI / 2;
+ size = height;
+ break;
+ case GTK_ARROW_DOWN:
+ angle = G_PI;
+ size = width;
+ break;
+ case GTK_ARROW_LEFT:
+ angle = 3 * (G_PI / 2);
+ size = height;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ switch (state)
+ {
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ case GTK_STATE_ACTIVE:
+ flags |= GTK_STATE_FLAG_ACTIVE;
+ break;
+ default:
+ break;
+ }
- if (detail && strcmp (detail, "menu_scroll_arrow_up") == 0)
- y++;
+ gtk_style_context_set_state (context, flags);
- if (state == GTK_STATE_INSENSITIVE)
- draw_arrow (cr, &style->white, arrow_type,
- x + 1, y + 1, width, height);
- draw_arrow (cr, &style->fg[state], arrow_type,
- x, y, width, height);
+ cairo_save (cr);
+
+ gtk_render_arrow (context,
+ cr, angle,
+ (gdouble) x,
+ (gdouble) y,
+ size);
+
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -2433,94 +2570,58 @@ gtk_default_draw_box (GtkStyle *style,
gint width,
gint height)
{
- gboolean is_spinbutton_box = FALSE;
-
- if (GTK_IS_SPIN_BUTTON (widget) && detail)
- {
- if (strcmp (detail, "spinbutton_up") == 0)
- {
- y += 2;
- width -= 3;
- height -= 2;
-
- if (get_direction (widget) == GTK_TEXT_DIR_RTL)
- x += 2;
- else
- x += 1;
-
- is_spinbutton_box = TRUE;
- }
- else if (strcmp (detail, "spinbutton_down") == 0)
- {
- width -= 3;
- height -= 2;
-
- if (get_direction (widget) == GTK_TEXT_DIR_RTL)
- x += 2;
- else
- x += 1;
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
- is_spinbutton_box = TRUE;
- }
- }
-
- if (background_is_solid (style, state_type))
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- GdkColor *gc = &style->bg[state_type];
-
- if (state_type == GTK_STATE_SELECTED && detail && strcmp (detail, "paned") == 0)
- {
- if (widget && !gtk_widget_has_focus (widget))
- gc = &style->base[GTK_STATE_ACTIVE];
- }
-
- _cairo_draw_rectangle (cr, gc, TRUE,
- x, y, width, height);
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
- else
- gtk_style_apply_default_background (style, cr, gtk_widget_get_window (widget),
- state_type, x, y, width, height);
+ gtk_style_context_save (context);
- if (is_spinbutton_box)
- {
- GdkColor *upper;
- GdkColor *lower;
+ if (detail)
+ transform_detail_string (detail, context);
- lower = &style->dark[state_type];
- if (shadow_type == GTK_SHADOW_OUT)
- upper = &style->light[state_type];
- else
- upper = &style->dark[state_type];
+ switch (state_type)
+ {
+ case GTK_STATE_ACTIVE:
+ flags |= GTK_STATE_FLAG_ACTIVE;
+ break;
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
+ break;
+ }
- _cairo_draw_line (cr, upper, x, y, x + width - 1, y);
- _cairo_draw_line (cr, lower, x, y + height - 1, x + width - 1, y + height - 1);
+ if (shadow_type == GTK_SHADOW_IN)
+ flags |= GTK_STATE_FLAG_ACTIVE;
- return;
- }
+ gtk_style_context_set_state (context, flags);
- gtk_paint_shadow (style, cr, state_type, shadow_type, widget, detail,
- x, y, width, height);
+ cairo_save (cr);
- if (detail && strcmp (detail, "optionmenu") == 0)
+ if (gtk_style_context_has_class (context, GTK_STYLE_CLASS_PROGRESSBAR))
+ gtk_render_activity (context, cr, x, y, width, height);
+ else
{
- GtkRequisition indicator_size;
- GtkBorder indicator_spacing;
- gint vline_x;
-
- option_menu_get_props (widget, &indicator_size, &indicator_spacing);
-
- if (get_direction (widget) == GTK_TEXT_DIR_RTL)
- vline_x = x + indicator_size.width + indicator_spacing.left + indicator_spacing.right;
- else
- vline_x = x + width - (indicator_size.width + indicator_spacing.left + indicator_spacing.right) - style->xthickness;
-
- gtk_paint_vline (style, cr, state_type, widget,
- detail,
- y + style->ythickness + 1,
- y + height - style->ythickness - 3,
- vline_x);
+ gtk_render_background (context, cr, x, y, width, height);
+ gtk_render_frame (context, cr, x, y, width, height);
}
+
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static GdkColor *
@@ -2552,198 +2653,56 @@ gtk_default_draw_flat_box (GtkStyle *style,
gint width,
gint height)
{
- GdkColor *gc1;
- GdkColor *freeme = NULL;
-
- cairo_set_line_width (cr, 1.0);
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
- if (detail)
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- int trimmed_len = strlen (detail);
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
+ }
- if (g_str_has_prefix (detail, "cell_"))
- {
- if (g_str_has_suffix (detail, "_start"))
- trimmed_len -= 6;
- else if (g_str_has_suffix (detail, "_middle"))
- trimmed_len -= 7;
- else if (g_str_has_suffix (detail, "_end"))
- trimmed_len -= 4;
- }
+ gtk_style_context_save (context);
- if (state_type == GTK_STATE_SELECTED)
- {
- if (!strcmp ("text", detail))
- gc1 = &style->bg[GTK_STATE_SELECTED];
- else if (!strncmp ("cell_even", detail, trimmed_len) ||
- !strncmp ("cell_odd", detail, trimmed_len) ||
- !strncmp ("cell_even_ruled", detail, trimmed_len) ||
- !strncmp ("cell_even_ruled_sorted", detail, trimmed_len))
- {
- /* This has to be really broken; alex made me do it. -jrb */
- if (widget && gtk_widget_has_focus (widget))
- gc1 = &style->base[state_type];
- else
- gc1 = &style->base[GTK_STATE_ACTIVE];
- }
- else if (!strncmp ("cell_odd_ruled", detail, trimmed_len) ||
- !strncmp ("cell_odd_ruled_sorted", detail, trimmed_len))
- {
- if (widget && gtk_widget_has_focus (widget))
- freeme = get_darkened (&style->base[state_type], 1);
- else
- freeme = get_darkened (&style->base[GTK_STATE_ACTIVE], 1);
- gc1 = freeme;
- }
- else
- {
- gc1 = &style->bg[state_type];
- }
- }
- else
- {
- if (!strcmp ("viewportbin", detail))
- gc1 = &style->bg[GTK_STATE_NORMAL];
- else if (!strcmp ("entry_bg", detail))
- gc1 = &style->base[gtk_widget_get_state (widget)];
-
- /* For trees: even rows are base color, odd rows are a shade of
- * the base color, the sort column is a shade of the original color
- * for that row.
- */
-
- else if (!strncmp ("cell_even", detail, trimmed_len) ||
- !strncmp ("cell_odd", detail, trimmed_len) ||
- !strncmp ("cell_even_ruled", detail, trimmed_len))
- {
- GdkColor *color = NULL;
+ if (detail)
+ transform_detail_string (detail, context);
- gtk_widget_style_get (widget,
- "even-row-color", &color,
- NULL);
+ switch (state_type)
+ {
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ case GTK_STATE_ACTIVE:
+ flags |= GTK_STATE_FLAG_ACTIVE;
+ break;
+ case GTK_STATE_FOCUSED:
+ flags |= GTK_STATE_FLAG_FOCUSED;
+ break;
+ default:
+ break;
+ }
- if (color)
- {
- freeme = get_darkened (color, 0);
- gc1 = freeme;
+ gtk_style_context_set_state (context, flags);
- gdk_color_free (color);
- }
- else
- gc1 = &style->base[state_type];
- }
- else if (!strncmp ("cell_odd_ruled", detail, trimmed_len))
- {
- GdkColor *color = NULL;
-
- gtk_widget_style_get (widget,
- "odd-row-color", &color,
- NULL);
-
- if (color)
- {
- freeme = get_darkened (color, 0);
- gc1 = freeme;
-
- gdk_color_free (color);
- }
- else
- {
- gtk_widget_style_get (widget,
- "even-row-color", &color,
- NULL);
-
- if (color)
- {
- freeme = get_darkened (color, 1);
- gdk_color_free (color);
- }
- else
- freeme = get_darkened (&style->base[state_type], 1);
- gc1 = freeme;
- }
- }
- else if (!strncmp ("cell_even_sorted", detail, trimmed_len) ||
- !strncmp ("cell_odd_sorted", detail, trimmed_len) ||
- !strncmp ("cell_even_ruled_sorted", detail, trimmed_len))
- {
- GdkColor *color = NULL;
-
- if (!strncmp ("cell_odd_sorted", detail, trimmed_len))
- gtk_widget_style_get (widget,
- "odd-row-color", &color,
- NULL);
- else
- gtk_widget_style_get (widget,
- "even-row-color", &color,
- NULL);
-
- if (color)
- {
- freeme = get_darkened (color, 1);
- gc1 = freeme;
-
- gdk_color_free (color);
- }
- else
- {
- freeme = get_darkened (&style->base[state_type], 1);
- gc1 = freeme;
- }
- }
- else if (!strncmp ("cell_odd_ruled_sorted", detail, trimmed_len))
- {
- GdkColor *color = NULL;
-
- gtk_widget_style_get (widget,
- "odd-row-color", &color,
- NULL);
-
- if (color)
- {
- freeme = get_darkened (color, 1);
- gc1 = freeme;
-
- gdk_color_free (color);
- }
- else
- {
- gtk_widget_style_get (widget,
- "even-row-color", &color,
- NULL);
-
- if (color)
- {
- freeme = get_darkened (color, 2);
- gdk_color_free (color);
- }
- else
- freeme = get_darkened (&style->base[state_type], 2);
- gc1 = freeme;
- }
- }
- else
- gc1 = &style->bg[state_type];
- }
- }
- else
- gc1 = &style->bg[state_type];
-
- if (background_is_solid (style, state_type) || gc1 != &style->bg[state_type])
- {
- _cairo_draw_rectangle (cr, gc1, TRUE,
- x, y, width, height);
+ cairo_save (cr);
- if (detail && !strcmp ("tooltip", detail))
- _cairo_draw_rectangle (cr, &style->black, FALSE,
- x, y, width - 1, height - 1);
- }
- else
- gtk_style_apply_default_background (style, cr, gtk_widget_get_window (widget),
- state_type, x, y, width, height);
+ gtk_render_background (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ (gdouble) width,
+ (gdouble) height);
- if (freeme)
- gdk_color_free (freeme);
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -2758,107 +2717,53 @@ gtk_default_draw_check (GtkStyle *style,
gint width,
gint height)
{
- enum { BUTTON, MENU, CELL } type = BUTTON;
- int exterior_size;
- int interior_size;
- int pad;
-
- if (detail)
- {
- if (strcmp (detail, "cellcheck") == 0)
- type = CELL;
- else if (strcmp (detail, "check") == 0)
- type = MENU;
- }
-
- exterior_size = MIN (width, height);
- if (exterior_size % 2 == 0) /* Ensure odd */
- exterior_size -= 1;
-
- pad = style->xthickness + MAX (1, (exterior_size - 2 * style->xthickness) / 9);
- interior_size = MAX (1, exterior_size - 2 * pad);
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
- if (interior_size < 7)
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- interior_size = 7;
- pad = MAX (0, (exterior_size - interior_size) / 2);
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
- x -= (1 + exterior_size - width) / 2;
- y -= (1 + exterior_size - height) / 2;
+ gtk_style_context_save (context);
- switch (type)
- {
- case BUTTON:
- case CELL:
- if (type == BUTTON)
- gdk_cairo_set_source_color (cr, &style->fg[state_type]);
- else
- gdk_cairo_set_source_color (cr, &style->text[state_type]);
-
- cairo_set_line_width (cr, 1.0);
- cairo_rectangle (cr, x + 0.5, y + 0.5, exterior_size - 1, exterior_size - 1);
- cairo_stroke (cr);
+ if (detail)
+ transform_detail_string (detail, context);
- gdk_cairo_set_source_color (cr, &style->base[state_type]);
- cairo_rectangle (cr, x + 1, y + 1, exterior_size - 2, exterior_size - 2);
- cairo_fill (cr);
+ switch (state_type)
+ {
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
break;
-
- case MENU:
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
break;
- }
-
- switch (type)
- {
- case BUTTON:
- case CELL:
- gdk_cairo_set_source_color (cr, &style->text[state_type]);
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
break;
- case MENU:
- gdk_cairo_set_source_color (cr, &style->fg[state_type]);
+ default:
break;
}
if (shadow_type == GTK_SHADOW_IN)
- {
- cairo_translate (cr,
- x + pad, y + pad);
-
- cairo_scale (cr, interior_size / 7., interior_size / 7.);
-
- cairo_move_to (cr, 7.0, 0.0);
- cairo_line_to (cr, 7.5, 1.0);
- cairo_curve_to (cr, 5.3, 2.0,
- 4.3, 4.0,
- 3.5, 7.0);
- cairo_curve_to (cr, 3.0, 5.7,
- 1.3, 4.7,
- 0.0, 4.7);
- cairo_line_to (cr, 0.2, 3.5);
- cairo_curve_to (cr, 1.1, 3.5,
- 2.3, 4.3,
- 3.0, 5.0);
- cairo_curve_to (cr, 1.0, 3.9,
- 2.4, 4.1,
- 3.2, 4.9);
- cairo_curve_to (cr, 3.5, 3.1,
- 5.2, 2.0,
- 7.0, 0.0);
-
- cairo_fill (cr);
- }
- else if (shadow_type == GTK_SHADOW_ETCHED_IN) /* inconsistent */
- {
- int line_thickness = MAX (1, (3 + interior_size * 2) / 7);
+ flags |= GTK_STATE_FLAG_ACTIVE;
+ else if (shadow_type == GTK_SHADOW_ETCHED_IN)
+ flags |= GTK_STATE_FLAG_INCONSISTENT;
- cairo_rectangle (cr,
- x + pad,
- y + pad + (1 + interior_size - line_thickness) / 2,
- interior_size,
- line_thickness);
- cairo_fill (cr);
- }
+ gtk_style_context_set_state (context, flags);
+
+ cairo_save (cr);
+
+ gtk_render_check (context,
+ cr, x, y,
+ width, height);
+
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -2873,102 +2778,54 @@ gtk_default_draw_option (GtkStyle *style,
gint width,
gint height)
{
- enum { BUTTON, MENU, CELL } type = BUTTON;
- int exterior_size;
-
- if (detail)
- {
- if (strcmp (detail, "radio") == 0)
- type = CELL;
- else if (strcmp (detail, "option") == 0)
- type = MENU;
- }
-
- exterior_size = MIN (width, height);
- if (exterior_size % 2 == 0) /* Ensure odd */
- exterior_size -= 1;
-
- x -= (1 + exterior_size - width) / 2;
- y -= (1 + exterior_size - height) / 2;
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
- switch (type)
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- case BUTTON:
- case CELL:
- gdk_cairo_set_source_color (cr, &style->base[state_type]);
-
- cairo_arc (cr,
- x + exterior_size / 2.,
- y + exterior_size / 2.,
- (exterior_size - 1) / 2.,
- 0, 2 * G_PI);
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
+ }
- cairo_fill_preserve (cr);
+ gtk_style_context_save (context);
- if (type == BUTTON)
- gdk_cairo_set_source_color (cr, &style->fg[state_type]);
- else
- gdk_cairo_set_source_color (cr, &style->text[state_type]);
-
- cairo_set_line_width (cr, 1.);
- cairo_stroke (cr);
- break;
+ if (detail)
+ transform_detail_string (detail, context);
- case MENU:
- break;
- }
-
- switch (type)
+ switch (state_type)
{
- case BUTTON:
- gdk_cairo_set_source_color (cr, &style->text[state_type]);
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
break;
- case CELL:
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
break;
- case MENU:
- gdk_cairo_set_source_color (cr, &style->fg[state_type]);
+ default:
break;
}
if (shadow_type == GTK_SHADOW_IN)
- {
- int pad = style->xthickness + MAX (1, 2 * (exterior_size - 2 * style->xthickness) / 9);
- int interior_size = MAX (1, exterior_size - 2 * pad);
+ flags |= GTK_STATE_FLAG_ACTIVE;
+ else if (shadow_type == GTK_SHADOW_ETCHED_IN)
+ flags |= GTK_STATE_FLAG_INCONSISTENT;
- if (interior_size < 5)
- {
- interior_size = 7;
- pad = MAX (0, (exterior_size - interior_size) / 2);
- }
+ gtk_style_context_set_state (context, flags);
- cairo_arc (cr,
- x + pad + interior_size / 2.,
- y + pad + interior_size / 2.,
- interior_size / 2.,
- 0, 2 * G_PI);
- cairo_fill (cr);
- }
- else if (shadow_type == GTK_SHADOW_ETCHED_IN) /* inconsistent */
- {
- int pad = style->xthickness + MAX (1, (exterior_size - 2 * style->xthickness) / 9);
- int interior_size = MAX (1, exterior_size - 2 * pad);
- int line_thickness;
-
- if (interior_size < 7)
- {
- interior_size = 7;
- pad = MAX (0, (exterior_size - interior_size) / 2);
- }
-
- line_thickness = MAX (1, (3 + interior_size * 2) / 7);
+ cairo_save (cr);
+ gtk_render_option (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ (gdouble) width,
+ (gdouble) height);
- cairo_rectangle (cr,
- x + pad,
- y + pad + (interior_size - line_thickness) / 2.,
- interior_size,
- line_thickness);
- cairo_fill (cr);
- }
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -3033,185 +2890,55 @@ gtk_default_draw_shadow_gap (GtkStyle *style,
gint gap_x,
gint gap_width)
{
- GdkColor *color1 = NULL;
- GdkColor *color2 = NULL;
- GdkColor *color3 = NULL;
- GdkColor *color4 = NULL;
-
- switch (shadow_type)
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
+
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- case GTK_SHADOW_NONE:
- default:
- return;
- case GTK_SHADOW_IN:
- color1 = &style->dark[state_type];
- color2 = &style->black;
- color3 = &style->bg[state_type];
- color4 = &style->light[state_type];
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
+ }
+
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
+
+ switch (state_type)
+ {
+ case GTK_STATE_ACTIVE:
+ flags |= GTK_STATE_FLAG_ACTIVE;
break;
- case GTK_SHADOW_ETCHED_IN:
- color1 = &style->dark[state_type];
- color2 = &style->light[state_type];
- color3 = &style->dark[state_type];
- color4 = &style->light[state_type];
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
break;
- case GTK_SHADOW_OUT:
- color1 = &style->light[state_type];
- color2 = &style->bg[state_type];
- color3 = &style->dark[state_type];
- color4 = &style->black;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
break;
- case GTK_SHADOW_ETCHED_OUT:
- color1 = &style->light[state_type];
- color2 = &style->dark[state_type];
- color3 = &style->light[state_type];
- color4 = &style->dark[state_type];
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
break;
}
- switch (shadow_type)
- {
- case GTK_SHADOW_NONE:
- case GTK_SHADOW_IN:
- case GTK_SHADOW_OUT:
- case GTK_SHADOW_ETCHED_IN:
- case GTK_SHADOW_ETCHED_OUT:
- switch (gap_side)
- {
- case GTK_POS_TOP:
- _cairo_draw_line (cr, color1,
- x, y, x, y + height - 1);
- _cairo_draw_line (cr, color2,
- x + 1, y, x + 1, y + height - 2);
-
- _cairo_draw_line (cr, color3,
- x + 1, y + height - 2, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, color3,
- x + width - 2, y, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, color4,
- x, y + height - 1, x + width - 1, y + height - 1);
- _cairo_draw_line (cr, color4,
- x + width - 1, y, x + width - 1, y + height - 1);
- if (gap_x > 0)
- {
- _cairo_draw_line (cr, color1,
- x, y, x + gap_x - 1, y);
- _cairo_draw_line (cr, color2,
- x + 1, y + 1, x + gap_x - 1, y + 1);
- _cairo_draw_line (cr, color2,
- x + gap_x, y, x + gap_x, y);
- }
- if ((width - (gap_x + gap_width)) > 0)
- {
- _cairo_draw_line (cr, color1,
- x + gap_x + gap_width, y, x + width - 2, y);
- _cairo_draw_line (cr, color2,
- x + gap_x + gap_width, y + 1, x + width - 3, y + 1);
- _cairo_draw_line (cr, color2,
- x + gap_x + gap_width - 1, y, x + gap_x + gap_width - 1, y);
- }
- break;
- case GTK_POS_BOTTOM:
- _cairo_draw_line (cr, color1,
- x, y, x + width - 1, y);
- _cairo_draw_line (cr, color1,
- x, y, x, y + height - 1);
- _cairo_draw_line (cr, color2,
- x + 1, y + 1, x + width - 2, y + 1);
- _cairo_draw_line (cr, color2,
- x + 1, y + 1, x + 1, y + height - 1);
-
- _cairo_draw_line (cr, color3,
- x + width - 2, y + 1, x + width - 2, y + height - 1);
- _cairo_draw_line (cr, color4,
- x + width - 1, y, x + width - 1, y + height - 1);
- if (gap_x > 0)
- {
- _cairo_draw_line (cr, color4,
- x, y + height - 1, x + gap_x - 1, y + height - 1);
- _cairo_draw_line (cr, color3,
- x + 1, y + height - 2, x + gap_x - 1, y + height - 2);
- _cairo_draw_line (cr, color3,
- x + gap_x, y + height - 1, x + gap_x, y + height - 1);
- }
- if ((width - (gap_x + gap_width)) > 0)
- {
- _cairo_draw_line (cr, color4,
- x + gap_x + gap_width, y + height - 1, x + width - 2, y + height - 1);
- _cairo_draw_line (cr, color3,
- x + gap_x + gap_width, y + height - 2, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, color3,
- x + gap_x + gap_width - 1, y + height - 1, x + gap_x + gap_width - 1, y + height - 1);
- }
- break;
- case GTK_POS_LEFT:
- _cairo_draw_line (cr, color1,
- x, y, x + width - 1, y);
- _cairo_draw_line (cr, color2,
- x, y + 1, x + width - 2, y + 1);
-
- _cairo_draw_line (cr, color3,
- x, y + height - 2, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, color3,
- x + width - 2, y + 1, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, color4,
- x, y + height - 1, x + width - 1, y + height - 1);
- _cairo_draw_line (cr, color4,
- x + width - 1, y, x + width - 1, y + height - 1);
- if (gap_x > 0)
- {
- _cairo_draw_line (cr, color1,
- x, y, x, y + gap_x - 1);
- _cairo_draw_line (cr, color2,
- x + 1, y + 1, x + 1, y + gap_x - 1);
- _cairo_draw_line (cr, color2,
- x, y + gap_x, x, y + gap_x);
- }
- if ((width - (gap_x + gap_width)) > 0)
- {
- _cairo_draw_line (cr, color1,
- x, y + gap_x + gap_width, x, y + height - 2);
- _cairo_draw_line (cr, color2,
- x + 1, y + gap_x + gap_width, x + 1, y + height - 2);
- _cairo_draw_line (cr, color2,
- x, y + gap_x + gap_width - 1, x, y + gap_x + gap_width - 1);
- }
- break;
- case GTK_POS_RIGHT:
- _cairo_draw_line (cr, color1,
- x, y, x + width - 1, y);
- _cairo_draw_line (cr, color1,
- x, y, x, y + height - 1);
- _cairo_draw_line (cr, color2,
- x + 1, y + 1, x + width - 1, y + 1);
- _cairo_draw_line (cr, color2,
- x + 1, y + 1, x + 1, y + height - 2);
-
- _cairo_draw_line (cr, color3,
- x + 1, y + height - 2, x + width - 1, y + height - 2);
- _cairo_draw_line (cr, color4,
- x, y + height - 1, x + width - 1, y + height - 1);
- if (gap_x > 0)
- {
- _cairo_draw_line (cr, color4,
- x + width - 1, y, x + width - 1, y + gap_x - 1);
- _cairo_draw_line (cr, color3,
- x + width - 2, y + 1, x + width - 2, y + gap_x - 1);
- _cairo_draw_line (cr, color3,
- x + width - 1, y + gap_x, x + width - 1, y + gap_x);
- }
- if ((width - (gap_x + gap_width)) > 0)
- {
- _cairo_draw_line (cr, color4,
- x + width - 1, y + gap_x + gap_width, x + width - 1, y + height - 2);
- _cairo_draw_line (cr, color3,
- x + width - 2, y + gap_x + gap_width, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, color3,
- x + width - 1, y + gap_x + gap_width - 1, x + width - 1, y + gap_x + gap_width - 1);
- }
- break;
- }
- }
+ gtk_style_context_set_state (context, flags);
+
+ cairo_save (cr);
+ gtk_render_frame_gap (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ (gdouble) width,
+ (gdouble) height,
+ gap_side,
+ (gdouble) gap_x,
+ (gdouble) gap_x + gap_width);
+
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -3229,189 +2956,61 @@ gtk_default_draw_box_gap (GtkStyle *style,
gint gap_x,
gint gap_width)
{
- GdkColor color1;
- GdkColor color2;
- GdkColor color3;
- GdkColor color4;
-
- gtk_style_apply_default_background (style, cr, gtk_widget_get_window (widget),
- state_type, x, y, width, height);
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
- switch (shadow_type)
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- case GTK_SHADOW_NONE:
- return;
- case GTK_SHADOW_IN:
- color1 = style->dark[state_type];
- color2 = style->black;
- color3 = style->bg[state_type];
- color4 = style->light[state_type];
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
+ }
+
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
+
+ switch (state_type)
+ {
+ case GTK_STATE_ACTIVE:
+ flags |= GTK_STATE_FLAG_ACTIVE;
break;
- case GTK_SHADOW_ETCHED_IN:
- color1 = style->dark[state_type];
- color2 = style->light[state_type];
- color3 = style->dark[state_type];
- color4 = style->light[state_type];
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
break;
- case GTK_SHADOW_OUT:
- color1 = style->light[state_type];
- color2 = style->bg[state_type];
- color3 = style->dark[state_type];
- color4 = style->black;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
break;
- case GTK_SHADOW_ETCHED_OUT:
- color1 = style->light[state_type];
- color2 = style->dark[state_type];
- color3 = style->light[state_type];
- color4 = style->dark[state_type];
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
break;
}
-
- cairo_set_line_width (cr, 1.0);
- switch (shadow_type)
- {
- case GTK_SHADOW_NONE:
- case GTK_SHADOW_IN:
- case GTK_SHADOW_OUT:
- case GTK_SHADOW_ETCHED_IN:
- case GTK_SHADOW_ETCHED_OUT:
- switch (gap_side)
- {
- case GTK_POS_TOP:
- _cairo_draw_line (cr, &color1,
- x, y, x, y + height - 1);
- _cairo_draw_line (cr, &color2,
- x + 1, y, x + 1, y + height - 2);
-
- _cairo_draw_line (cr, &color3,
- x + 1, y + height - 2, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &color3,
- x + width - 2, y, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &color4,
- x, y + height - 1, x + width - 1, y + height - 1);
- _cairo_draw_line (cr, &color4,
- x + width - 1, y, x + width - 1, y + height - 1);
- if (gap_x > 0)
- {
- _cairo_draw_line (cr, &color1,
- x, y, x + gap_x - 1, y);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + gap_x - 1, y + 1);
- _cairo_draw_line (cr, &color2,
- x + gap_x, y, x + gap_x, y);
- }
- if ((width - (gap_x + gap_width)) > 0)
- {
- _cairo_draw_line (cr, &color1,
- x + gap_x + gap_width, y, x + width - 2, y);
- _cairo_draw_line (cr, &color2,
- x + gap_x + gap_width, y + 1, x + width - 2, y + 1);
- _cairo_draw_line (cr, &color2,
- x + gap_x + gap_width - 1, y, x + gap_x + gap_width - 1, y);
- }
- break;
- case GTK_POS_BOTTOM:
- _cairo_draw_line (cr, &color1,
- x, y, x + width - 1, y);
- _cairo_draw_line (cr, &color1,
- x, y, x, y + height - 1);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + width - 2, y + 1);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + 1, y + height - 1);
-
- _cairo_draw_line (cr, &color3,
- x + width - 2, y + 1, x + width - 2, y + height - 1);
- _cairo_draw_line (cr, &color4,
- x + width - 1, y, x + width - 1, y + height - 1);
- if (gap_x > 0)
- {
- _cairo_draw_line (cr, &color4,
- x, y + height - 1, x + gap_x - 1, y + height - 1);
- _cairo_draw_line (cr, &color3,
- x + 1, y + height - 2, x + gap_x - 1, y + height - 2);
- _cairo_draw_line (cr, &color3,
- x + gap_x, y + height - 1, x + gap_x, y + height - 1);
- }
- if ((width - (gap_x + gap_width)) > 0)
- {
- _cairo_draw_line (cr, &color4,
- x + gap_x + gap_width, y + height - 1, x + width - 2, y + height - 1);
- _cairo_draw_line (cr, &color3,
- x + gap_x + gap_width, y + height - 2, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &color3,
- x + gap_x + gap_width - 1, y + height - 1, x + gap_x + gap_width - 1, y + height - 1);
- }
- break;
- case GTK_POS_LEFT:
- _cairo_draw_line (cr, &color1,
- x, y, x + width - 1, y);
- _cairo_draw_line (cr, &color2,
- x, y + 1, x + width - 2, y + 1);
-
- _cairo_draw_line (cr, &color3,
- x, y + height - 2, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &color3,
- x + width - 2, y + 1, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &color4,
- x, y + height - 1, x + width - 1, y + height - 1);
- _cairo_draw_line (cr, &color4,
- x + width - 1, y, x + width - 1, y + height - 1);
- if (gap_x > 0)
- {
- _cairo_draw_line (cr, &color1,
- x, y, x, y + gap_x - 1);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + 1, y + gap_x - 1);
- _cairo_draw_line (cr, &color2,
- x, y + gap_x, x, y + gap_x);
- }
- if ((height - (gap_x + gap_width)) > 0)
- {
- _cairo_draw_line (cr, &color1,
- x, y + gap_x + gap_width, x, y + height - 2);
- _cairo_draw_line (cr, &color2,
- x + 1, y + gap_x + gap_width, x + 1, y + height - 2);
- _cairo_draw_line (cr, &color2,
- x, y + gap_x + gap_width - 1, x, y + gap_x + gap_width - 1);
- }
- break;
- case GTK_POS_RIGHT:
- _cairo_draw_line (cr, &color1,
- x, y, x + width - 1, y);
- _cairo_draw_line (cr, &color1,
- x, y, x, y + height - 1);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + width - 1, y + 1);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + 1, y + height - 2);
-
- _cairo_draw_line (cr, &color3,
- x + 1, y + height - 2, x + width - 1, y + height - 2);
- _cairo_draw_line (cr, &color4,
- x, y + height - 1, x + width - 1, y + height - 1);
- if (gap_x > 0)
- {
- _cairo_draw_line (cr, &color4,
- x + width - 1, y, x + width - 1, y + gap_x - 1);
- _cairo_draw_line (cr, &color3,
- x + width - 2, y + 1, x + width - 2, y + gap_x - 1);
- _cairo_draw_line (cr, &color3,
- x + width - 1, y + gap_x, x + width - 1, y + gap_x);
- }
- if ((height - (gap_x + gap_width)) > 0)
- {
- _cairo_draw_line (cr, &color4,
- x + width - 1, y + gap_x + gap_width, x + width - 1, y + height - 2);
- _cairo_draw_line (cr, &color3,
- x + width - 2, y + gap_x + gap_width, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &color3,
- x + width - 1, y + gap_x + gap_width - 1, x + width - 1, y + gap_x + gap_width - 1);
- }
- break;
- }
- }
+ gtk_style_context_set_state (context, flags);
+
+ cairo_save (cr);
+ gtk_render_background (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ (gdouble) width,
+ (gdouble) height);
+
+ gtk_render_frame_gap (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ (gdouble) width,
+ (gdouble) height,
+ gap_side,
+ (gdouble) gap_x,
+ (gdouble) gap_x + gap_width);
+
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -3427,151 +3026,54 @@ gtk_default_draw_extension (GtkStyle *style,
gint height,
GtkPositionType gap_side)
{
- GdkWindow *window = gtk_widget_get_window (widget);
- GdkColor color1;
- GdkColor color2;
- GdkColor color3;
- GdkColor color4;
-
- switch (gap_side)
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
+
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- case GTK_POS_TOP:
- gtk_style_apply_default_background (style, cr, window,
- state_type,
- x + 1,
- y,
- width - 2,
- height - 1);
- break;
- case GTK_POS_BOTTOM:
- gtk_style_apply_default_background (style, cr, window,
- state_type,
- x + 1,
- y + 1,
- width - 2,
- height - 1);
- break;
- case GTK_POS_LEFT:
- gtk_style_apply_default_background (style, cr, window,
- state_type,
- x,
- y + 1,
- width - 1,
- height - 2);
- break;
- case GTK_POS_RIGHT:
- gtk_style_apply_default_background (style, cr, window,
- state_type,
- x + 1,
- y + 1,
- width - 1,
- height - 2);
- break;
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
- switch (shadow_type)
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
+
+ switch (state_type)
{
- case GTK_SHADOW_NONE:
- return;
- case GTK_SHADOW_IN:
- color1 = style->dark[state_type];
- color2 = style->black;
- color3 = style->bg[state_type];
- color4 = style->light[state_type];
+ case GTK_STATE_ACTIVE:
+ flags |= GTK_STATE_FLAG_ACTIVE;
break;
- case GTK_SHADOW_ETCHED_IN:
- color1 = style->dark[state_type];
- color2 = style->light[state_type];
- color3 = style->dark[state_type];
- color4 = style->light[state_type];
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
break;
- case GTK_SHADOW_OUT:
- color1 = style->light[state_type];
- color2 = style->bg[state_type];
- color3 = style->dark[state_type];
- color4 = style->black;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
break;
- case GTK_SHADOW_ETCHED_OUT:
- color1 = style->light[state_type];
- color2 = style->dark[state_type];
- color3 = style->light[state_type];
- color4 = style->dark[state_type];
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
break;
}
- cairo_set_line_width (cr, 1.0);
+ gtk_style_context_set_state (context, flags);
- switch (shadow_type)
- {
- case GTK_SHADOW_NONE:
- case GTK_SHADOW_IN:
- case GTK_SHADOW_OUT:
- case GTK_SHADOW_ETCHED_IN:
- case GTK_SHADOW_ETCHED_OUT:
- switch (gap_side)
- {
- case GTK_POS_TOP:
- _cairo_draw_line (cr, &color1,
- x, y, x, y + height - 2);
- _cairo_draw_line (cr, &color2,
- x + 1, y, x + 1, y + height - 2);
-
- _cairo_draw_line (cr, &color3,
- x + 2, y + height - 2, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &color3,
- x + width - 2, y, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &color4,
- x + 1, y + height - 1, x + width - 2, y + height - 1);
- _cairo_draw_line (cr, &color4,
- x + width - 1, y, x + width - 1, y + height - 2);
- break;
- case GTK_POS_BOTTOM:
- _cairo_draw_line (cr, &color1,
- x + 1, y, x + width - 2, y);
- _cairo_draw_line (cr, &color1,
- x, y + 1, x, y + height - 1);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + width - 2, y + 1);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + 1, y + height - 1);
-
- _cairo_draw_line (cr, &color3,
- x + width - 2, y + 2, x + width - 2, y + height - 1);
- _cairo_draw_line (cr, &color4,
- x + width - 1, y + 1, x + width - 1, y + height - 1);
- break;
- case GTK_POS_LEFT:
- _cairo_draw_line (cr, &color1,
- x, y, x + width - 2, y);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + width - 2, y + 1);
-
- _cairo_draw_line (cr, &color3,
- x, y + height - 2, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &color3,
- x + width - 2, y + 2, x + width - 2, y + height - 2);
- _cairo_draw_line (cr, &color4,
- x, y + height - 1, x + width - 2, y + height - 1);
- _cairo_draw_line (cr, &color4,
- x + width - 1, y + 1, x + width - 1, y + height - 2);
- break;
- case GTK_POS_RIGHT:
- _cairo_draw_line (cr, &color1,
- x + 1, y, x + width - 1, y);
- _cairo_draw_line (cr, &color1,
- x, y + 1, x, y + height - 2);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + width - 1, y + 1);
- _cairo_draw_line (cr, &color2,
- x + 1, y + 1, x + 1, y + height - 2);
-
- _cairo_draw_line (cr, &color3,
- x + 2, y + height - 2, x + width - 1, y + height - 2);
- _cairo_draw_line (cr, &color4,
- x + 1, y + height - 1, x + width - 1, y + height - 1);
- break;
- }
- }
+ cairo_save (cr);
+
+ gtk_render_extension (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ (gdouble) width,
+ (gdouble) height,
+ gap_side);
+
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -3585,74 +3087,32 @@ gtk_default_draw_focus (GtkStyle *style,
gint width,
gint height)
{
- gboolean free_dash_list = FALSE;
- gint line_width = 1;
- gint8 *dash_list = (gint8 *) "\1\1";
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- gtk_widget_style_get (widget,
- "focus-line-width", &line_width,
- "focus-line-pattern", (gchar *)&dash_list,
- NULL);
-
- free_dash_list = TRUE;
- }
-
- if (detail && !strcmp (detail, "add-mode"))
- {
- if (free_dash_list)
- g_free (dash_list);
-
- dash_list = (gint8 *) "\4\4";
- free_dash_list = FALSE;
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
- if (detail && !strcmp (detail, "colorwheel_light"))
- cairo_set_source_rgb (cr, 0., 0., 0.);
- else if (detail && !strcmp (detail, "colorwheel_dark"))
- cairo_set_source_rgb (cr, 1., 1., 1.);
- else
- gdk_cairo_set_source_color (cr, &style->fg[state_type]);
-
- cairo_set_line_width (cr, line_width);
-
- if (dash_list[0])
- {
- gint n_dashes = strlen ((const gchar *) dash_list);
- gdouble *dashes = g_new (gdouble, n_dashes);
- gdouble total_length = 0;
- gdouble dash_offset;
- gint i;
+ gtk_style_context_save (context);
- for (i = 0; i < n_dashes; i++)
- {
- dashes[i] = dash_list[i];
- total_length += dash_list[i];
- }
+ if (detail)
+ transform_detail_string (detail, context);
- /* The dash offset here aligns the pattern to integer pixels
- * by starting the dash at the right side of the left border
- * Negative dash offsets in cairo don't work
- * (https://bugs.freedesktop.org/show_bug.cgi?id=2729)
- */
- dash_offset = - line_width / 2.;
- while (dash_offset < 0)
- dash_offset += total_length;
-
- cairo_set_dash (cr, dashes, n_dashes, dash_offset);
- g_free (dashes);
- }
+ cairo_save (cr);
- cairo_rectangle (cr,
- x + line_width / 2.,
- y + line_width / 2.,
- width - line_width,
- height - line_width);
- cairo_stroke (cr);
+ gtk_render_focus (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ (gdouble) width,
+ (gdouble) height);
- if (free_dash_list)
- g_free (dash_list);
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -3668,22 +3128,46 @@ gtk_default_draw_slider (GtkStyle *style,
gint height,
GtkOrientation orientation)
{
- gtk_paint_box (style, cr, state_type, shadow_type,
- widget, detail, x, y, width, height);
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
- if (detail &&
- (strcmp ("hscale", detail) == 0 ||
- strcmp ("vscale", detail) == 0))
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- if (orientation == GTK_ORIENTATION_HORIZONTAL)
- gtk_paint_vline (style, cr, state_type, widget, detail,
- y + style->ythickness,
- y + height - style->ythickness - 1, x + width / 2);
- else
- gtk_paint_hline (style, cr, state_type, widget, detail,
- x + style->xthickness,
- x + width - style->xthickness - 1, y + height / 2);
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
+ }
+
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
+
+ switch (state_type)
+ {
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
+ break;
}
+
+ gtk_style_context_set_state (context, flags);
+
+ cairo_save (cr);
+
+ gtk_render_slider (context, cr, x, y, width, height, orientation);
+
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -3725,58 +3209,50 @@ gtk_default_draw_handle (GtkStyle *style,
gint height,
GtkOrientation orientation)
{
- gint xx, yy;
- gint xthick, ythick;
- GdkColor light, dark;
-
- gtk_paint_box (style, cr, state_type, shadow_type, widget,
- detail, x, y, width, height);
-
- if (detail && !strcmp (detail, "paned"))
- {
- /* we want to ignore the shadow border in paned widgets */
- xthick = 0;
- ythick = 0;
-
- if (state_type == GTK_STATE_SELECTED && widget && !gtk_widget_has_focus (widget))
- _gtk_style_shade (&style->base[GTK_STATE_ACTIVE], &light,
- LIGHTNESS_MULT);
- else
- light = style->light[state_type];
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
- dark = style->black;
- }
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
else
{
- xthick = style->xthickness;
- ythick = style->ythickness;
-
- light = style->light[state_type];
- dark = style->dark[state_type];
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
-
- cairo_rectangle(cr, x + xthick, y + ythick,
- width - (xthick * 2), height - (ythick * 2));
- cairo_clip (cr);
- if (detail && !strcmp (detail, "paned"))
- {
- if (orientation == GTK_ORIENTATION_HORIZONTAL)
- for (xx = x + width/2 - 15; xx <= x + width/2 + 15; xx += 5)
- draw_dot (cr, &light, &dark, xx, y + height/2 - 1, 3);
- else
- for (yy = y + height/2 - 15; yy <= y + height/2 + 15; yy += 5)
- draw_dot (cr, &light, &dark, x + width/2 - 1, yy, 3);
- }
- else
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
+
+ switch (state_type)
{
- for (yy = y + ythick; yy < (y + height - ythick); yy += 3)
- for (xx = x + xthick; xx < (x + width - xthick); xx += 6)
- {
- draw_dot (cr, &light, &dark, xx, yy, 2);
- draw_dot (cr, &light, &dark, xx + 3, yy + 1, 2);
- }
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
+ break;
}
+
+ gtk_style_context_set_state (context, flags);
+
+ cairo_save (cr);
+
+ gtk_render_handle (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ (gdouble) width,
+ (gdouble) height);
+
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -3789,117 +3265,63 @@ gtk_default_draw_expander (GtkStyle *style,
gint y,
GtkExpanderStyle expander_style)
{
-#define DEFAULT_EXPANDER_SIZE 12
-
- gint expander_size;
- gint line_width;
- double vertical_overshoot;
- int diameter;
- double radius;
- double interp; /* interpolation factor for center position */
- double x_double_horz, y_double_horz;
- double x_double_vert, y_double_vert;
- double x_double, y_double;
- gint degrees = 0;
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
+ gint size;
- if (widget &&
- gtk_widget_class_find_style_property (GTK_WIDGET_GET_CLASS (widget),
- "expander-size"))
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- gtk_widget_style_get (widget,
- "expander-size", &expander_size,
- NULL);
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
- else
- expander_size = DEFAULT_EXPANDER_SIZE;
-
- line_width = MAX (1, expander_size/9);
- switch (expander_style)
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
+
+ gtk_style_context_add_class (context, "expander");
+
+ switch (state_type)
{
- case GTK_EXPANDER_COLLAPSED:
- degrees = (get_direction (widget) == GTK_TEXT_DIR_RTL) ? 180 : 0;
- interp = 0.0;
- break;
- case GTK_EXPANDER_SEMI_COLLAPSED:
- degrees = (get_direction (widget) == GTK_TEXT_DIR_RTL) ? 150 : 30;
- interp = 0.25;
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
break;
- case GTK_EXPANDER_SEMI_EXPANDED:
- degrees = (get_direction (widget) == GTK_TEXT_DIR_RTL) ? 120 : 60;
- interp = 0.75;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
break;
- case GTK_EXPANDER_EXPANDED:
- degrees = 90;
- interp = 1.0;
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
break;
default:
- g_assert_not_reached ();
+ break;
}
- /* Compute distance that the stroke extends beyonds the end
- * of the triangle we draw.
- */
- vertical_overshoot = line_width / 2.0 * (1. / tan (G_PI / 8));
-
- /* For odd line widths, we end the vertical line of the triangle
- * at a half pixel, so we round differently.
- */
- if (line_width % 2 == 1)
- vertical_overshoot = ceil (0.5 + vertical_overshoot) - 0.5;
+ if (widget &&
+ gtk_widget_class_find_style_property (GTK_WIDGET_GET_CLASS (widget),
+ "expander-size"))
+ gtk_widget_style_get (widget, "expander-size", &size, NULL);
else
- vertical_overshoot = ceil (vertical_overshoot);
+ size = 12;
- /* Adjust the size of the triangle we draw so that the entire stroke fits
- */
- diameter = MAX (3, expander_size - 2 * vertical_overshoot);
+ if (expander_style == GTK_EXPANDER_EXPANDED)
+ flags |= GTK_STATE_FLAG_ACTIVE;
- /* If the line width is odd, we want the diameter to be even,
- * and vice versa, so force the sum to be odd. This relationship
- * makes the point of the triangle look right.
- */
- diameter -= (1 - (diameter + line_width) % 2);
-
- radius = diameter / 2.;
+ gtk_style_context_set_state (context, flags);
- /* Adjust the center so that the stroke is properly aligned with
- * the pixel grid. The center adjustment is different for the
- * horizontal and vertical orientations. For intermediate positions
- * we interpolate between the two.
- */
- x_double_vert = floor (x - (radius + line_width) / 2.) + (radius + line_width) / 2.;
- y_double_vert = y - 0.5;
-
- x_double_horz = x - 0.5;
- y_double_horz = floor (y - (radius + line_width) / 2.) + (radius + line_width) / 2.;
+ cairo_save (cr);
- x_double = x_double_vert * (1 - interp) + x_double_horz * interp;
- y_double = y_double_vert * (1 - interp) + y_double_horz * interp;
-
- cairo_translate (cr, x_double, y_double);
- cairo_rotate (cr, degrees * G_PI / 180);
+ gtk_render_expander (context, cr,
+ (gdouble) x - (size / 2),
+ (gdouble) y - (size / 2),
+ (gdouble) size,
+ (gdouble) size);
- cairo_move_to (cr, - radius / 2., - radius);
- cairo_line_to (cr, radius / 2., 0);
- cairo_line_to (cr, - radius / 2., radius);
- cairo_close_path (cr);
-
- cairo_set_line_width (cr, line_width);
-
- if (state_type == GTK_STATE_PRELIGHT)
- gdk_cairo_set_source_color (cr,
- &style->fg[GTK_STATE_PRELIGHT]);
- else if (state_type == GTK_STATE_ACTIVE)
- gdk_cairo_set_source_color (cr,
- &style->light[GTK_STATE_ACTIVE]);
- else
- gdk_cairo_set_source_color (cr,
- &style->base[GTK_STATE_NORMAL]);
-
- cairo_fill_preserve (cr);
-
- gdk_cairo_set_source_color (cr, &style->fg[state_type]);
- cairo_stroke (cr);
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -3913,49 +3335,49 @@ gtk_default_draw_layout (GtkStyle *style,
gint y,
PangoLayout *layout)
{
- GdkColor *gc;
- const PangoMatrix *matrix;
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
- matrix = pango_context_get_matrix (pango_layout_get_context (layout));
- if (matrix)
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
{
- cairo_matrix_t cairo_matrix;
- PangoMatrix tmp_matrix;
- PangoRectangle rect;
-
- cairo_matrix_init (&cairo_matrix,
- matrix->xx, matrix->yx,
- matrix->xy, matrix->yy,
- matrix->x0, matrix->y0);
-
- pango_layout_get_extents (layout, NULL, &rect);
- pango_matrix_transform_rectangle (matrix, &rect);
- pango_extents_to_pixels (&rect, NULL);
-
- tmp_matrix = *matrix;
- cairo_matrix.x0 += x - rect.x;
- cairo_matrix.y0 += y - rect.y;
-
- cairo_set_matrix (cr, &cairo_matrix);
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
}
- else
- cairo_translate (cr, x, y);
- cairo_new_path (cr);
+ gtk_style_context_save (context);
- if (state_type == GTK_STATE_INSENSITIVE)
+ if (detail)
+ transform_detail_string (detail, context);
+
+ switch (state_type)
{
- gdk_cairo_set_source_color (cr, &style->white);
- cairo_move_to (cr, 1, 1);
- _gtk_pango_fill_layout (cr, layout);
- cairo_new_path (cr);
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
+ break;
}
- gc = use_text ? &style->text[state_type] : &style->fg[state_type];
+ gtk_style_context_set_state (context, flags);
+
+ cairo_save (cr);
- gdk_cairo_set_source_color (cr, gc);
+ gtk_render_layout (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ layout);
- pango_cairo_show_layout (cr, layout);
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -3970,284 +3392,83 @@ gtk_default_draw_resize_grip (GtkStyle *style,
gint width,
gint height)
{
- gint skip;
+ GtkStyleContext *context;
+ GtkStylePrivate *priv;
+ GtkStateFlags flags = 0;
+ GtkJunctionSides sides = 0;
- cairo_rectangle (cr, x, y, width, height);
- cairo_clip (cr);
+ if (widget)
+ context = gtk_widget_get_style_context (widget);
+ else
+ {
+ priv = GTK_STYLE_GET_PRIVATE (style);
+ context = priv->context;
+ }
+
+ gtk_style_context_save (context);
+
+ if (detail)
+ transform_detail_string (detail, context);
- cairo_set_line_width (cr, 1.0);
+ gtk_style_context_add_class (context, "grip");
+
+ switch (state_type)
+ {
+ case GTK_STATE_PRELIGHT:
+ flags |= GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags |= GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ default:
+ break;
+ }
+
+ gtk_style_context_set_state (context, flags);
- skip = -1;
switch (edge)
{
case GDK_WINDOW_EDGE_NORTH_WEST:
- /* make it square */
- if (width < height)
- height = width;
- else if (height < width)
- width = height;
- skip = 2;
+ sides = GTK_JUNCTION_CORNER_TOPLEFT;
break;
case GDK_WINDOW_EDGE_NORTH:
- if (width < height)
- height = width;
+ sides = GTK_JUNCTION_TOP;
break;
case GDK_WINDOW_EDGE_NORTH_EAST:
- /* make it square, aligning to top right */
- if (width < height)
- height = width;
- else if (height < width)
- {
- x += (width - height);
- width = height;
- }
- skip = 3;
+ sides = GTK_JUNCTION_CORNER_TOPRIGHT;
break;
case GDK_WINDOW_EDGE_WEST:
- if (height < width)
- width = height;
+ sides = GTK_JUNCTION_LEFT;
break;
case GDK_WINDOW_EDGE_EAST:
- /* aligning to right */
- if (height < width)
- {
- x += (width - height);
- width = height;
- }
+ sides = GTK_JUNCTION_RIGHT;
break;
case GDK_WINDOW_EDGE_SOUTH_WEST:
- /* make it square, aligning to bottom left */
- if (width < height)
- {
- y += (height - width);
- height = width;
- }
- else if (height < width)
- width = height;
- skip = 1;
- break;
- case GDK_WINDOW_EDGE_SOUTH:
- /* align to bottom */
- if (width < height)
- {
- y += (height - width);
- height = width;
- }
- break;
- case GDK_WINDOW_EDGE_SOUTH_EAST:
- /* make it square, aligning to bottom right */
- if (width < height)
- {
- y += (height - width);
- height = width;
- }
- else if (height < width)
- {
- x += (width - height);
- width = height;
- }
- skip = 0;
- break;
- default:
- g_assert_not_reached ();
- }
-
- switch (edge)
- {
- case GDK_WINDOW_EDGE_WEST:
- case GDK_WINDOW_EDGE_EAST:
- {
- gint xi;
-
- xi = x;
-
- while (xi < x + width)
- {
- _cairo_draw_line (cr,
- &style->light[state_type],
- xi, y,
- xi, y + height);
-
- xi++;
- _cairo_draw_line (cr,
- &style->dark[state_type],
- xi, y,
- xi, y + height);
-
- xi += 2;
- }
- }
+ sides = GTK_JUNCTION_CORNER_BOTTOMLEFT;
break;
- case GDK_WINDOW_EDGE_NORTH:
case GDK_WINDOW_EDGE_SOUTH:
- {
- gint yi;
-
- yi = y;
-
- while (yi < y + height)
- {
- _cairo_draw_line (cr,
- &style->light[state_type],
- x, yi,
- x + width, yi);
-
- yi++;
- _cairo_draw_line (cr,
- &style->dark[state_type],
- x, yi,
- x + width, yi);
-
- yi+= 2;
- }
- }
- break;
- case GDK_WINDOW_EDGE_NORTH_WEST:
- {
- gint xi, yi;
-
- xi = x + width;
- yi = y + height;
-
- while (xi > x + 3)
- {
- _cairo_draw_line (cr,
- &style->dark[state_type],
- xi, y,
- x, yi);
-
- --xi;
- --yi;
-
- _cairo_draw_line (cr,
- &style->dark[state_type],
- xi, y,
- x, yi);
-
- --xi;
- --yi;
-
- _cairo_draw_line (cr,
- &style->light[state_type],
- xi, y,
- x, yi);
-
- xi -= 3;
- yi -= 3;
-
- }
- }
- break;
- case GDK_WINDOW_EDGE_NORTH_EAST:
- {
- gint xi, yi;
-
- xi = x;
- yi = y + height;
-
- while (xi < (x + width - 3))
- {
- _cairo_draw_line (cr,
- &style->light[state_type],
- xi, y,
- x + width, yi);
-
- ++xi;
- --yi;
-
- _cairo_draw_line (cr,
- &style->dark[state_type],
- xi, y,
- x + width, yi);
-
- ++xi;
- --yi;
-
- _cairo_draw_line (cr,
- &style->dark[state_type],
- xi, y,
- x + width, yi);
-
- xi += 3;
- yi -= 3;
- }
- }
- break;
- case GDK_WINDOW_EDGE_SOUTH_WEST:
- {
- gint xi, yi;
-
- xi = x + width;
- yi = y;
-
- while (xi > x + 3)
- {
- _cairo_draw_line (cr,
- &style->dark[state_type],
- x, yi,
- xi, y + height);
-
- --xi;
- ++yi;
-
- _cairo_draw_line (cr,
- &style->dark[state_type],
- x, yi,
- xi, y + height);
-
- --xi;
- ++yi;
-
- _cairo_draw_line (cr,
- &style->light[state_type],
- x, yi,
- xi, y + height);
-
- xi -= 3;
- yi += 3;
-
- }
- }
+ sides = GTK_JUNCTION_BOTTOM;
break;
case GDK_WINDOW_EDGE_SOUTH_EAST:
- {
- gint xi, yi;
-
- xi = x;
- yi = y;
-
- while (xi < (x + width - 3))
- {
- _cairo_draw_line (cr,
- &style->light[state_type],
- xi, y + height,
- x + width, yi);
-
- ++xi;
- ++yi;
-
- _cairo_draw_line (cr,
- &style->dark[state_type],
- xi, y + height,
- x + width, yi);
-
- ++xi;
- ++yi;
-
- _cairo_draw_line (cr,
- &style->dark[state_type],
- xi, y + height,
- x + width, yi);
-
- xi += 3;
- yi += 3;
- }
- }
- break;
- default:
- g_assert_not_reached ();
+ sides = GTK_JUNCTION_CORNER_BOTTOMRIGHT;
break;
}
+
+ gtk_style_context_set_junction_sides (context, sides);
+
+ cairo_save (cr);
+
+ gtk_render_handle (context, cr,
+ (gdouble) x,
+ (gdouble) y,
+ (gdouble) width,
+ (gdouble) height);
+
+ cairo_restore (cr);
+ gtk_style_context_restore (context);
}
static void
@@ -4517,8 +3738,10 @@ hls_to_rgb (gdouble *h,
*
* Draws a horizontal line from (@x1, @y) to (@x2, @y) in @cr
* using the given style and state.
- **/
-void
+ *
+ * Deprecated:3.0: Use gtk_render_line() instead
+ **/
+void
gtk_paint_hline (GtkStyle *style,
cairo_t *cr,
GtkStateType state_type,
@@ -4554,6 +3777,8 @@ gtk_paint_hline (GtkStyle *style,
*
* Draws a vertical line from (@x, @y1_) to (@x, @y2_) in @cr
* using the given style and state.
+ *
+ * Deprecated:3.0: Use gtk_render_line() instead
*/
void
gtk_paint_vline (GtkStyle *style,
@@ -4591,8 +3816,10 @@ gtk_paint_vline (GtkStyle *style,
* @width: width of the rectangle
* @height: width of the rectangle
*
- * Draws a shadow around the given rectangle in @cr
+ * Draws a shadow around the given rectangle in @cr
* using the given style and state and shadow type.
+ *
+ * Deprecated:3.0: Use gtk_render_frame() instead
*/
void
gtk_paint_shadow (GtkStyle *style,
@@ -4635,9 +3862,11 @@ gtk_paint_shadow (GtkStyle *style,
* @y: y origin of the rectangle to draw the arrow in
* @width: width of the rectangle to draw the arrow in
* @height: height of the rectangle to draw the arrow in
- *
- * Draws an arrow in the given rectangle on @cr using the given
+ *
+ * Draws an arrow in the given rectangle on @cr using the given
* parameters. @arrow_type determines the direction of the arrow.
+ *
+ * Deprecated:3.0: Use gtk_render_arrow() instead
*/
void
gtk_paint_arrow (GtkStyle *style,
@@ -4683,6 +3912,8 @@ gtk_paint_arrow (GtkStyle *style,
*
* Draws a diamond in the given rectangle on @window using the given
* parameters.
+ *
+ * Deprecated:3.0: Use cairo instead
*/
void
gtk_paint_diamond (GtkStyle *style,
@@ -4723,8 +3954,10 @@ gtk_paint_diamond (GtkStyle *style,
* @y: y origin of the box
* @width: the width of the box
* @height: the height of the box
- *
+ *
* Draws a box on @cr with the given parameters.
+ *
+ * Deprecated:3.0: Use gtk_render_frame() and gtk_render_background() instead
*/
void
gtk_paint_box (GtkStyle *style,
@@ -4765,8 +3998,10 @@ gtk_paint_box (GtkStyle *style,
* @y: y origin of the box
* @width: the width of the box
* @height: the height of the box
- *
+ *
* Draws a flat box on @cr with the given parameters.
+ *
+ * Deprecated:3.0: Use gtk_render_frame() and gtk_render_background() instead
*/
void
gtk_paint_flat_box (GtkStyle *style,
@@ -4807,9 +4042,11 @@ gtk_paint_flat_box (GtkStyle *style,
* @y: y origin of the rectangle to draw the check in
* @width: the width of the rectangle to draw the check in
* @height: the height of the rectangle to draw the check in
- *
- * Draws a check button indicator in the given rectangle on @cr with
+ *
+ * Draws a check button indicator in the given rectangle on @cr with
* the given parameters.
+ *
+ * Deprecated:3.0: Use gtk_render_check() instead
*/
void
gtk_paint_check (GtkStyle *style,
@@ -4849,8 +4086,10 @@ gtk_paint_check (GtkStyle *style,
* @width: the width of the rectangle to draw the option in
* @height: the height of the rectangle to draw the option in
*
- * Draws a radio button indicator in the given rectangle on @cr with
+ * Draws a radio button indicator in the given rectangle on @cr with
* the given parameters.
+ *
+ * Deprecated:3.0: Use gtk_render_option() instead
*/
void
gtk_paint_option (GtkStyle *style,
@@ -4892,7 +4131,9 @@ gtk_paint_option (GtkStyle *style,
*
* Draws an option menu tab (i.e. the up and down pointing arrows)
* in the given rectangle on @cr using the given parameters.
- */
+ *
+ * Deprecated:3.0: Use cairo instead
+ */
void
gtk_paint_tab (GtkStyle *style,
cairo_t *cr,
@@ -4935,9 +4176,11 @@ gtk_paint_tab (GtkStyle *style,
* @gap_width: width of the gap
*
* Draws a shadow around the given rectangle in @cr
- * using the given style and state and shadow type, leaving a
+ * using the given style and state and shadow type, leaving a
* gap in one side.
-*/
+ *
+ * Deprecated:3.0: Use gtk_render_frame_gap() instead
+ */
void
gtk_paint_shadow_gap (GtkStyle *style,
cairo_t *cr,
@@ -4984,8 +4227,10 @@ gtk_paint_shadow_gap (GtkStyle *style,
* @gap_x: starting position of the gap
* @gap_width: width of the gap
*
- * Draws a box in @cr using the given style and state and shadow type,
+ * Draws a box in @cr using the given style and state and shadow type,
* leaving a gap in one side.
+ *
+ * Deprecated:3.0: Use gtk_render_frame_gap() instead
*/
void
gtk_paint_box_gap (GtkStyle *style,
@@ -5018,7 +4263,7 @@ gtk_paint_box_gap (GtkStyle *style,
}
/**
- * gtk_paint_extension:
+ * gtk_paint_extension:
* @style: a #GtkStyle
* @cr: a #cairo_t
* @state_type: a state
@@ -5030,8 +4275,10 @@ gtk_paint_box_gap (GtkStyle *style,
* @width: width of the extension
* @height: width of the extension
* @gap_side: the side on to which the extension is attached
- *
+ *
* Draws an extension, i.e. a notebook tab.
+ *
+ * Deprecated:3.0: Use gtk_render_extension() instead
**/
void
gtk_paint_extension (GtkStyle *style,
@@ -5075,6 +4322,8 @@ gtk_paint_extension (GtkStyle *style,
*
* Draws a focus indicator around the given rectangle on @cr using the
* given style.
+ *
+ * Deprecated:3.0: Use gtk_render_focus() instead
*/
void
gtk_paint_focus (GtkStyle *style,
@@ -5118,6 +4367,8 @@ gtk_paint_focus (GtkStyle *style,
*
* Draws a slider in the given rectangle on @cr using the
* given style and orientation.
+ *
+ * Deprecated:3.0: Use gtk_render_slider() instead
**/
void
gtk_paint_slider (GtkStyle *style,
@@ -5160,8 +4411,10 @@ gtk_paint_slider (GtkStyle *style,
* @width: with of the handle
* @height: height of the handle
* @orientation: the orientation of the handle
- *
+ *
* Draws a handle as used in #GtkHandleBox and #GtkPaned.
+ *
+ * Deprecated:3.0: Use gtk_render_handle() instead
**/
void
gtk_paint_handle (GtkStyle *style,
@@ -5203,7 +4456,7 @@ gtk_paint_handle (GtkStyle *style,
* @expander_style: the style to draw the expander in; determines
* whether the expander is collapsed, expanded, or in an
* intermediate state.
- *
+ *
* Draws an expander as used in #GtkTreeView. @x and @y specify the
* center the expander. The size of the expander is determined by the
* "expander-size" style property of @widget. (If widget is not
@@ -5213,6 +4466,8 @@ gtk_paint_handle (GtkStyle *style,
* likely not useful.) The expander is expander_size pixels tall
* in the collapsed position and expander_size pixels wide in the
* expanded position.
+ *
+ * Deprecated:3.0: Use gtk_render_expander() instead
**/
void
gtk_paint_expander (GtkStyle *style,
@@ -5251,6 +4506,8 @@ gtk_paint_expander (GtkStyle *style,
* @layout: the layout to draw
*
* Draws a layout on @cr using the given parameters.
+ *
+ * Deprecated:3.0: Use gtk_render_layout() instead
**/
void
gtk_paint_layout (GtkStyle *style,
@@ -5290,7 +4547,9 @@ gtk_paint_layout (GtkStyle *style,
* @height: the height of the rectangle in which to draw the resize grip
*
* Draws a resize grip in the given rectangle on @cr using the given
- * parameters.
+ * parameters.
+ *
+ * Deprecated:3.0: Use gtk_render_handle() instead
*/
void
gtk_paint_resize_grip (GtkStyle *style,
@@ -5330,6 +4589,8 @@ gtk_paint_resize_grip (GtkStyle *style,
* @height: the height of the rectangle in which to draw the spinner
*
* Draws a spinner on @window using the given parameters.
+ *
+ * Deprecated:3.0: Use gtk_render_activity() instead
*/
void
gtk_paint_spinner (GtkStyle *style,
@@ -5356,53 +4617,6 @@ gtk_paint_spinner (GtkStyle *style,
cairo_restore (cr);
}
-/**
- * gtk_border_new:
- *
- * Allocates a new #GtkBorder structure and initializes its elements to zero.
- *
- * Returns: a new empty #GtkBorder. The newly allocated #GtkBorder should be
- * freed with gtk_border_free()
- *
- * Since: 2.14
- **/
-GtkBorder *
-gtk_border_new (void)
-{
- return g_slice_new0 (GtkBorder);
-}
-
-/**
- * gtk_border_copy:
- * @border_: a #GtkBorder.
- * @returns: a copy of @border_.
- *
- * Copies a #GtkBorder structure.
- **/
-GtkBorder *
-gtk_border_copy (const GtkBorder *border)
-{
- g_return_val_if_fail (border != NULL, NULL);
-
- return g_slice_dup (GtkBorder, border);
-}
-
-/**
- * gtk_border_free:
- * @border_: a #GtkBorder.
- *
- * Frees a #GtkBorder structure.
- **/
-void
-gtk_border_free (GtkBorder *border)
-{
- g_slice_free (GtkBorder, border);
-}
-
-G_DEFINE_BOXED_TYPE (GtkBorder, gtk_border,
- gtk_border_copy,
- gtk_border_free)
-
typedef struct _CursorInfo CursorInfo;
struct _CursorInfo
@@ -5516,7 +4730,7 @@ _gtk_widget_get_cursor_color (GtkWidget *widget,
* right-to-left. Should never be #GTK_TEXT_DIR_NONE
* @draw_arrow: %TRUE to draw a directional arrow on the
* cursor. Should be %FALSE unless the cursor is split.
- *
+ *
* Draws a text caret on @cr at @location. This is not a style function
* but merely a convenience function for drawing the standard cursor shape.
*
diff --git a/gtk/gtkstyle.h b/gtk/gtkstyle.h
index 84a018ce1a..3d44b1ffa7 100644
--- a/gtk/gtkstyle.h
+++ b/gtk/gtkstyle.h
@@ -45,12 +45,9 @@ G_BEGIN_DECLS
#define GTK_IS_STYLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_STYLE))
#define GTK_STYLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_STYLE, GtkStyleClass))
-#define GTK_TYPE_BORDER (gtk_border_get_type ())
-
/* Some forward declarations needed to rationalize the header
* files.
*/
-typedef struct _GtkBorder GtkBorder;
typedef struct _GtkStyle GtkStyle;
typedef struct _GtkStyleClass GtkStyleClass;
typedef struct _GtkThemeEngine GtkThemeEngine;
@@ -383,23 +380,7 @@ struct _GtkStyleClass
void (*_gtk_reserved11) (void);
};
-/**
- * GtkBorder:
- * @left: The width of the left border.
- * @right: The width of the right border.
- * @top: The width of the top border.
- * @bottom: The width of the bottom border.
- *
- * A struct that specifies a border around a rectangular area that can
- * be of different width on each side.
- */
-struct _GtkBorder
-{
- gint16 left;
- gint16 right;
- gint16 top;
- gint16 bottom;
-};
+#if !defined(GTK_DISABLE_DEPRECATED) || defined(GTK_COMPILATION)
GType gtk_style_get_type (void) G_GNUC_CONST;
GtkStyle* gtk_style_new (void);
@@ -637,12 +618,6 @@ void gtk_paint_spinner (GtkStyle *style,
gint width,
gint height);
-
-GType gtk_border_get_type (void) G_GNUC_CONST;
-GtkBorder *gtk_border_new (void) G_GNUC_MALLOC;
-GtkBorder *gtk_border_copy (const GtkBorder *border_);
-void gtk_border_free (GtkBorder *border_);
-
void gtk_style_get_style_property (GtkStyle *style,
GType widget_type,
const gchar *property_name,
@@ -656,6 +631,8 @@ void gtk_style_get (GtkStyle *style,
const gchar *first_property_name,
...) G_GNUC_NULL_TERMINATED;
+#endif
+
/* --- private API --- */
const GValue* _gtk_style_peek_property_value (GtkStyle *style,
GType widget_type,
@@ -678,6 +655,10 @@ void gtk_draw_insertion_cursor (GtkWidget *widget,
void _gtk_widget_get_cursor_color (GtkWidget *widget,
GdkColor *color);
+gboolean gtk_style_has_context (GtkStyle *style);
+
+
+
G_END_DECLS
#endif /* __GTK_STYLE_H__ */
diff --git a/gtk/gtkstylecontext.c b/gtk/gtkstylecontext.c
new file mode 100644
index 0000000000..b8d8c992e7
--- /dev/null
+++ b/gtk/gtkstylecontext.c
@@ -0,0 +1,3985 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <gdk/gdk.h>
+#include <stdlib.h>
+#include <gobject/gvaluecollector.h>
+
+#include "gtkstylecontext.h"
+#include "gtktypebuiltins.h"
+#include "gtkthemingengine.h"
+#include "gtkintl.h"
+#include "gtkwidget.h"
+#include "gtkwindow.h"
+#include "gtkprivate.h"
+#include "gtksymboliccolor.h"
+#include "gtkanimationdescription.h"
+#include "gtktimeline.h"
+#include "gtkiconfactory.h"
+
+/**
+ * SECTION:gtkstylecontext
+ * @Short_description: Rendering UI elements
+ * @Title: GtkStyleContext
+ *
+ * #GtkStyleContext is an object that stores styling information affecting
+ * a widget defined by #GtkWidgetPath.
+ *
+ * In order to construct the final style information, #GtkStyleContext
+ * queries information from all attached #GtkStyleProviders. Style providers
+ * can be either attached explicitly to the context through
+ * gtk_style_context_add_provider(), or to the screen through
+ * gtk_style_context_add_provider_for_screen(). The resulting style is a
+ * combination of all provider's information in priority order.
+ *
+ * For GTK+ widgets, any #GtkStyleContext returned by
+ * gtk_widget_get_style_context() will already have a #GtkWidgetPath, a
+ * #GdkScreen and RTL/LTR information set, the style context will be also
+ * updated automatically if any of these settings change on the widget.
+ *
+ * If you are using are the theming layer standalone, you will need to set a
+ * widget path and a screen yourself to the created style context through
+ * gtk_style_context_set_path() and gtk_style_context_set_screen(), as well
+ * as updating the context yourself using gtk_style_context_invalidate()
+ * whenever any of the conditions change, such as a change in the
+ * #GtkSettings:gtk-theme-name setting or a hierarchy change in the rendered
+ * widget.
+ *
+ * <refsect2 id="gtkstylecontext-animations">
+ * <title>Transition animations</title>
+ * <para>
+ * #GtkStyleContext has built-in support for state change transitions.
+ * Note that these animations respect the #GtkSettings:gtk-enable-animations
+ * setting.
+ * </para>
+ * <para>
+ * For simple widgets where state changes affect the whole widget area,
+ * calling gtk_style_context_notify_state_change() with a %NULL region
+ * is sufficient to trigger the transition animation. And GTK+ already
+ * does that when gtk_widget_set_state() or gtk_widget_set_state_flags()
+ * are called.
+ * </para>
+ * <para>
+ * If a widget needs to declare several animatable regions (i.e. not
+ * affecting the whole widget area), its #GtkWidget::draw signal handler
+ * needs to wrap the render operations for the different regions with
+ * calls to gtk_style_context_push_animatable_region() and
+ * gtk_style_context_pop_animatable_region(). These functions take an
+ * identifier for the region which must be unique within the style context.
+ * For simple widgets with a fixed set of animatable regions, using an
+ * enumeration works well:
+ * </para>
+ * <example>
+ * <title>Using an enumeration to identify animatable regions</title>
+ * <programlisting>
+ * enum {
+ * REGION_ENTRY,
+ * REGION_BUTTON_UP,
+ * REGION_BUTTON_DOWN
+ * };
+ *
+ * ...
+ *
+ * gboolean
+ * spin_button_draw (GtkWidget *widget,
+ * cairo_t *cr)
+ * {
+ * GtkStyleContext *context;
+ *
+ * context = gtk_widget_get_style_context (widget);
+ *
+ * gtk_style_context_push_animatable_region (context,
+ * GUINT_TO_POINTER (REGION_ENTRY));
+ *
+ * gtk_render_background (cr, 0, 0, 100, 30);
+ * gtk_render_frame (cr, 0, 0, 100, 30);
+ *
+ * gtk_style_context_pop_animatable_region (context);
+ *
+ * ...
+ * }
+ * </programlisting>
+ * </example>
+ * <para>
+ * For complex widgets with an arbitrary number of animatable regions, it
+ * is up to the implementation to come up with a way to uniquely identify
+ * each animatable region. Using pointers to internal structs is one way
+ * to achieve this:
+ * </para>
+ * <example>
+ * <title>Using struct pointers to identify animatable regions</title>
+ * <programlisting>
+ * void
+ * notebook_draw_tab (GtkWidget *widget,
+ * NotebookPage *page,
+ * cairo_t *cr)
+ * {
+ * gtk_style_context_push_animatable_region (context, page);
+ * gtk_render_extension (cr, page->x, page->y, page->width, page->height);
+ * gtk_style_context_pop_animatable_region (context);
+ * }
+ * </programlisting>
+ * </example>
+ * <para>
+ * The widget also needs to notify the style context about a state change
+ * for a given animatable region so the animation is triggered.
+ * </para>
+ * <example>
+ * <title>Triggering a state change animation on a region</title>
+ * <programlisting>
+ * gboolean
+ * notebook_motion_notify (GtkWidget *widget,
+ * GdkEventMotion *event)
+ * {
+ * GtkStyleContext *context;
+ * NotebookPage *page;
+ *
+ * context = gtk_widget_get_style_context (widget);
+ * page = find_page_under_pointer (widget, event);
+ * gtk_style_context_notify_state_change (context,
+ * gtk_widget_get_window (widget),
+ * page,
+ * GTK_STATE_PRELIGHT,
+ * TRUE);
+ * ...
+ * }
+ * </programlisting>
+ * </example>
+ * <para>
+ * gtk_style_context_notify_state_change() accepts %NULL region IDs as a
+ * special value, in this case, the whole widget area will be updated
+ * by the animation.
+ * </para>
+ * </refsect2>
+ * <refsect2 id="gtkstylecontext-classes">
+ * <title>Style classes and regions</title>
+ * <para>
+ * Widgets can add style classes to their context, which can be used
+ * to associate different styles by class (see <xref linkend="gtkcssprovider-selectors"/>). Theme engines can also use style classes to vary their
+ * rendering. GTK+ has a number of predefined style classes:
+ * <informaltable>
+ * <tgroup cols="3">
+ * <thead>
+ * <row>
+ * <entry>Style class</entry>
+ * <entry>Macro</entry>
+ * <entry>Used by</entry>
+ * </row>
+ * </thead>
+ * <tbody>
+ * <row>
+ * <entry>button</entry>
+ * <entry>GTK_STYLE_CLASS_BUTTON</entry>
+ * <entry>#GtkButton, #GtkToggleButton, #GtkRadioButton, #GtkCheckButton</entry>
+ * </row>
+ * <row>
+ * <entry>default</entry>
+ * <entry>GTK_STYLE_CLASS_DEFAULT</entry>
+ * <entry>#GtkButton</entry>
+ * </row>
+ * <row>
+ * <entry>check</entry>
+ * <entry>GTK_STYLE_CLASS_CHECK</entry>
+ * <entry>#GtkCheckButton, #GtkCheckMenuItem, #GtkCellRendererToggle</entry>
+ * </row>
+ * <row>
+ * <entry>radio</entry>
+ * <entry>GTK_STYLE_CLASS_RADIO</entry>
+ * <entry>#GtkRadioButton, #GtkRadioMenuItem, #GtkCellRendererToggle</entry>
+ * </row>
+ * <row>
+ * <entry>arrow</entry>
+ * <entry>GTK_STYLE_CLASS_ARROW</entry>
+ * <entry>#GtkArrow</entry>
+ * </row>
+ * <row>
+ * <entry>calendar</entry>
+ * <entry>GTK_STYLE_CLASS_CALENDAR</entry>
+ * <entry>#GtkCalendar</entry>
+ * </row>
+ * <row>
+ * <entry>entry</entry>
+ * <entry>GTK_STYLE_CLASS_ENTRY</entry>
+ * <entry>#GtkEntry</entry>
+ * </row>
+ * <row>
+ * <entry>cell</entry>
+ * <entry>GTK_STYLE_CLASS_CELL</entry>
+ * <entry>#GtkCellRendererToggle</entry>
+ * </row>
+ * <row>
+ * <entry>menu</entry>
+ * <entry>GTK_STYLE_CLASS_MENU</entry>
+ * <entry>#GtkMenu, #GtkMenuItem, #GtkCheckMenuItem, #GtkRadioMenuItem</entry>
+ * </row>
+ * <row>
+ * <entry>expander</entry>
+ * <entry>GTK_STYLE_CLASS_EXPANDER</entry>
+ * <entry>#GtkExpander</entry>
+ * </row>
+ * <row>
+ * <entry>tooltip</entry>
+ * <entry>GTK_STYLE_CLASS_TOOLTIP</entry>
+ * <entry>#GtkTooltip</entry>
+ * </row>
+ * <row>
+ * <entry>frame</entry>
+ * <entry>GTK_STYLE_CLASS_FRAME</entry>
+ * <entry>#GtkFrame</entry>
+ * </row>
+ * <row>
+ * <entry>scrolled-window</entry>
+ * <entry></entry>
+ * <entry>#GtkScrolledWindow</entry>
+ * </row>
+ * <row>
+ * <entry>viewport</entry>
+ * <entry></entry>
+ * <entry>#GtkViewport</entry>
+ * </row>
+ * <row>
+ * <entry>trough</entry>
+ * <entry>GTK_STYLE_CLASS_TROUGH</entry>
+ * <entry>#GtkScrollbar, #GtkProgressBar, #GtkScale</entry>
+ * </row>
+ * <row>
+ * <entry>progressbar</entry>
+ * <entry>GTK_STYLE_CLASS_PROGRESSBAR</entry>
+ * <entry>#GtkProgressBar, #GtkCellRendererProgress</entry>
+ * </row>
+ * <row>
+ * <entry>slider</entry>
+ * <entry>GTK_STYLE_CLASS_SLIDER</entry>
+ * <entry>#GtkScrollbar, #GtkScale</entry>
+ * </row>
+ * <row>
+ * <entry>menuitem</entry>
+ * <entry>GTK_STYLE_CLASS_MENUITEM</entry>
+ * <entry>#GtkMenuItem</entry>
+ * </row>
+ * <row>
+ * <entry>popup</entry>
+ * <entry></entry>
+ * <entry>#GtkMenu</entry>
+ * </row>
+ * <row>
+ * <entry>accelerator</entry>
+ * <entry>GTK_STYLE_CLASS_ACCELERATOR</entry>
+ * <entry>#GtkAccelLabel</entry>
+ * </row>
+ * <row>
+ * <entry>menubar</entry>
+ * <entry>GTK_STYLE_CLASS_MENUBAR</entry>
+ * <entry>#GtkMenuBar</entry>
+ * </row>
+ * <row>
+ * <entry>toolbar</entry>
+ * <entry>GTK_STYLE_CLASS_TOOLBAR</entry>
+ * <entry>#GtkToolbar</entry>
+ * </row>
+ * <row>
+ * <entry>dock</entry>
+ * <entry>GTK_STYLE_CLASS_DOCK</entry>
+ * <entry>#GtkHandleBox</entry>
+ * </row>
+ * <row>
+ * <entry>notebook</entry>
+ * <entry></entry>
+ * <entry>#GtkNotebook</entry>
+ * </row>
+ * <row>
+ * <entry>background</entry>
+ * <entry>GTK_STYLE_CLASS_BACKGROUND</entry>
+ * <entry>#GtkWindow</entry>
+ * </row>
+ * <row>
+ * <entry>rubberband</entry>
+ * <entry>GTK_STYLE_CLASS_RUBBERBAND</entry>
+ * <entry></entry>
+ * </row>
+ * <row>
+ * <entry>header</entry>
+ * <entry>GTK_STYLE_CLASS_HEADER</entry>
+ * <entry></entry>
+ * </row>
+ * <row>
+ * <entry>grip</entry>
+ * <entry>GTK_STYLE_CLASS_GRIP</entry>
+ * <entry>#GtkWindow</entry>
+ * </row>
+ * <row>
+ * <entry>spinner</entry>
+ * <entry>GTK_STYLE_CLASS_SPINNER</entry>
+ * <entry>#GtkSpinner</entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ * </para>
+ * <para>
+ * Widgets can also add regions with flags to their context.
+ * The regions used by GTK+ widgets are:
+ * <informaltable>
+ * <tgroup cols="4">
+ * <thead>
+ * <row>
+ * <entry>Region</entry>
+ * <entry>Flags</entry>
+ * <entry>Macro</entry>
+ * <entry>Used by</entry>
+ * </row>
+ * </thead>
+ * <tbody>
+ * <row>
+ * <entry>row</entry>
+ * <entry>even, odd</entry>
+ * <entry>GTK_STYLE_REGION_ROW</entry>
+ * <entry>#GtkTreeView</entry>
+ * </row>
+ * <row>
+ * <entry>column</entry>
+ * <entry>first, last, sorted</entry>
+ * <entry>GTK_STYLE_REGION_COLUMN</entry>
+ * <entry>#GtkTreeView</entry>
+ * </row>
+ * <row>
+ * <entry>column-header</entry>
+ * <entry></entry>
+ * <entry>GTK_STYLE_REGION_COLUMN_HEADER</entry>
+ * <entry></entry>
+ * </row>
+ * <row>
+ * <entry>tab</entry>
+ * <entry>even, odd, first, last</entry>
+ * <entry>GTK_STYLE_REGION_TAB</entry>
+ * <entry>#GtkNotebook</entry>
+ * </row>
+ * </tbody>
+ * </tgroup>
+ * </informaltable>
+ * </para>
+ * </refsect2>
+ * <refsect2 id="gtkstylecontext-custom-styling">
+ * <title>Custom styling in UI libraries and applications</title>
+ * <para>
+ * If you are developing a library with custom #GtkWidget<!-- -->s that
+ * render differently than standard components, you may need to add a
+ * #GtkStyleProvider yourself with the %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK
+ * priority, either a #GtkCssProvider or a custom object implementing the
+ * #GtkStyleProvider interface. This way theming engines may still attempt
+ * to style your UI elements in a different way if needed so.
+ * </para>
+ * <para>
+ * If you are using custom styling on an applications, you probably want then
+ * to make your style information prevail to the theme's, so you must use
+ * a #GtkStyleProvider with the %GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
+ * priority, keep in mind that the user settings in
+ * <filename><replaceable>XDG_CONFIG_HOME</replaceable>/gtk-3.0/gtk.css</filename> will
+ * still take precedence over your changes, as it uses the
+ * %GTK_STYLE_PROVIDER_PRIORITY_USER priority.
+ * </para>
+ * <para>
+ * If a custom theming engine is needed, you probably want to implement a
+ * #GtkStyleProvider yourself so it points to your #GtkThemingEngine
+ * implementation, as #GtkCssProvider uses gtk_theming_engine_load()
+ * which loads the theming engine module from the standard paths.
+ * </para>
+ * </refsect2>
+ */
+
+typedef struct GtkStyleContextPrivate GtkStyleContextPrivate;
+typedef struct GtkStyleProviderData GtkStyleProviderData;
+typedef struct GtkStyleInfo GtkStyleInfo;
+typedef struct GtkRegion GtkRegion;
+typedef struct PropertyValue PropertyValue;
+typedef struct AnimationInfo AnimationInfo;
+typedef struct StyleData StyleData;
+
+struct GtkRegion
+{
+ GQuark class_quark;
+ GtkRegionFlags flags;
+};
+
+struct GtkStyleProviderData
+{
+ GtkStyleProvider *provider;
+ guint priority;
+};
+
+struct PropertyValue
+{
+ GType widget_type;
+ GParamSpec *pspec;
+ GtkStateFlags state;
+ GValue value;
+};
+
+struct GtkStyleInfo
+{
+ GArray *style_classes;
+ GArray *regions;
+ GtkJunctionSides junction_sides;
+ GtkStateFlags state_flags;
+};
+
+struct StyleData
+{
+ GtkStyleProperties *store;
+ GSList *icon_factories;
+ GArray *property_cache;
+};
+
+struct AnimationInfo
+{
+ GtkTimeline *timeline;
+
+ gpointer region_id;
+ GdkWindow *window;
+ GtkStateType state;
+ gboolean target_value;
+
+ cairo_region_t *invalidation_region;
+ GArray *rectangles;
+};
+
+struct GtkStyleContextPrivate
+{
+ GdkScreen *screen;
+
+ GList *providers;
+ GList *providers_last;
+
+ GtkWidgetPath *widget_path;
+ GHashTable *style_data;
+ GSList *info_stack;
+ StyleData *current_data;
+
+ GSList *animation_regions;
+ GSList *animations;
+
+ guint animations_invalidated : 1;
+ guint invalidating_context : 1;
+
+ GtkThemingEngine *theming_engine;
+
+ GtkTextDirection direction;
+};
+
+enum {
+ PROP_0,
+ PROP_SCREEN,
+ PROP_DIRECTION
+};
+
+enum {
+ CHANGED,
+ LAST_SIGNAL
+};
+
+guint signals[LAST_SIGNAL] = { 0 };
+
+static GQuark provider_list_quark = 0;
+
+static void gtk_style_context_finalize (GObject *object);
+
+static void gtk_style_context_impl_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_style_context_impl_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+
+G_DEFINE_TYPE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT)
+
+static void
+gtk_style_context_class_init (GtkStyleContextClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_style_context_finalize;
+ object_class->set_property = gtk_style_context_impl_set_property;
+ object_class->get_property = gtk_style_context_impl_get_property;
+
+ signals[CHANGED] =
+ g_signal_new (I_("changed"),
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkStyleContextClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_object_class_install_property (object_class,
+ PROP_SCREEN,
+ g_param_spec_object ("screen",
+ P_("Screen"),
+ P_("The associated GdkScreen"),
+ GDK_TYPE_SCREEN,
+ GTK_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_DIRECTION,
+ g_param_spec_enum ("direction",
+ P_("Direction"),
+ P_("Text direction"),
+ GTK_TYPE_TEXT_DIRECTION,
+ GTK_TEXT_DIR_LTR,
+ GTK_PARAM_READWRITE));
+
+ g_type_class_add_private (object_class, sizeof (GtkStyleContextPrivate));
+}
+
+static GtkStyleInfo *
+style_info_new (void)
+{
+ GtkStyleInfo *info;
+
+ info = g_slice_new0 (GtkStyleInfo);
+ info->style_classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
+ info->regions = g_array_new (FALSE, FALSE, sizeof (GtkRegion));
+
+ return info;
+}
+
+static void
+style_info_free (GtkStyleInfo *info)
+{
+ g_array_free (info->style_classes, TRUE);
+ g_array_free (info->regions, TRUE);
+ g_slice_free (GtkStyleInfo, info);
+}
+
+static GtkStyleInfo *
+style_info_copy (const GtkStyleInfo *info)
+{
+ GtkStyleInfo *copy;
+
+ copy = style_info_new ();
+ g_array_insert_vals (copy->style_classes, 0,
+ info->style_classes->data,
+ info->style_classes->len);
+
+ g_array_insert_vals (copy->regions, 0,
+ info->regions->data,
+ info->regions->len);
+
+ copy->junction_sides = info->junction_sides;
+ copy->state_flags = info->state_flags;
+
+ return copy;
+}
+
+static guint
+style_info_hash (gconstpointer elem)
+{
+ const GtkStyleInfo *info;
+ guint i, hash = 0;
+
+ info = elem;
+
+ for (i = 0; i < info->style_classes->len; i++)
+ {
+ hash += g_array_index (info->style_classes, GQuark, i);
+ hash <<= 5;
+ }
+
+ for (i = 0; i < info->regions->len; i++)
+ {
+ GtkRegion *region;
+
+ region = &g_array_index (info->regions, GtkRegion, i);
+ hash += region->class_quark;
+ hash += region->flags;
+ hash <<= 5;
+ }
+
+ return hash;
+}
+
+static gboolean
+style_info_equal (gconstpointer elem1,
+ gconstpointer elem2)
+{
+ const GtkStyleInfo *info1, *info2;
+
+ info1 = elem1;
+ info2 = elem2;
+
+ if (info1->junction_sides != info2->junction_sides)
+ return FALSE;
+
+ if (info1->style_classes->len != info2->style_classes->len)
+ return FALSE;
+
+ if (memcmp (info1->style_classes->data,
+ info2->style_classes->data,
+ info1->style_classes->len * sizeof (GQuark)) != 0)
+ return FALSE;
+
+ if (info1->regions->len != info2->regions->len)
+ return FALSE;
+
+ if (memcmp (info1->regions->data,
+ info2->regions->data,
+ info1->regions->len * sizeof (GtkRegion)) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static StyleData *
+style_data_new (void)
+{
+ StyleData *data;
+
+ data = g_slice_new0 (StyleData);
+ data->store = gtk_style_properties_new ();
+
+ return data;
+}
+
+static void
+clear_property_cache (StyleData *data)
+{
+ guint i;
+
+ if (!data->property_cache)
+ return;
+
+ for (i = 0; i < data->property_cache->len; i++)
+ {
+ PropertyValue *node = &g_array_index (data->property_cache, PropertyValue, i);
+
+ g_param_spec_unref (node->pspec);
+ g_value_unset (&node->value);
+ }
+
+ g_array_free (data->property_cache, TRUE);
+ data->property_cache = NULL;
+}
+
+static void
+style_data_free (StyleData *data)
+{
+ g_object_unref (data->store);
+ clear_property_cache (data);
+
+ g_slist_foreach (data->icon_factories, (GFunc) g_object_unref, NULL);
+ g_slist_free (data->icon_factories);
+
+ g_slice_free (StyleData, data);
+}
+
+static void
+gtk_style_context_init (GtkStyleContext *style_context)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+
+ priv = style_context->priv = G_TYPE_INSTANCE_GET_PRIVATE (style_context,
+ GTK_TYPE_STYLE_CONTEXT,
+ GtkStyleContextPrivate);
+
+ priv->style_data = g_hash_table_new_full (style_info_hash,
+ style_info_equal,
+ (GDestroyNotify) style_info_free,
+ (GDestroyNotify) style_data_free);
+ priv->theming_engine = g_object_ref ((gpointer) gtk_theming_engine_load (NULL));
+
+ priv->direction = GTK_TEXT_DIR_RTL;
+
+ priv->screen = gdk_screen_get_default ();
+
+ /* Create default info store */
+ info = style_info_new ();
+ priv->info_stack = g_slist_prepend (priv->info_stack, info);
+}
+
+static GtkStyleProviderData *
+style_provider_data_new (GtkStyleProvider *provider,
+ guint priority)
+{
+ GtkStyleProviderData *data;
+
+ data = g_slice_new (GtkStyleProviderData);
+ data->provider = g_object_ref (provider);
+ data->priority = priority;
+
+ return data;
+}
+
+static void
+style_provider_data_free (GtkStyleProviderData *data)
+{
+ g_object_unref (data->provider);
+ g_slice_free (GtkStyleProviderData, data);
+}
+
+static void
+animation_info_free (AnimationInfo *info)
+{
+ g_object_unref (info->timeline);
+ g_object_unref (info->window);
+
+ if (info->invalidation_region)
+ cairo_region_destroy (info->invalidation_region);
+
+ g_array_free (info->rectangles, TRUE);
+ g_slice_free (AnimationInfo, info);
+}
+
+static AnimationInfo *
+animation_info_lookup_by_timeline (GtkStyleContext *context,
+ GtkTimeline *timeline)
+{
+ GtkStyleContextPrivate *priv;
+ AnimationInfo *info;
+ GSList *l;
+
+ priv = context->priv;
+
+ for (l = priv->animations; l; l = l->next)
+ {
+ info = l->data;
+
+ if (info->timeline == timeline)
+ return info;
+ }
+
+ return NULL;
+}
+
+static void
+timeline_frame_cb (GtkTimeline *timeline,
+ gdouble progress,
+ gpointer user_data)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleContext *context;
+ AnimationInfo *info;
+
+ context = user_data;
+ priv = context->priv;
+ info = animation_info_lookup_by_timeline (context, timeline);
+
+ g_assert (info != NULL);
+
+ /* Cancel transition if window is gone */
+ if (gdk_window_is_destroyed (info->window) ||
+ !gdk_window_is_visible (info->window))
+ {
+ priv->animations = g_slist_remove (priv->animations, info);
+ animation_info_free (info);
+ return;
+ }
+
+ if (info->invalidation_region &&
+ !cairo_region_is_empty (info->invalidation_region))
+ gdk_window_invalidate_region (info->window, info->invalidation_region, TRUE);
+ else
+ gdk_window_invalidate_rect (info->window, NULL, TRUE);
+}
+
+static void
+timeline_finished_cb (GtkTimeline *timeline,
+ gpointer user_data)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleContext *context;
+ AnimationInfo *info;
+
+ context = user_data;
+ priv = context->priv;
+ info = animation_info_lookup_by_timeline (context, timeline);
+
+ g_assert (info != NULL);
+
+ priv->animations = g_slist_remove (priv->animations, info);
+
+ /* Invalidate one last time the area, so the final content is painted */
+ if (info->invalidation_region &&
+ !cairo_region_is_empty (info->invalidation_region))
+ gdk_window_invalidate_region (info->window, info->invalidation_region, TRUE);
+ else
+ gdk_window_invalidate_rect (info->window, NULL, TRUE);
+
+ animation_info_free (info);
+}
+
+static AnimationInfo *
+animation_info_new (GtkStyleContext *context,
+ gpointer region_id,
+ guint duration,
+ GtkTimelineProgressType progress_type,
+ gboolean loop,
+ GtkStateType state,
+ gboolean target_value,
+ GdkWindow *window)
+{
+ AnimationInfo *info;
+
+ info = g_slice_new0 (AnimationInfo);
+
+ info->rectangles = g_array_new (FALSE, FALSE, sizeof (cairo_rectangle_int_t));
+ info->timeline = gtk_timeline_new (duration);
+ info->window = g_object_ref (window);
+ info->state = state;
+ info->target_value = target_value;
+ info->region_id = region_id;
+
+ gtk_timeline_set_progress_type (info->timeline, progress_type);
+ gtk_timeline_set_loop (info->timeline, loop);
+
+ if (!loop && !target_value)
+ {
+ gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_BACKWARD);
+ gtk_timeline_rewind (info->timeline);
+ }
+
+ g_signal_connect (info->timeline, "frame",
+ G_CALLBACK (timeline_frame_cb), context);
+ g_signal_connect (info->timeline, "finished",
+ G_CALLBACK (timeline_finished_cb), context);
+
+ gtk_timeline_start (info->timeline);
+
+ return info;
+}
+
+static AnimationInfo *
+animation_info_lookup (GtkStyleContext *context,
+ gpointer region_id,
+ GtkStateType state)
+{
+ GtkStyleContextPrivate *priv;
+ GSList *l;
+
+ priv = context->priv;
+
+ for (l = priv->animations; l; l = l->next)
+ {
+ AnimationInfo *info;
+
+ info = l->data;
+
+ if (info->state == state &&
+ info->region_id == region_id)
+ return info;
+ }
+
+ return NULL;
+}
+
+static void
+gtk_style_context_finalize (GObject *object)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleContext *style_context;
+ GSList *l;
+
+ style_context = GTK_STYLE_CONTEXT (object);
+ priv = style_context->priv;
+
+ if (priv->widget_path)
+ gtk_widget_path_free (priv->widget_path);
+
+ g_hash_table_destroy (priv->style_data);
+
+ g_list_foreach (priv->providers, (GFunc) style_provider_data_free, NULL);
+ g_list_free (priv->providers);
+
+ g_slist_foreach (priv->info_stack, (GFunc) style_info_free, NULL);
+ g_slist_free (priv->info_stack);
+
+ g_slist_free (priv->animation_regions);
+
+ for (l = priv->animations; l; l = l->next)
+ animation_info_free ((AnimationInfo *) l->data);
+
+ g_slist_free (priv->animations);
+
+ if (priv->theming_engine)
+ g_object_unref (priv->theming_engine);
+
+ G_OBJECT_CLASS (gtk_style_context_parent_class)->finalize (object);
+}
+
+static void
+gtk_style_context_impl_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleContext *style_context;
+
+ style_context = GTK_STYLE_CONTEXT (object);
+ priv = style_context->priv;
+
+ switch (prop_id)
+ {
+ case PROP_SCREEN:
+ gtk_style_context_set_screen (style_context,
+ g_value_get_object (value));
+ break;
+ case PROP_DIRECTION:
+ gtk_style_context_set_direction (style_context,
+ g_value_get_enum (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_style_context_impl_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleContext *style_context;
+
+ style_context = GTK_STYLE_CONTEXT (object);
+ priv = style_context->priv;
+
+ switch (prop_id)
+ {
+ case PROP_SCREEN:
+ g_value_set_object (value, priv->screen);
+ break;
+ case PROP_DIRECTION:
+ g_value_set_enum (value, priv->direction);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GList *
+find_next_candidate (GList *local,
+ GList *global,
+ gboolean ascending)
+{
+ if (local && global)
+ {
+ GtkStyleProviderData *local_data, *global_data;
+
+ local_data = local->data;
+ global_data = global->data;
+
+ if (local_data->priority < global_data->priority)
+ return (ascending) ? local : global;
+ else
+ return (ascending) ? global : local;
+ }
+ else if (local)
+ return local;
+ else if (global)
+ return global;
+
+ return NULL;
+}
+
+static void
+build_properties (GtkStyleContext *context,
+ StyleData *style_data,
+ GtkWidgetPath *path)
+{
+ GtkStyleContextPrivate *priv;
+ GList *elem, *list, *global_list = NULL;
+
+ priv = context->priv;
+ list = priv->providers;
+
+ if (priv->screen)
+ global_list = g_object_get_qdata (G_OBJECT (priv->screen), provider_list_quark);
+
+ while ((elem = find_next_candidate (list, global_list, TRUE)) != NULL)
+ {
+ GtkStyleProviderData *data;
+ GtkStyleProperties *provider_style;
+
+ data = elem->data;
+
+ if (elem == list)
+ list = list->next;
+ else
+ global_list = global_list->next;
+
+ provider_style = gtk_style_provider_get_style (data->provider, path);
+
+ if (provider_style)
+ {
+ gtk_style_properties_merge (style_data->store, provider_style, TRUE);
+ g_object_unref (provider_style);
+ }
+ }
+}
+
+static void
+build_icon_factories (GtkStyleContext *context,
+ StyleData *style_data,
+ GtkWidgetPath *path)
+{
+ GtkStyleContextPrivate *priv;
+ GList *elem, *list, *global_list = NULL;
+
+ priv = context->priv;
+ list = priv->providers_last;
+
+ if (priv->screen)
+ {
+ global_list = g_object_get_qdata (G_OBJECT (priv->screen), provider_list_quark);
+ global_list = g_list_last (global_list);
+ }
+
+ while ((elem = find_next_candidate (list, global_list, FALSE)) != NULL)
+ {
+ GtkIconFactory *factory;
+ GtkStyleProviderData *data;
+
+ data = elem->data;
+
+ if (elem == list)
+ list = list->prev;
+ else
+ global_list = global_list->prev;
+
+ factory = gtk_style_provider_get_icon_factory (data->provider, path);
+
+ if (factory)
+ style_data->icon_factories = g_slist_prepend (style_data->icon_factories, factory);
+ }
+}
+
+GtkWidgetPath *
+create_query_path (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+ GtkWidgetPath *path;
+ GtkStyleInfo *info;
+ guint i, pos;
+
+ priv = context->priv;
+ path = gtk_widget_path_copy (priv->widget_path);
+ pos = gtk_widget_path_length (path) - 1;
+
+ info = priv->info_stack->data;
+
+ /* Set widget regions */
+ for (i = 0; i < info->regions->len; i++)
+ {
+ GtkRegion *region;
+
+ region = &g_array_index (info->regions, GtkRegion, i);
+ gtk_widget_path_iter_add_region (path, pos,
+ g_quark_to_string (region->class_quark),
+ region->flags);
+ }
+
+ /* Set widget classes */
+ for (i = 0; i < info->style_classes->len; i++)
+ {
+ GQuark quark;
+
+ quark = g_array_index (info->style_classes, GQuark, i);
+ gtk_widget_path_iter_add_class (path, pos,
+ g_quark_to_string (quark));
+ }
+
+ return path;
+}
+
+static StyleData *
+style_data_lookup (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+
+ priv = context->priv;
+
+ /* Current data in use is cached, just return it */
+ if (priv->current_data)
+ return priv->current_data;
+
+ g_assert (priv->widget_path != NULL);
+
+ data = g_hash_table_lookup (priv->style_data, priv->info_stack->data);
+
+ if (!data)
+ {
+ GtkWidgetPath *path;
+
+ data = style_data_new ();
+ path = create_query_path (context);
+
+ build_properties (context, data, path);
+ build_icon_factories (context, data, path);
+
+ g_hash_table_insert (priv->style_data,
+ style_info_copy (priv->info_stack->data),
+ data);
+
+ gtk_widget_path_free (path);
+ }
+
+ priv->current_data = data;
+
+ if (priv->theming_engine)
+ g_object_unref (priv->theming_engine);
+
+ gtk_style_properties_get (data->store, 0,
+ "engine", &priv->theming_engine,
+ NULL);
+ return data;
+}
+
+static void
+style_provider_add (GList **list,
+ GtkStyleProvider *provider,
+ guint priority)
+{
+ GtkStyleProviderData *new_data;
+ gboolean added = FALSE;
+ GList *l = *list;
+
+ new_data = style_provider_data_new (provider, priority);
+
+ while (l)
+ {
+ GtkStyleProviderData *data;
+
+ data = l->data;
+
+ /* Provider was already attached to the style
+ * context, remove in order to add the new data
+ */
+ if (data->provider == provider)
+ {
+ GList *link;
+
+ link = l;
+ l = l->next;
+
+ /* Remove and free link */
+ *list = g_list_remove_link (*list, link);
+ style_provider_data_free (link->data);
+ g_list_free_1 (link);
+
+ continue;
+ }
+
+ if (!added &&
+ data->priority > priority)
+ {
+ *list = g_list_insert_before (*list, l, new_data);
+ added = TRUE;
+ }
+
+ l = l->next;
+ }
+
+ if (!added)
+ *list = g_list_append (*list, new_data);
+}
+
+static gboolean
+style_provider_remove (GList **list,
+ GtkStyleProvider *provider)
+{
+ GList *l = *list;
+
+ while (l)
+ {
+ GtkStyleProviderData *data;
+
+ data = l->data;
+
+ if (data->provider == provider)
+ {
+ *list = g_list_remove_link (*list, l);
+ style_provider_data_free (l->data);
+ g_list_free_1 (l);
+
+ return TRUE;
+ }
+
+ l = l->next;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gtk_style_context_new:
+ *
+ * Creates a standalone #GtkStyleContext, this style context
+ * won't be attached to any widget, so you may want
+ * to call gtk_style_context_set_path() yourself.
+ *
+ * <note>
+ * This function is only useful when using the theming layer
+ * separated from GTK+, if you are using #GtkStyleContext to
+ * theme #GtkWidget<!-- -->s, use gtk_widget_get_style_context()
+ * in order to get a style context ready to theme the widget.
+ * </note>
+ *
+ * Returns: A newly created #GtkStyleContext.
+ **/
+GtkStyleContext *
+gtk_style_context_new (void)
+{
+ return g_object_new (GTK_TYPE_STYLE_CONTEXT, NULL);
+}
+
+/**
+ * gtk_style_context_add_provider:
+ * @context: a #GtkStyleContext
+ * @provider: a #GtkStyleProvider
+ * @priority: the priority of the style provider. The lower
+ * it is, the earlier it will be used in the style
+ * construction. Typically this will be in the range
+ * between %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK and
+ * %GTK_STYLE_PROVIDER_PRIORITY_USER
+ *
+ * Adds a style provider to @context, to be used in style construction.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_add_provider (GtkStyleContext *context,
+ GtkStyleProvider *provider,
+ guint priority)
+{
+ GtkStyleContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
+
+ priv = context->priv;
+ style_provider_add (&priv->providers, provider, priority);
+ priv->providers_last = g_list_last (priv->providers);
+
+ gtk_style_context_invalidate (context);
+}
+
+/**
+ * gtk_style_context_remove_provider:
+ * @context: a #GtkStyleContext
+ * @provider: a #GtkStyleProvider
+ *
+ * Removes @provider from the style providers list in @context.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_remove_provider (GtkStyleContext *context,
+ GtkStyleProvider *provider)
+{
+ GtkStyleContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
+
+ priv = context->priv;
+
+ if (style_provider_remove (&priv->providers, provider))
+ {
+ priv->providers_last = g_list_last (priv->providers);
+
+ gtk_style_context_invalidate (context);
+ }
+}
+
+/**
+ * gtk_style_context_reset_widgets:
+ * @screen: a #GdkScreen
+ *
+ * This function recomputes the styles for all widgets under a particular
+ * #GdkScreen. This is useful when some global parameter has changed that
+ * affects the appearance of all widgets, because when a widget gets a new
+ * style, it will both redraw and recompute any cached information about
+ * its appearance. As an example, it is used when the color scheme changes
+ * in the related #GtkSettings object.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_reset_widgets (GdkScreen *screen)
+{
+ GList *list, *toplevels;
+
+ _gtk_icon_set_invalidate_caches ();
+
+ toplevels = gtk_window_list_toplevels ();
+ g_list_foreach (toplevels, (GFunc) g_object_ref, NULL);
+
+ for (list = toplevels; list; list = list->next)
+ {
+ if (gtk_widget_get_screen (list->data) == screen)
+ gtk_widget_reset_style (list->data);
+
+ g_object_unref (list->data);
+ }
+
+ g_list_free (toplevels);
+}
+
+/**
+ * gtk_style_context_add_provider_for_screen:
+ * @screen: a #GdkScreen
+ * @provider: a #GtkStyleProvider
+ * @priority: the priority of the style provider. The lower
+ * it is, the earlier it will be used in the style
+ * construction. Typically this will be in the range
+ * between %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK and
+ * %GTK_STYLE_PROVIDER_PRIORITY_USER
+ *
+ * Adds a global style provider to @screen, which will be used
+ * in style construction for all #GtkStyleContext<!-- -->s under
+ * @screen.
+ *
+ * GTK+ uses this to make styling information from #GtkSettings
+ * available.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_add_provider_for_screen (GdkScreen *screen,
+ GtkStyleProvider *provider,
+ guint priority)
+{
+ GList *providers, *list;
+
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+ g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
+
+ if (G_UNLIKELY (!provider_list_quark))
+ provider_list_quark = g_quark_from_static_string ("gtk-provider-list-quark");
+
+ list = providers = g_object_get_qdata (G_OBJECT (screen), provider_list_quark);
+ style_provider_add (&list, provider, priority);
+
+ if (list != providers)
+ g_object_set_qdata (G_OBJECT (screen), provider_list_quark, list);
+
+ gtk_style_context_reset_widgets (screen);
+}
+
+/**
+ * gtk_style_context_remove_provider_for_screen:
+ * @screen: a #GdkScreen
+ * @provider: a #GtkStyleProvider
+ *
+ * Removes @provider from the global style providers list in @screen.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_remove_provider_for_screen (GdkScreen *screen,
+ GtkStyleProvider *provider)
+{
+ GList *providers, *list;
+
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+ g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
+
+ if (G_UNLIKELY (!provider_list_quark))
+ return;
+
+ list = providers = g_object_get_qdata (G_OBJECT (screen), provider_list_quark);
+
+ if (style_provider_remove (&list, provider))
+ {
+ if (list != providers)
+ g_object_set_qdata (G_OBJECT (screen), provider_list_quark, list);
+
+ gtk_style_context_reset_widgets (screen);
+ }
+}
+
+/**
+ * gtk_style_context_get_property:
+ * @context: a #GtkStyleContext
+ * @property: style property name
+ * @state: state to retrieve the property value for
+ * @value: (out) (transfer full): return location for the style property value
+ *
+ * Gets a style property from @context for the given state.
+ *
+ * When @value is no longer needed, g_value_unset() must be called
+ * to free any allocated memory.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_property (GtkStyleContext *context,
+ const gchar *property,
+ GtkStateFlags state,
+ GValue *value)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (property != NULL);
+ g_return_if_fail (value != NULL);
+
+ priv = context->priv;
+
+ g_return_if_fail (priv->widget_path != NULL);
+
+ data = style_data_lookup (context);
+ gtk_style_properties_get_property (data->store, property, state, value);
+}
+
+/**
+ * gtk_style_context_get_valist:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the property values for
+ * @args: va_list of property name/return location pairs, followed by %NULL
+ *
+ * Retrieves several style property values from @context for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_valist (GtkStyleContext *context,
+ GtkStateFlags state,
+ va_list args)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ g_return_if_fail (priv->widget_path != NULL);
+
+ data = style_data_lookup (context);
+ gtk_style_properties_get_valist (data->store, state, args);
+}
+
+/**
+ * gtk_style_context_get:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the property values for
+ * @...: property name /return value pairs, followed by %NULL
+ *
+ * Retrieves several style property values from @context for a
+ * given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get (GtkStyleContext *context,
+ GtkStateFlags state,
+ ...)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+ va_list args;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ g_return_if_fail (priv->widget_path != NULL);
+
+ data = style_data_lookup (context);
+
+ va_start (args, state);
+ gtk_style_properties_get_valist (data->store, state, args);
+ va_end (args);
+}
+
+/**
+ * gtk_style_context_set_state:
+ * @context: a #GtkStyleContext
+ * @flags: state to represent
+ *
+ * Sets the state to be used when rendering with any
+ * of the gtk_render_*() functions.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_set_state (GtkStyleContext *context,
+ GtkStateFlags flags)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ info = priv->info_stack->data;
+ info->state_flags = flags;
+}
+
+/**
+ * gtk_style_context_get_state:
+ * @context: a #GtkStyleContext
+ *
+ * Returns the state used when rendering.
+ *
+ * Returns: the state flags
+ *
+ * Since: 3.0
+ **/
+GtkStateFlags
+gtk_style_context_get_state (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
+
+ priv = context->priv;
+ info = priv->info_stack->data;
+
+ return info->state_flags;
+}
+
+static gboolean
+context_has_animatable_region (GtkStyleContext *context,
+ gpointer region_id)
+{
+ GtkStyleContextPrivate *priv;
+ GSList *r;
+
+ /* NULL region_id means everything
+ * rendered through the style context
+ */
+ if (!region_id)
+ return TRUE;
+
+ priv = context->priv;
+
+ for (r = priv->animation_regions; r; r = r->next)
+ {
+ if (r->data == region_id)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gtk_style_context_state_is_running:
+ * @context: a #GtkStyleContext
+ * @state: a widget state
+ * @progress: (out): return location for the transition progress
+ *
+ * Returns %TRUE if there is a transition animation running for the
+ * current region (see gtk_style_context_push_animatable_region()).
+ *
+ * If @progress is not %NULL, the animation progress will be returned
+ * there, 0.0 means the state is closest to being unset, while 1.0 means
+ * it's closest to being set. This means transition animation will
+ * run from 0 to 1 when @state is being set and from 1 to 0 when
+ * it's being unset.
+ *
+ * Returns: %TRUE if there is a running transition animation for @state.
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_style_context_state_is_running (GtkStyleContext *context,
+ GtkStateType state,
+ gdouble *progress)
+{
+ GtkStyleContextPrivate *priv;
+ AnimationInfo *info;
+ GSList *l;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
+
+ priv = context->priv;
+
+ for (l = priv->animations; l; l = l->next)
+ {
+ info = l->data;
+
+ if (info->state == state &&
+ context_has_animatable_region (context, info->region_id))
+ {
+ if (progress)
+ *progress = gtk_timeline_get_progress (info->timeline);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * gtk_style_context_set_path:
+ * @context: a #GtkStyleContext
+ * @path: a #GtkWidgetPath
+ *
+ * Sets the #GtkWidgetPath used for style matching. As a
+ * consequence, the style will be regenerated to match
+ * the new given path.
+ *
+ * If you are using a #GtkStyleContext returned from
+ * gtk_widget_get_style_context(), you do not need to call
+ * this yourself.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_set_path (GtkStyleContext *context,
+ GtkWidgetPath *path)
+{
+ GtkStyleContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (path != NULL);
+
+ priv = context->priv;
+
+ if (priv->widget_path)
+ {
+ gtk_widget_path_free (priv->widget_path);
+ priv->widget_path = NULL;
+ }
+
+ if (path)
+ priv->widget_path = gtk_widget_path_copy (path);
+
+ gtk_style_context_invalidate (context);
+}
+
+/**
+ * gtk_style_context_get_path:
+ * @context: a #GtkStyleContext
+ *
+ * Returns the widget path used for style matching.
+ *
+ * Returns: (transfer none): A #GtkWidgetPath
+ *
+ * Since: 3.0
+ **/
+G_CONST_RETURN GtkWidgetPath *
+gtk_style_context_get_path (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+
+ priv = context->priv;
+ return priv->widget_path;
+}
+
+/**
+ * gtk_style_context_save:
+ * @context: a #GtkStyleContext
+ *
+ * Saves the @context state, so all modifications done through
+ * gtk_style_context_add_class(), gtk_style_context_remove_class(),
+ * gtk_style_context_add_region(), gtk_style_context_remove_region()
+ * or gtk_style_context_set_junction_sides() can be reverted in one
+ * go through gtk_style_context_restore().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_save (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+
+ g_assert (priv->info_stack != NULL);
+
+ info = style_info_copy (priv->info_stack->data);
+ priv->info_stack = g_slist_prepend (priv->info_stack, info);
+}
+
+/**
+ * gtk_style_context_restore:
+ * @context: a #GtkStyleContext
+ *
+ * Restores @context state to a previous stage.
+ * See gtk_style_context_save().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_restore (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+
+ if (priv->info_stack)
+ {
+ info = priv->info_stack->data;
+ priv->info_stack = g_slist_remove (priv->info_stack, info);
+ style_info_free (info);
+ }
+
+ if (!priv->info_stack)
+ {
+ g_warning ("Unpaired gtk_style_context_restore() call");
+
+ /* Create default region */
+ info = style_info_new ();
+ priv->info_stack = g_slist_prepend (priv->info_stack, info);
+ }
+
+ priv->current_data = NULL;
+}
+
+static gboolean
+style_class_find (GArray *array,
+ GQuark class_quark,
+ guint *position)
+{
+ gint min, max, mid;
+ gboolean found = FALSE;
+ guint pos;
+
+ if (position)
+ *position = 0;
+
+ if (!array || array->len == 0)
+ return FALSE;
+
+ min = 0;
+ max = array->len - 1;
+
+ do
+ {
+ GQuark item;
+
+ mid = (min + max) / 2;
+ item = g_array_index (array, GQuark, mid);
+
+ if (class_quark == item)
+ {
+ found = TRUE;
+ pos = mid;
+ }
+ else if (class_quark > item)
+ min = pos = mid + 1;
+ else
+ {
+ max = mid - 1;
+ pos = mid;
+ }
+ }
+ while (!found && min <= max);
+
+ if (position)
+ *position = pos;
+
+ return found;
+}
+
+static gboolean
+region_find (GArray *array,
+ GQuark class_quark,
+ guint *position)
+{
+ gint min, max, mid;
+ gboolean found = FALSE;
+ guint pos;
+
+ if (position)
+ *position = 0;
+
+ if (!array || array->len == 0)
+ return FALSE;
+
+ min = 0;
+ max = array->len - 1;
+
+ do
+ {
+ GtkRegion *region;
+
+ mid = (min + max) / 2;
+ region = &g_array_index (array, GtkRegion, mid);
+
+ if (region->class_quark == class_quark)
+ {
+ found = TRUE;
+ pos = mid;
+ }
+ else if (region->class_quark > class_quark)
+ min = pos = mid + 1;
+ else
+ {
+ max = mid - 1;
+ pos = mid;
+ }
+ }
+ while (!found && min <= max);
+
+ if (position)
+ *position = pos;
+
+ return found;
+}
+
+/**
+ * gtk_style_context_add_class:
+ * @context: a #GtkStyleContext
+ * @class_name: class name to use in styling
+ *
+ * Adds a style class to @context, so posterior calls to
+ * gtk_style_context_get() or any of the gtk_render_*()
+ * functions will make use of this new class for styling.
+ *
+ * In the CSS file format, a #GtkEntry defining an "entry"
+ * class, would be matched by:
+ *
+ * <programlisting>
+ * GtkEntry.entry { ... }
+ * </programlisting>
+ *
+ * While any widget defining an "entry" class would be
+ * matched by:
+ * <programlisting>
+ * .entry { ... }
+ * </programlisting>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_add_class (GtkStyleContext *context,
+ const gchar *class_name)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+ GQuark class_quark;
+ guint position;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (class_name != NULL);
+
+ priv = context->priv;
+ class_quark = g_quark_from_string (class_name);
+
+ g_assert (priv->info_stack != NULL);
+ info = priv->info_stack->data;
+
+ if (!style_class_find (info->style_classes, class_quark, &position))
+ {
+ g_array_insert_val (info->style_classes, position, class_quark);
+
+ /* Unset current data, as it likely changed due to the class change */
+ priv->current_data = NULL;
+ }
+}
+
+/**
+ * gtk_style_context_remove_class:
+ * @context: a #GtkStyleContext
+ * @class_name: class name to remove
+ *
+ * Removes @class_name from @context.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_remove_class (GtkStyleContext *context,
+ const gchar *class_name)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+ GQuark class_quark;
+ guint position;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (class_name != NULL);
+
+ class_quark = g_quark_try_string (class_name);
+
+ if (!class_quark)
+ return;
+
+ priv = context->priv;
+
+ g_assert (priv->info_stack != NULL);
+ info = priv->info_stack->data;
+
+ if (style_class_find (info->style_classes, class_quark, &position))
+ {
+ g_array_remove_index (info->style_classes, position);
+
+ /* Unset current data, as it likely changed due to the class change */
+ priv->current_data = NULL;
+ }
+}
+
+/**
+ * gtk_style_context_has_class:
+ * @context: a #GtkStyleContext
+ * @class_name: a class name
+ *
+ * Returns %TRUE if @context currently has defined the
+ * given class name
+ *
+ * Returns: %TRUE if @context has @class_name defined
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_style_context_has_class (GtkStyleContext *context,
+ const gchar *class_name)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+ GQuark class_quark;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
+ g_return_val_if_fail (class_name != NULL, FALSE);
+
+ class_quark = g_quark_try_string (class_name);
+
+ if (!class_quark)
+ return FALSE;
+
+ priv = context->priv;
+
+ g_assert (priv->info_stack != NULL);
+ info = priv->info_stack->data;
+
+ if (style_class_find (info->style_classes, class_quark, NULL))
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * gtk_style_context_list_classes:
+ * @context: a #GtkStyleContext
+ *
+ * Returns the list of classes currently defined in @context.
+ *
+ * Returns: (transfer container) (element-type utf8): a #GList of
+ * strings with the currently defined classes. The contents
+ * of the list are owned by GTK+, but you must free the list
+ * itself with g_list_free() when you are done with it.
+ *
+ * Since: 3.0
+ **/
+GList *
+gtk_style_context_list_classes (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+ GList *classes = NULL;
+ guint i;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+
+ priv = context->priv;
+
+ g_assert (priv->info_stack != NULL);
+ info = priv->info_stack->data;
+
+ for (i = 0; i < info->style_classes->len; i++)
+ {
+ GQuark quark;
+
+ quark = g_array_index (info->style_classes, GQuark, i);
+ classes = g_list_prepend (classes, (gchar *) g_quark_to_string (quark));
+ }
+
+ return classes;
+}
+
+/**
+ * gtk_style_context_list_regions:
+ * @context: a #GtkStyleContext
+ *
+ * Returns the list of regions currently defined in @context.
+ *
+ * Returns: (transfer container) (element-type utf8): a #GList of
+ * strings with the currently defined regions. The contents
+ * of the list are owned by GTK+, but you must free the list
+ * itself with g_list_free() when you are done with it.
+ *
+ * Since: 3.0
+ **/
+GList *
+gtk_style_context_list_regions (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+ GList *classes = NULL;
+ guint i;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+
+ priv = context->priv;
+
+ g_assert (priv->info_stack != NULL);
+ info = priv->info_stack->data;
+
+ for (i = 0; i < info->regions->len; i++)
+ {
+ GtkRegion *region;
+ const gchar *class_name;
+
+ region = &g_array_index (info->regions, GtkRegion, i);
+
+ class_name = g_quark_to_string (region->class_quark);
+ classes = g_list_prepend (classes, (gchar *) class_name);
+ }
+
+ return classes;
+}
+
+/**
+ * gtk_style_context_add_region:
+ * @context: a #GtkStyleContext
+ * @region_name: region name to use in styling
+ * @flags: flags that apply to the region
+ *
+ * Adds a region to @context, so posterior calls to
+ * gtk_style_context_get() or any of the gtk_render_*()
+ * functions will make use of this new region for styling.
+ *
+ * In the CSS file format, a #GtkTreeView defining a "row"
+ * region, would be matched by:
+ *
+ * <programlisting>
+ * GtkTreeView row { ... }
+ * </programlisting>
+ *
+ * Pseudo-classes are used for matching @flags, so the two
+ * following rules:
+ * <programlisting>
+ * GtkTreeView row:nth-child (even) { ... }
+ * GtkTreeView row:nth-child (odd) { ... }
+ * </programlisting>
+ *
+ * would apply to even and odd rows, respectively.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_add_region (GtkStyleContext *context,
+ const gchar *region_name,
+ GtkRegionFlags flags)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+ GQuark region_quark;
+ guint position;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (region_name != NULL);
+
+ priv = context->priv;
+ region_quark = g_quark_from_string (region_name);
+
+ g_assert (priv->info_stack != NULL);
+ info = priv->info_stack->data;
+
+ if (!region_find (info->regions, region_quark, &position))
+ {
+ GtkRegion region;
+
+ region.class_quark = region_quark;
+ region.flags = flags;
+
+ g_array_insert_val (info->regions, position, region);
+
+ /* Unset current data, as it likely changed due to the region change */
+ priv->current_data = NULL;
+ }
+}
+
+/**
+ * gtk_style_context_remove_region:
+ * @context: a #GtkStyleContext
+ * @region_name: region name to unset
+ *
+ * Removes a region from @context.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_remove_region (GtkStyleContext *context,
+ const gchar *region_name)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+ GQuark region_quark;
+ guint position;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (region_name != NULL);
+
+ region_quark = g_quark_try_string (region_name);
+
+ if (!region_quark)
+ return;
+
+ priv = context->priv;
+
+ g_assert (priv->info_stack != NULL);
+ info = priv->info_stack->data;
+
+ if (region_find (info->regions, region_quark, &position))
+ {
+ g_array_remove_index (info->regions, position);
+
+ /* Unset current data, as it likely changed due to the region change */
+ priv->current_data = NULL;
+ }
+}
+
+/**
+ * gtk_style_context_has_region:
+ * @context: a #GtkStyleContext
+ * @region_name: a region name
+ * @flags_return: (out) (allow-none): return location for region flags
+ *
+ * Returns %TRUE if @context has the region defined.
+ * If @flags_return is not %NULL, it is set to the flags
+ * affecting the region.
+ *
+ * Returns: %TRUE if region is defined
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_style_context_has_region (GtkStyleContext *context,
+ const gchar *region_name,
+ GtkRegionFlags *flags_return)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+ GQuark region_quark;
+ guint position;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
+ g_return_val_if_fail (region_name != NULL, FALSE);
+
+ if (flags_return)
+ *flags_return = 0;
+
+ region_quark = g_quark_try_string (region_name);
+
+ if (!region_quark)
+ return FALSE;
+
+ priv = context->priv;
+
+ g_assert (priv->info_stack != NULL);
+ info = priv->info_stack->data;
+
+ if (region_find (info->regions, region_quark, &position))
+ {
+ if (flags_return)
+ {
+ GtkRegion *region;
+
+ region = &g_array_index (info->regions, GtkRegion, position);
+ *flags_return = region->flags;
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint
+style_property_values_cmp (gconstpointer bsearch_node1,
+ gconstpointer bsearch_node2)
+{
+ const PropertyValue *val1 = bsearch_node1;
+ const PropertyValue *val2 = bsearch_node2;
+
+ if (val1->widget_type != val2->widget_type)
+ return val1->widget_type < val2->widget_type ? -1 : 1;
+
+ if (val1->pspec != val2->pspec)
+ return val1->pspec < val2->pspec ? -1 : 1;
+
+ if (val1->state != val2->state)
+ return val1->state < val2->state ? -1 : 1;
+
+ return 0;
+}
+
+const GValue *
+_gtk_style_context_peek_style_property (GtkStyleContext *context,
+ GType widget_type,
+ GtkStateFlags state,
+ GParamSpec *pspec)
+{
+ GtkStyleContextPrivate *priv;
+ PropertyValue *pcache, key = { 0 };
+ GList *global_list = NULL;
+ StyleData *data;
+ guint i;
+
+ priv = context->priv;
+ data = style_data_lookup (context);
+
+ key.widget_type = widget_type;
+ key.state = state;
+ key.pspec = pspec;
+
+ /* need value cache array */
+ if (!data->property_cache)
+ data->property_cache = g_array_new (FALSE, FALSE, sizeof (PropertyValue));
+ else
+ {
+ pcache = bsearch (&key,
+ data->property_cache->data, data->property_cache->len,
+ sizeof (PropertyValue), style_property_values_cmp);
+ if (pcache)
+ return &pcache->value;
+ }
+
+ i = 0;
+ while (i < data->property_cache->len &&
+ style_property_values_cmp (&key, &g_array_index (data->property_cache, PropertyValue, i)) >= 0)
+ i++;
+
+ g_array_insert_val (data->property_cache, i, key);
+ pcache = &g_array_index (data->property_cache, PropertyValue, i);
+
+ /* cache miss, initialize value type, then set contents */
+ g_param_spec_ref (pcache->pspec);
+ g_value_init (&pcache->value, G_PARAM_SPEC_VALUE_TYPE (pspec));
+
+ if (priv->screen)
+ {
+ global_list = g_object_get_qdata (G_OBJECT (priv->screen), provider_list_quark);
+ global_list = g_list_last (global_list);
+ }
+
+ if (priv->widget_path)
+ {
+ GList *list, *global, *elem;
+
+ list = priv->providers_last;
+ global = global_list;
+
+ while ((elem = find_next_candidate (list, global, FALSE)) != NULL)
+ {
+ GtkStyleProviderData *provider_data;
+
+ provider_data = elem->data;
+
+ if (elem == list)
+ list = list->prev;
+ else
+ global = global->prev;
+
+ if (gtk_style_provider_get_style_property (provider_data->provider,
+ priv->widget_path, state,
+ pspec, &pcache->value))
+ {
+ /* Resolve symbolic colors to GdkColor/GdkRGBA */
+ if (G_VALUE_TYPE (&pcache->value) == GTK_TYPE_SYMBOLIC_COLOR)
+ {
+ GtkSymbolicColor *color;
+ GdkRGBA rgba;
+
+ color = g_value_get_boxed (&pcache->value);
+
+ if (gtk_symbolic_color_resolve (color, data->store, &rgba))
+ {
+ g_value_unset (&pcache->value);
+
+ if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_RGBA)
+ {
+ g_value_init (&pcache->value, GDK_TYPE_RGBA);
+ g_value_set_boxed (&pcache->value, &rgba);
+ }
+ else
+ {
+ GdkColor rgb;
+
+ rgb.red = rgba.red * 65535. + 0.5;
+ rgb.green = rgba.green * 65535. + 0.5;
+ rgb.blue = rgba.blue * 65535. + 0.5;
+
+ g_value_init (&pcache->value, GDK_TYPE_COLOR);
+ g_value_set_boxed (&pcache->value, &rgb);
+ }
+ }
+ else
+ g_param_value_set_default (pspec, &pcache->value);
+ }
+
+ return &pcache->value;
+ }
+ }
+ }
+
+ /* not supplied by any provider, revert to default */
+ g_param_value_set_default (pspec, &pcache->value);
+
+ return &pcache->value;
+}
+
+/**
+ * gtk_style_context_get_style_property:
+ * @context: a #GtkStyleContext
+ * @property_name: the name of the widget style property
+ * @value: (out) (transfer full): Return location for the property value
+ *
+ * Gets the value for a widget style property.
+ *
+ * When @value is no longer needed, g_value_unset() must be called
+ * to free any allocated memory.
+ **/
+void
+gtk_style_context_get_style_property (GtkStyleContext *context,
+ const gchar *property_name,
+ GValue *value)
+{
+ GtkStyleContextPrivate *priv;
+ GtkWidgetClass *widget_class;
+ GtkStateFlags state;
+ GParamSpec *pspec;
+ const GValue *peek_value;
+ GType widget_type;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (property_name != NULL);
+ g_return_if_fail (value != NULL);
+
+ priv = context->priv;
+
+ if (!priv->widget_path)
+ return;
+
+ widget_type = gtk_widget_path_get_widget_type (priv->widget_path);
+
+ widget_class = g_type_class_ref (widget_type);
+ pspec = gtk_widget_class_find_style_property (widget_class, property_name);
+ g_type_class_unref (widget_class);
+
+ if (!pspec)
+ {
+ g_warning ("%s: widget class `%s' has no style property named `%s'",
+ G_STRLOC,
+ g_type_name (widget_type),
+ property_name);
+ return;
+ }
+
+ state = gtk_style_context_get_state (context);
+ peek_value = _gtk_style_context_peek_style_property (context, widget_type,
+ state, pspec);
+
+ if (G_VALUE_TYPE (value) == G_VALUE_TYPE (peek_value))
+ g_value_copy (peek_value, value);
+ else if (g_value_type_transformable (G_VALUE_TYPE (peek_value), G_VALUE_TYPE (value)))
+ g_value_transform (peek_value, value);
+ else
+ g_warning ("can't retrieve style property `%s' of type `%s' as value of type `%s'",
+ pspec->name,
+ G_VALUE_TYPE_NAME (peek_value),
+ G_VALUE_TYPE_NAME (value));
+}
+
+/**
+ * gtk_style_context_get_style_valist:
+ * @context: a #GtkStyleContext
+ * @args: va_list of property name/return location pairs, followed by %NULL
+ *
+ * Retrieves several widget style properties from @context according to the
+ * current style.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_style_valist (GtkStyleContext *context,
+ va_list args)
+{
+ GtkStyleContextPrivate *priv;
+ const gchar *prop_name;
+ GtkStateFlags state;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ prop_name = va_arg (args, const gchar *);
+ priv = context->priv;
+
+ if (!priv->widget_path)
+ return;
+
+ state = gtk_style_context_get_state (context);
+
+ while (prop_name)
+ {
+ GtkWidgetClass *widget_class;
+ GParamSpec *pspec;
+ const GValue *peek_value;
+ GType widget_type;
+ gchar *error;
+
+ widget_type = gtk_widget_path_get_widget_type (priv->widget_path);
+
+ widget_class = g_type_class_ref (widget_type);
+ pspec = gtk_widget_class_find_style_property (widget_class, prop_name);
+ g_type_class_unref (widget_class);
+
+ if (!pspec)
+ {
+ g_warning ("%s: widget class `%s' has no style property named `%s'",
+ G_STRLOC,
+ g_type_name (widget_type),
+ prop_name);
+ continue;
+ }
+
+ peek_value = _gtk_style_context_peek_style_property (context, widget_type,
+ state, pspec);
+
+ G_VALUE_LCOPY (peek_value, args, 0, &error);
+
+ if (error)
+ {
+ g_warning ("can't retrieve style property `%s' of type `%s': %s",
+ pspec->name,
+ G_VALUE_TYPE_NAME (peek_value),
+ error);
+ g_free (error);
+ }
+
+ prop_name = va_arg (args, const gchar *);
+ }
+}
+
+/**
+ * gtk_style_context_get_style:
+ * @context: a #GtkStyleContext
+ * @...: property name /return value pairs, followed by %NULL
+ *
+ * Retrieves several widget style properties from @context according to the
+ * current style.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_style (GtkStyleContext *context,
+ ...)
+{
+ va_list args;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ va_start (args, context);
+ gtk_style_context_get_style_valist (context, args);
+ va_end (args);
+}
+
+
+/**
+ * gtk_style_context_lookup_icon_set:
+ * @context: a #GtkStyleContext
+ * @stock_id: an icon name
+ *
+ * Looks up @stock_id in the icon factories associated to @context and
+ * the default icon factory, returning an icon set if found, otherwise
+ * %NULL.
+ *
+ * Returns: (transfer none): The looked up %GtkIconSet, or %NULL
+ **/
+GtkIconSet *
+gtk_style_context_lookup_icon_set (GtkStyleContext *context,
+ const gchar *stock_id)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+ GSList *list;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+ g_return_val_if_fail (stock_id != NULL, NULL);
+
+ priv = context->priv;
+ g_return_val_if_fail (priv->widget_path != NULL, NULL);
+
+ data = style_data_lookup (context);
+
+ for (list = data->icon_factories; list; list = list->next)
+ {
+ GtkIconFactory *factory;
+ GtkIconSet *icon_set;
+
+ factory = list->data;
+ icon_set = gtk_icon_factory_lookup (factory, stock_id);
+
+ if (icon_set)
+ return icon_set;
+ }
+
+ return gtk_icon_factory_lookup_default (stock_id);
+}
+
+/**
+ * gtk_style_context_set_screen:
+ * @context: a #GtkStyleContext
+ * @screen: a #GdkScreen
+ *
+ * Attaches @context to the given screen.
+ *
+ * The screen is used to add style information from 'global' style
+ * providers, such as the screens #GtkSettings instance.
+ *
+ * If you are using a #GtkStyleContext returned from
+ * gtk_widget_get_style_context(), you do not need to
+ * call this yourself.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_set_screen (GtkStyleContext *context,
+ GdkScreen *screen)
+{
+ GtkStyleContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ priv = context->priv;
+ if (priv->screen == screen)
+ return;
+
+ priv->screen = screen;
+
+ g_object_notify (G_OBJECT (context), "screen");
+
+ gtk_style_context_invalidate (context);
+}
+
+/**
+ * gtk_style_context_get_screen:
+ * @context: a #GtkStyleContext
+ *
+ * Returns the #GdkScreen to which @context is attached.
+ *
+ * Returns: a #GdkScreen.
+ **/
+GdkScreen *
+gtk_style_context_get_screen (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+
+ priv = context->priv;
+ return priv->screen;
+}
+
+/**
+ * gtk_style_context_set_direction:
+ * @context: a #GtkStyleContext
+ * @direction: the new direction.
+ *
+ * Sets the reading direction for rendering purposes.
+ *
+ * If you are using a #GtkStyleContext returned from
+ * gtk_widget_get_style_context(), you do not need to
+ * call this yourself.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_set_direction (GtkStyleContext *context,
+ GtkTextDirection direction)
+{
+ GtkStyleContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ priv->direction = direction;
+
+ g_object_notify (G_OBJECT (context), "direction");
+}
+
+/**
+ * gtk_style_context_get_direction:
+ * @context: a #GtkStyleContext
+ *
+ * Returns the widget direction used for rendering.
+ *
+ * Returns: the widget direction
+ *
+ * Since: 3.0
+ **/
+GtkTextDirection
+gtk_style_context_get_direction (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), GTK_TEXT_DIR_LTR);
+
+ priv = context->priv;
+ return priv->direction;
+}
+
+/**
+ * gtk_style_context_set_junction_sides:
+ * @context: a #GtkStyleContext
+ * @sides: sides where rendered elements are visually connected to
+ * other elements
+ *
+ * Sets the sides where rendered elements (mostly through
+ * gtk_render_frame()) will visually connect with other visual elements.
+ *
+ * This is merely a hint that may or may not be honored
+ * by theming engines.
+ *
+ * Container widgets are expected to set junction hints as appropriate
+ * for their children, so it should not normally be necessary to call
+ * this function manually.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_set_junction_sides (GtkStyleContext *context,
+ GtkJunctionSides sides)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ info = priv->info_stack->data;
+ info->junction_sides = sides;
+}
+
+/**
+ * gtk_style_context_get_junction_sides:
+ * @context: a #GtkStyleContext
+ *
+ * Returns the sides where rendered elements connect visually with others.
+ *
+ * Returns: the junction sides
+ *
+ * Since: 3.0
+ **/
+GtkJunctionSides
+gtk_style_context_get_junction_sides (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+ GtkStyleInfo *info;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
+
+ priv = context->priv;
+ info = priv->info_stack->data;
+ return info->junction_sides;
+}
+
+/**
+ * gtk_style_context_lookup_color:
+ * @context: a #GtkStyleContext
+ * @color_name: color name to lookup
+ * @color: (out): Return location for the looked up color
+ *
+ * Looks up and resolves a color name in the @context color map.
+ *
+ * Returns: %TRUE if @color_name was found and resolved, %FALSE otherwise
+ **/
+gboolean
+gtk_style_context_lookup_color (GtkStyleContext *context,
+ const gchar *color_name,
+ GdkRGBA *color)
+{
+ GtkStyleContextPrivate *priv;
+ GtkSymbolicColor *sym_color;
+ StyleData *data;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
+ g_return_val_if_fail (color_name != NULL, FALSE);
+ g_return_val_if_fail (color != NULL, FALSE);
+
+ priv = context->priv;
+ g_return_val_if_fail (priv->widget_path != NULL, FALSE);
+
+ data = style_data_lookup (context);
+ sym_color = gtk_style_properties_lookup_color (data->store, color_name);
+
+ if (!sym_color)
+ return FALSE;
+
+ return gtk_symbolic_color_resolve (sym_color, data->store, color);
+}
+
+/**
+ * gtk_style_context_notify_state_change:
+ * @context: a #GtkStyleContext
+ * @window: a #GdkWindow
+ * @region_id: (allow-none): animatable region to notify on, or %NULL.
+ * See gtk_style_context_push_animatable_region()
+ * @state: state to trigger transition for
+ * @state_value: %TRUE if @state is the state we are changing to,
+ * %FALSE if we are changing away from it
+ *
+ * Notifies a state change on @context, so if the current style makes use
+ * of transition animations, one will be started so all rendered elements
+ * under @region_id are animated for state @state being set to value
+ * @state_value.
+ *
+ * The @window parameter is used in order to invalidate the rendered area
+ * as the animation runs, so make sure it is the same window that is being
+ * rendered on by the gtk_render_*() functions.
+ *
+ * If @region_id is %NULL, all rendered elements using @context will be
+ * affected by this state transition.
+ *
+ * As a practical example, a #GtkButton notifying a state transition on
+ * the prelight state:
+ * <programlisting>
+ * gtk_style_context_notify_state_change (context,
+ * gtk_widget_get_window (widget),
+ * NULL,
+ * GTK_STATE_PRELIGHT,
+ * button->in_button);
+ * </programlisting>
+ *
+ * Can be handled in the CSS file like this:
+ * <programlisting>
+ * GtkButton {
+ * background-color: &num;f00
+ * }
+ *
+ * GtkButton:hover {
+ * background-color: &num;fff;
+ * transition: 200ms linear
+ * }
+ * </programlisting>
+ *
+ * This combination will animate the button background from red to white
+ * if a pointer enters the button, and back to red if the pointer leaves
+ * the button.
+ *
+ * Note that @state is used when finding the transition parameters, which
+ * is why the style places the transition under the :hover pseudo-class.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_notify_state_change (GtkStyleContext *context,
+ GdkWindow *window,
+ gpointer region_id,
+ GtkStateType state,
+ gboolean state_value)
+{
+ GtkStyleContextPrivate *priv;
+ GtkAnimationDescription *desc;
+ AnimationInfo *info;
+ GtkStateFlags flags;
+ StyleData *data;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (GDK_IS_WINDOW (window));
+ g_return_if_fail (state > GTK_STATE_NORMAL && state <= GTK_STATE_FOCUSED);
+
+ priv = context->priv;
+ g_return_if_fail (priv->widget_path != NULL);
+
+ state_value = (state_value == TRUE);
+
+ switch (state)
+ {
+ case GTK_STATE_ACTIVE:
+ flags = GTK_STATE_FLAG_ACTIVE;
+ break;
+ case GTK_STATE_PRELIGHT:
+ flags = GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags = GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags = GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ case GTK_STATE_INCONSISTENT:
+ flags = GTK_STATE_FLAG_INCONSISTENT;
+ break;
+ case GTK_STATE_FOCUSED:
+ flags = GTK_STATE_FLAG_FOCUSED;
+ break;
+ case GTK_STATE_NORMAL:
+ default:
+ flags = 0;
+ break;
+ }
+
+ /* Find out if there is any animation description for the given
+ * state, it will fallback to the normal state as well if necessary.
+ */
+ data = style_data_lookup (context);
+ gtk_style_properties_get (data->store, flags,
+ "transition", &desc,
+ NULL);
+
+ if (!desc)
+ return;
+
+ if (gtk_animation_description_get_duration (desc) == 0)
+ {
+ gtk_animation_description_unref (desc);
+ return;
+ }
+
+ info = animation_info_lookup (context, region_id, state);
+
+ if (info &&
+ info->target_value != state_value)
+ {
+ /* Target values are the opposite */
+ if (!gtk_timeline_get_loop (info->timeline))
+ {
+ /* Reverse the animation */
+ if (gtk_timeline_get_direction (info->timeline) == GTK_TIMELINE_DIRECTION_FORWARD)
+ gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_BACKWARD);
+ else
+ gtk_timeline_set_direction (info->timeline, GTK_TIMELINE_DIRECTION_FORWARD);
+
+ info->target_value = state_value;
+ }
+ else
+ {
+ /* Take it out of its looping state */
+ gtk_timeline_set_loop (info->timeline, FALSE);
+ }
+ }
+ else if (!info &&
+ (!gtk_animation_description_get_loop (desc) ||
+ state_value))
+ {
+ info = animation_info_new (context, region_id,
+ gtk_animation_description_get_duration (desc),
+ gtk_animation_description_get_progress_type (desc),
+ gtk_animation_description_get_loop (desc),
+ state, state_value, window);
+
+ priv->animations = g_slist_prepend (priv->animations, info);
+ priv->animations_invalidated = TRUE;
+ }
+
+ gtk_animation_description_unref (desc);
+}
+
+/**
+ * gtk_style_context_push_animatable_region:
+ * @context: a #GtkStyleContext
+ * @region_id: unique identifier for the animatable region
+ *
+ * Pushes an animatable region, so all further gtk_render_*() calls between
+ * this call and the following gtk_style_context_pop_animatable_region()
+ * will potentially show transition animations for this region if
+ * gtk_style_context_notify_state_change() is called for a given state,
+ * and the current theme/style defines transition animations for state
+ * changes.
+ *
+ * The @region_id used must be unique in @context so the theming engine
+ * can uniquely identify rendered elements subject to a state transition.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_push_animatable_region (GtkStyleContext *context,
+ gpointer region_id)
+{
+ GtkStyleContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (region_id != NULL);
+
+ priv = context->priv;
+ priv->animation_regions = g_slist_prepend (priv->animation_regions, region_id);
+}
+
+/**
+ * gtk_style_context_pop_animatable_region:
+ * @context: a #GtkStyleContext
+ *
+ * Pops an animatable region from @context.
+ * See gtk_style_context_push_animatable_region().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_pop_animatable_region (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ priv->animation_regions = g_slist_delete_link (priv->animation_regions,
+ priv->animation_regions);
+}
+
+void
+_gtk_style_context_invalidate_animation_areas (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+ GSList *l;
+
+ priv = context->priv;
+
+ for (l = priv->animations; l; l = l->next)
+ {
+ AnimationInfo *info;
+
+ info = l->data;
+
+ /* A NULL invalidation region means it has to be recreated on
+ * the next expose event, this happens usually after a widget
+ * allocation change, so the next expose after it will update
+ * the invalidation region.
+ */
+ if (info->invalidation_region)
+ {
+ cairo_region_destroy (info->invalidation_region);
+ info->invalidation_region = NULL;
+ }
+ }
+
+ priv->animations_invalidated = TRUE;
+}
+
+void
+_gtk_style_context_coalesce_animation_areas (GtkStyleContext *context,
+ gint rel_x,
+ gint rel_y)
+{
+ GtkStyleContextPrivate *priv;
+ GSList *l;
+
+ priv = context->priv;
+
+ if (!priv->animations_invalidated)
+ return;
+
+ l = priv->animations;
+
+ while (l)
+ {
+ AnimationInfo *info;
+ GSList *cur;
+ guint i;
+
+ cur = l;
+ info = cur->data;
+ l = l->next;
+
+ if (info->invalidation_region)
+ continue;
+
+ /* There's not much point in keeping the animation running */
+ if (info->rectangles->len == 0)
+ {
+ priv->animations = g_slist_remove (priv->animations, info);
+ animation_info_free (info);
+ continue;
+ }
+
+ info->invalidation_region = cairo_region_create ();
+
+ for (i = 0; i < info->rectangles->len; i++)
+ {
+ cairo_rectangle_int_t *rect;
+
+ rect = &g_array_index (info->rectangles, cairo_rectangle_int_t, i);
+ rect->x += rel_x;
+ rect->y += rel_y;
+
+ cairo_region_union_rectangle (info->invalidation_region, rect);
+ }
+
+ g_array_remove_range (info->rectangles, 0, info->rectangles->len);
+ }
+
+ priv->animations_invalidated = FALSE;
+}
+
+static void
+store_animation_region (GtkStyleContext *context,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStyleContextPrivate *priv;
+ GSList *l;
+
+ priv = context->priv;
+
+ if (!priv->animations_invalidated)
+ return;
+
+ for (l = priv->animations; l; l = l->next)
+ {
+ AnimationInfo *info;
+
+ info = l->data;
+
+ /* The animation doesn't need updating
+ * the invalidation area, bail out.
+ */
+ if (info->invalidation_region)
+ continue;
+
+ if (context_has_animatable_region (context, info->region_id))
+ {
+ cairo_rectangle_int_t rect;
+
+ rect.x = (gint) x;
+ rect.y = (gint) y;
+ rect.width = (gint) width;
+ rect.height = (gint) height;
+
+ g_array_append_val (info->rectangles, rect);
+ }
+ }
+}
+
+/**
+ * gtk_style_context_invalidate:
+ * @context: a #GtkStyleContext.
+ *
+ * Invalidates @context style information, so it will be reconstructed
+ * again.
+ *
+ * If you're using a #GtkStyleContext returned from
+ * gtk_widget_get_style_context(), you do not need to
+ * call this yourself.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_invalidate (GtkStyleContext *context)
+{
+ GtkStyleContextPrivate *priv;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+
+ /* Avoid reentrancy */
+ if (priv->invalidating_context)
+ return;
+
+ priv->invalidating_context = TRUE;
+
+ g_hash_table_remove_all (priv->style_data);
+ priv->current_data = NULL;
+
+ g_signal_emit (context, signals[CHANGED], 0);
+
+ priv->invalidating_context = FALSE;
+}
+
+/**
+ * gtk_style_context_set_background:
+ * @context: a #GtkStyleContext
+ * @window: a #GdkWindow
+ *
+ * Sets the background of @window to the background pattern or
+ * color specified in @context for its current state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_set_background (GtkStyleContext *context,
+ GdkWindow *window)
+{
+ GtkStateFlags state;
+ cairo_pattern_t *pattern;
+ GdkRGBA *color;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ state = gtk_style_context_get_state (context);
+ gtk_style_context_get (context, state,
+ "background-image", &pattern,
+ NULL);
+ if (pattern)
+ {
+ gdk_window_set_background_pattern (window, pattern);
+ cairo_pattern_destroy (pattern);
+ return;
+ }
+
+ gtk_style_context_get (context, state,
+ "background-color", &color,
+ NULL);
+ if (color)
+ {
+ gdk_window_set_background_rgba (window, color);
+ gdk_rgba_free (color);
+ }
+}
+
+/**
+ * gtk_style_context_get_color:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the color for
+ * @color: (out): return value for the foreground color
+ *
+ * Gets the foreground color for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_color (GtkStyleContext *context,
+ GtkStateFlags state,
+ GdkRGBA *color)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+ const GValue *value;
+ GdkRGBA *c;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ g_return_if_fail (priv->widget_path != NULL);
+
+ data = style_data_lookup (context);
+ value = _gtk_style_properties_peek_property (data->store,
+ "color", state);
+ c = g_value_get_boxed (value);
+ *color = *c;
+}
+
+/**
+ * gtk_style_context_get_background_color:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the color for
+ * @color: (out): return value for the background color
+ *
+ * Gets the background color for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_background_color (GtkStyleContext *context,
+ GtkStateFlags state,
+ GdkRGBA *color)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+ const GValue *value;
+ GdkRGBA *c;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ g_return_if_fail (priv->widget_path != NULL);
+
+ data = style_data_lookup (context);
+ value = _gtk_style_properties_peek_property (data->store,
+ "background-color", state);
+ c = g_value_get_boxed (value);
+ *color = *c;
+}
+
+/**
+ * gtk_style_context_get_border_color:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the color for
+ * @color: (out): return value for the border color
+ *
+ * Gets the border color for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_border_color (GtkStyleContext *context,
+ GtkStateFlags state,
+ GdkRGBA *color)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+ const GValue *value;
+ GdkRGBA *c;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ g_return_if_fail (priv->widget_path != NULL);
+
+ data = style_data_lookup (context);
+ value = _gtk_style_properties_peek_property (data->store,
+ "border-color", state);
+ c = g_value_get_boxed (value);
+ *color = *c;
+}
+
+/**
+ * gtk_style_context_get_border:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the border for
+ * @color: (out): return value for the border settings
+ *
+ * Gets the border for a given state as a #GtkBorder.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_border (GtkStyleContext *context,
+ GtkStateFlags state,
+ GtkBorder *border)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+ const GValue *value;
+ GtkBorder *b;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ g_return_if_fail (priv->widget_path != NULL);
+
+ data = style_data_lookup (context);
+ value = _gtk_style_properties_peek_property (data->store,
+ "border-width", state);
+ b = g_value_get_boxed (value);
+ *border = *b;
+}
+
+/**
+ * gtk_style_context_get_padding:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the padding for
+ * @color: (out): return value for the padding settings
+ *
+ * Gets the padding for a given state as a #GtkBorder.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_padding (GtkStyleContext *context,
+ GtkStateFlags state,
+ GtkBorder *padding)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+ const GValue *value;
+ GtkBorder *b;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ g_return_if_fail (priv->widget_path != NULL);
+
+ data = style_data_lookup (context);
+ value = _gtk_style_properties_peek_property (data->store,
+ "padding", state);
+ b = g_value_get_boxed (value);
+ *padding = *b;
+}
+
+/**
+ * gtk_style_context_get_margin:
+ * @context: a #GtkStyleContext
+ * @state: state to retrieve the border for
+ * @color: (out): return value for the margin settings
+ *
+ * Gets the margin for a given state as a #GtkBorder.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_context_get_margin (GtkStyleContext *context,
+ GtkStateFlags state,
+ GtkBorder *margin)
+{
+ GtkStyleContextPrivate *priv;
+ StyleData *data;
+ const GValue *value;
+ GtkBorder *b;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = context->priv;
+ g_return_if_fail (priv->widget_path != NULL);
+
+ data = style_data_lookup (context);
+ value = _gtk_style_properties_peek_property (data->store,
+ "margin", state);
+ b = g_value_get_boxed (value);
+ *margin = *b;
+}
+
+/* Paint methods */
+
+/**
+ * gtk_render_check:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ *
+ * Renders a checkmark (as in a #GtkCheckButton).
+ *
+ * The %GTK_STATE_FLAG_ACTIVE state determines whether the check is
+ * on or off, and %GTK_STATE_FLAG_INCONSISTENT determines whether it
+ * should be marked as undefined.
+ *
+ * <example>
+ * <title>Typical checkmark rendering</title>
+ * <inlinegraphic fileref="checks.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_check (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_check (priv->theming_engine, cr,
+ x, y, width, height);
+}
+
+/**
+ * gtk_render_option:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ *
+ * Renders an option mark (as in a #GtkRadioButton), the %GTK_STATE_FLAG_ACTIVE
+ * state will determine whether the option is on or off, and
+ * %GTK_STATE_FLAG_INCONSISTENT whether it should be marked as undefined.
+ *
+ * <example>
+ * <title>Typical option mark rendering</title>
+ * <inlinegraphic fileref="options.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_option (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_option (priv->theming_engine, cr,
+ x, y, width, height);
+}
+
+/**
+ * gtk_render_arrow:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @angle: arrow angle from 0 to 2 * %G_PI, being 0 the arrow pointing to the north
+ * @x: Center X for the render area
+ * @y: Center Y for the render area
+ * @size: square side for render area
+ *
+ * Renders an arrow pointing to @angle.
+ *
+ * <example>
+ * <title>Typical arrow rendering at 0, 1&solidus;2 &pi;, &pi; and 3&solidus;2 &pi;</title>
+ * <inlinegraphic fileref="arrows.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_arrow (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble angle,
+ gdouble x,
+ gdouble y,
+ gdouble size)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, size, size);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_arrow (priv->theming_engine, cr,
+ angle, x, y, size);
+}
+
+/**
+ * gtk_render_background:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ *
+ * Renders the background of an element.
+ *
+ * <example>
+ * <title>Typical background rendering, showing the effect of
+ * <parameter>background-image</parameter>,
+ * <parameter>border-width</parameter> and
+ * <parameter>border-radius</parameter></title>
+ * <inlinegraphic fileref="background.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0.
+ **/
+void
+gtk_render_background (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_background (priv->theming_engine, cr, x, y, width, height);
+}
+
+/**
+ * gtk_render_frame:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ *
+ * Renders a frame around the rectangle defined by @x, @y, @width, @height.
+ *
+ * <example>
+ * <title>Examples of frame rendering, showing the effect of
+ * <parameter>border-image</parameter>,
+ * <parameter>border-color</parameter>,
+ * <parameter>border-width</parameter>,
+ * <parameter>border-radius</parameter> and
+ * junctions</title>
+ * <inlinegraphic fileref="frames.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_frame (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_frame (priv->theming_engine, cr, x, y, width, height);
+}
+
+/**
+ * gtk_render_expander:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ *
+ * Renders an expander (as used in #GtkTreeView and #GtkExpander) in the area
+ * defined by @x, @y, @width, @height. The state %GTK_STATE_FLAG_ACTIVE
+ * determines whether the expander is collapsed or expanded.
+ *
+ * <example>
+ * <title>Typical expander rendering</title>
+ * <inlinegraphic fileref="expanders.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_expander (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_expander (priv->theming_engine, cr, x, y, width, height);
+}
+
+/**
+ * gtk_render_focus:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ *
+ * Renders a focus indicator on the rectangle determined by @x, @y, @width, @height.
+ * <example>
+ * <title>Typical focus rendering</title>
+ * <inlinegraphic fileref="focus.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_focus (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_focus (priv->theming_engine, cr, x, y, width, height);
+}
+
+/**
+ * gtk_render_layout:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin
+ * @y: Y origin
+ * @layout: the #PangoLayout to render
+ *
+ * Renders @layout on the coordinates @x, @y
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_layout (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ PangoLayout *layout)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+ PangoRectangle extents;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ pango_layout_get_extents (layout, &extents, NULL);
+
+ store_animation_region (context,
+ x + extents.x,
+ y + extents.y,
+ extents.width,
+ extents.height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_layout (priv->theming_engine, cr, x, y, layout);
+}
+
+/**
+ * gtk_render_line:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x0: X coordinate for the origin of the line
+ * @y0: Y coordinate for the origin of the line
+ * @x1: X coordinate for the end of the line
+ * @y1: Y coordinate for the end of the line
+ *
+ * Renders a line from (x0, y0) to (x1, y1).
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_line (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_line (priv->theming_engine, cr, x0, y0, x1, y1);
+}
+
+/**
+ * gtk_render_slider:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ * @orientation: orientation of the slider
+ *
+ * Renders a slider (as in #GtkScale) in the rectangle defined by @x, @y,
+ * @width, @height. @orientation defines whether the slider is vertical
+ * or horizontal.
+ *
+ * <example>
+ * <title>Typical slider rendering</title>
+ * <inlinegraphic fileref="sliders.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_slider (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkOrientation orientation)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_slider (priv->theming_engine, cr, x, y, width, height, orientation);
+}
+
+/**
+ * gtk_render_frame_gap:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ * @gap_side: side where the gap is
+ * @xy0_gap: initial coordinate (X or Y depending on @gap_side) for the gap
+ * @xy1_gap: end coordinate (X or Y depending on @gap_side) for the gap
+ *
+ * Renders a frame around the rectangle defined by (@x, @y, @width, @height),
+ * leaving a gap on one side. @xy0_gap and @xy1_gap will mean X coordinates
+ * for %GTK_POS_TOP and %GTK_POS_BOTTOM gap sides, and Y coordinates for
+ * %GTK_POS_LEFT and %GTK_POS_RIGHT.
+ *
+ * <example>
+ * <title>Typical rendering of a frame with a gap</title>
+ * <inlinegraphic fileref="frame-gap.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_frame_gap (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkPositionType gap_side,
+ gdouble xy0_gap,
+ gdouble xy1_gap)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_frame_gap (priv->theming_engine, cr,
+ x, y, width, height, gap_side,
+ xy0_gap, xy1_gap);
+}
+
+/**
+ * gtk_render_extension:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ * @gap_side: side where the gap is
+ *
+ * Renders a extension (as in a #GtkNotebook tab) in the rectangle
+ * defined by @x, @y, @width, @height. The side where the extension
+ * connects to is defined by @gap_side.
+ *
+ * <example>
+ * <title>Typical extension rendering</title>
+ * <inlinegraphic fileref="extensions.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_extension (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkPositionType gap_side)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_extension (priv->theming_engine, cr, x, y, width, height, gap_side);
+}
+
+/**
+ * gtk_render_handle:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ *
+ * Renders a handle (as in #GtkHandleBox, #GtkPaned and
+ * #GtkWindow<!-- -->'s resize grip), in the rectangle
+ * determined by @x, @y, @width, @height.
+ *
+ * <example>
+ * <title>Handles rendered for the paned and grip classes</title>
+ * <inlinegraphic fileref="handles.png" format="PNG"/>
+ * </example>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_handle (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_handle (priv->theming_engine, cr, x, y, width, height);
+}
+
+/**
+ * gtk_render_activity:
+ * @context: a #GtkStyleContext
+ * @cr: a #cairo_t
+ * @x: X origin of the rectangle
+ * @y: Y origin of the rectangle
+ * @width: rectangle width
+ * @height: rectangle height
+ *
+ * Renders an activity area (Such as in #GtkSpinner or the
+ * fill line in #GtkRange), the state %GTK_STATE_FLAG_ACTIVE
+ * determines whether there is activity going on.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_render_activity (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+ g_return_if_fail (cr != NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ store_animation_region (context, x, y, width, height);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ engine_class->render_activity (priv->theming_engine, cr, x, y, width, height);
+}
+
+/**
+ * gtk_render_icon_pixbuf:
+ * @context: a #GtkStyleContext
+ * @source: the #GtkIconSource specifying the icon to render
+ * @size: (type int): the size to render the icon at. A size of (GtkIconSize) -1
+ * means render at the size of the source and don't scale.
+ *
+ * Renders the icon specified by @source at the given @size, returning the result
+ * in a pixbuf.
+ *
+ * Returns: (transfer full): a newly-created #GdkPixbuf containing the rendered icon
+ *
+ * Since: 3.0
+ **/
+GdkPixbuf *
+gtk_render_icon_pixbuf (GtkStyleContext *context,
+ const GtkIconSource *source,
+ GtkIconSize size)
+{
+ GtkStyleContextPrivate *priv;
+ GtkThemingEngineClass *engine_class;
+
+ g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
+ g_return_val_if_fail (size == -1 || size <= GTK_ICON_SIZE_DIALOG, NULL);
+ g_return_val_if_fail (source != NULL, NULL);
+
+ priv = context->priv;
+ engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
+
+ _gtk_theming_engine_set_context (priv->theming_engine, context);
+ return engine_class->render_icon_pixbuf (priv->theming_engine, source, size);
+}
diff --git a/gtk/gtkstylecontext.h b/gtk/gtkstylecontext.h
new file mode 100644
index 0000000000..a67e523a83
--- /dev/null
+++ b/gtk/gtkstylecontext.h
@@ -0,0 +1,564 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_STYLE_CONTEXT_H__
+#define __GTK_STYLE_CONTEXT_H__
+
+#include <glib-object.h>
+#include <gtk/gtkstyleprovider.h>
+#include <gtk/gtkwidgetpath.h>
+#include <gtk/gtkborder.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_STYLE_CONTEXT (gtk_style_context_get_type ())
+#define GTK_STYLE_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_STYLE_CONTEXT, GtkStyleContext))
+#define GTK_STYLE_CONTEXT_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_STYLE_CONTEXT, GtkStyleContextClass))
+#define GTK_IS_STYLE_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_STYLE_CONTEXT))
+#define GTK_IS_STYLE_CONTEXT_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GTK_TYPE_STYLE_CONTEXT))
+#define GTK_STYLE_CONTEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_STYLE_CONTEXT, GtkStyleContextClass))
+
+typedef struct _GtkStyleContext GtkStyleContext;
+typedef struct _GtkStyleContextClass GtkStyleContextClass;
+
+struct _GtkStyleContext
+{
+ GObject parent_object;
+ gpointer priv;
+};
+
+struct _GtkStyleContextClass
+{
+ GObjectClass parent_class;
+
+ void (* changed) (GtkStyleContext *context);
+};
+
+/* Default set of properties that GtkStyleContext may contain */
+
+/**
+ * GTK_STYLE_PROPERTY_BACKGROUND_COLOR:
+ *
+ * A property holding the background color of rendered elements as a #GdkRGBA.
+ */
+#define GTK_STYLE_PROPERTY_BACKGROUND_COLOR "background-color"
+
+/**
+ * GTK_STYLE_PROPERTY_COLOR:
+ *
+ * A property holding the foreground color of rendered elements as a #GdkRGBA.
+ */
+#define GTK_STYLE_PROPERTY_COLOR "color"
+
+/**
+ * GTK_STYLE_PROPERTY_FONT:
+ *
+ * A property holding the font properties used when rendering text
+ * as a #PangoFontDescription.
+ */
+#define GTK_STYLE_PROPERTY_FONT "font"
+
+/**
+ * GTK_STYLE_PROPERTY_MARGIN:
+ *
+ * A property holding the rendered element's margin as a #GtkBorder. The
+ * margin is defined as the spacing between the border of the element
+ * and its surrounding elements.
+ */
+#define GTK_STYLE_PROPERTY_MARGIN "margin"
+
+/**
+ * GTK_STYLE_PROPERTY_PADDING:
+ *
+ * A property holding the rendered element's padding as a #GtkBorder. The
+ * padding is defined as the spacing between the inner part of the element border
+ * and its child.
+ */
+#define GTK_STYLE_PROPERTY_PADDING "padding"
+
+/**
+ * GTK_STYLE_PROPERTY_BORDER_WIDTH:
+ *
+ * A property holding the rendered element's border width in pixels as a #gint.
+ */
+#define GTK_STYLE_PROPERTY_BORDER_WIDTH "border-width"
+
+/**
+ * GTK_STYLE_PROPERTY_BORDER_RADIUS:
+ *
+ * A property holding the rendered element's border radius in pixels as a #gint.
+ */
+#define GTK_STYLE_PROPERTY_BORDER_RADIUS "border-radius"
+
+/**
+ * GTK_STYLE_PROPERTY_BORDER_STYLE:
+ *
+ * A property holding the element's border style as a #GtkBorderStyle.
+ */
+#define GTK_STYLE_PROPERTY_BORDER_STYLE "border-style"
+
+/**
+ * GTK_STYLE_PROPERTY_BORDER_COLOR:
+ *
+ * A property holding the element's border color as a #GdkRGBA.
+ */
+#define GTK_STYLE_PROPERTY_BORDER_COLOR "border-color"
+
+/**
+ * GTK_STYLE_PROPERTY_BACKGROUND_IMAGE:
+ *
+ * A property holding the element's background as a #cairo_pattern_t.
+ */
+#define GTK_STYLE_PROPERTY_BACKGROUND_IMAGE "background-image"
+
+
+/* Predefined set of CSS classes */
+
+/**
+ * GTK_STYLE_CLASS_CELL:
+ *
+ * A CSS class to match content rendered in cell views.
+ */
+#define GTK_STYLE_CLASS_CELL "cell"
+
+/**
+ * GTK_STYLE_CLASS_ENTRY:
+ *
+ * A CSS class to match text entries.
+ */
+#define GTK_STYLE_CLASS_ENTRY "entry"
+
+/**
+ * GTK_STYLE_CLASS_BUTTON:
+ *
+ * A CSS class to match buttons.
+ */
+#define GTK_STYLE_CLASS_BUTTON "button"
+
+/**
+ * GTK_STYLE_CLASS_CALENDAR:
+ *
+ * A CSS class to match calendars.
+ */
+#define GTK_STYLE_CLASS_CALENDAR "calendar"
+
+/**
+ * GTK_STYLE_CLASS_SLIDER:
+ *
+ * A CSS class to match sliders.
+ */
+#define GTK_STYLE_CLASS_SLIDER "slider"
+
+/**
+ * GTK_STYLE_CLASS_BACKGROUND:
+ *
+ * A CSS class to match the window background.
+ */
+#define GTK_STYLE_CLASS_BACKGROUND "background"
+
+/**
+ * GTK_STYLE_CLASS_RUBBERBAND:
+ *
+ * A CSS class to match the rubberband selection rectangle.
+ */
+#define GTK_STYLE_CLASS_RUBBERBAND "rubberband"
+
+/**
+ * GTK_STYLE_CLASS_TOOLTIP:
+ *
+ * A CSS class to match tooltip windows.
+ */
+#define GTK_STYLE_CLASS_TOOLTIP "tooltip"
+
+/**
+ * GTK_STYLE_CLASS_MENU:
+ *
+ * A CSS class to match popup menus.
+ */
+#define GTK_STYLE_CLASS_MENU "menu"
+
+/**
+ * GTK_STYLE_CLASS_MENUBAR:
+ *
+ * A CSS class to menubars.
+ */
+#define GTK_STYLE_CLASS_MENUBAR "menubar"
+
+/**
+ * GTK_STYLE_CLASS_MENUITEM:
+ *
+ * A CSS class to match menu items.
+ */
+#define GTK_STYLE_CLASS_MENUITEM "menuitem"
+
+/**
+ * GTK_STYLE_CLASS_TOOLBAR:
+ *
+ * A CSS class to match toolbars.
+ */
+#define GTK_STYLE_CLASS_TOOLBAR "toolbar"
+
+/**
+ * GTK_STYLE_CLASS_RADIO:
+ *
+ * A CSS class to match radio buttons.
+ */
+#define GTK_STYLE_CLASS_RADIO "radio"
+
+/**
+ * GTK_STYLE_CLASS_CHECK:
+ *
+ * A CSS class to match check boxes.
+ */
+#define GTK_STYLE_CLASS_CHECK "check"
+
+/**
+ * GTK_STYLE_CLASS_DEFAULT:
+ *
+ * A CSS class to match the default widget.
+ */
+#define GTK_STYLE_CLASS_DEFAULT "default"
+
+/**
+ * GTK_STYLE_CLASS_TROUGH:
+ *
+ * A CSS class to match troughs, as in scrollbars and progressbars.
+ */
+#define GTK_STYLE_CLASS_TROUGH "trough"
+
+/**
+ * GTK_STYLE_CLASS_SCROLLBAR:
+ *
+ * A CSS class to match scrollbars.
+ */
+#define GTK_STYLE_CLASS_SCROLLBAR "scrollbar"
+
+/**
+ * GTK_STYLE_CLASS_HEADER:
+ *
+ * A CSS class to match a header element.
+ */
+#define GTK_STYLE_CLASS_HEADER "header"
+
+/**
+ * GTK_STYLE_CLASS_ACCELERATOR:
+ *
+ * A CSS class to match an accelerator.
+ */
+#define GTK_STYLE_CLASS_ACCELERATOR "accelerator"
+
+/**
+ * GTK_STYLE_CLASS_GRIP:
+ *
+ * A widget class defining a resize grip
+ */
+#define GTK_STYLE_CLASS_GRIP "grip"
+
+/**
+ * GTK_STYLE_CLASS_DOCK:
+ *
+ * A widget class defining a dock area
+ */
+#define GTK_STYLE_CLASS_DOCK "dock"
+
+/**
+ * GTK_STYLE_CLASS_PROGRESSBAR:
+ *
+ * A widget class defining a resize grip
+ */
+#define GTK_STYLE_CLASS_PROGRESSBAR "progressbar"
+
+/**
+ * GTK_STYLE_CLASS_SPINNER:
+ *
+ * A widget class defining a spinner
+ */
+#define GTK_STYLE_CLASS_SPINNER "spinner"
+
+/* Predefined set of widget regions */
+
+/**
+ * GTK_STYLE_REGION_ROW:
+ *
+ * A widget region name to define a treeview row.
+ */
+#define GTK_STYLE_REGION_ROW "row"
+
+/**
+ * GTK_STYLE_REGION_COLUMN:
+ *
+ * A widget region name to define a treeview column.
+ */
+#define GTK_STYLE_REGION_COLUMN "column"
+
+/**
+ * GTK_STYLE_REGION_COLUMN_HEADER:
+ *
+ * A widget region name to define a treeview column header.
+ */
+#define GTK_STYLE_REGION_COLUMN_HEADER "column-header"
+
+/**
+ * GTK_STYLE_REGION_TAB:
+ *
+ * A widget region name to define a notebook tab.
+ */
+#define GTK_STYLE_REGION_TAB "tab"
+
+
+GType gtk_style_context_get_type (void) G_GNUC_CONST;
+
+GtkStyleContext * gtk_style_context_new (void);
+
+void gtk_style_context_add_provider_for_screen (GdkScreen *screen,
+ GtkStyleProvider *provider,
+ guint priority);
+void gtk_style_context_remove_provider_for_screen (GdkScreen *screen,
+ GtkStyleProvider *provider);
+
+void gtk_style_context_add_provider (GtkStyleContext *context,
+ GtkStyleProvider *provider,
+ guint priority);
+
+void gtk_style_context_remove_provider (GtkStyleContext *context,
+ GtkStyleProvider *provider);
+
+void gtk_style_context_save (GtkStyleContext *context);
+void gtk_style_context_restore (GtkStyleContext *context);
+
+void gtk_style_context_get_property (GtkStyleContext *context,
+ const gchar *property,
+ GtkStateFlags state,
+ GValue *value);
+void gtk_style_context_get_valist (GtkStyleContext *context,
+ GtkStateFlags state,
+ va_list args);
+void gtk_style_context_get (GtkStyleContext *context,
+ GtkStateFlags state,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gtk_style_context_set_state (GtkStyleContext *context,
+ GtkStateFlags flags);
+GtkStateFlags gtk_style_context_get_state (GtkStyleContext *context);
+
+gboolean gtk_style_context_state_is_running (GtkStyleContext *context,
+ GtkStateType state,
+ gdouble *progress);
+
+void gtk_style_context_set_path (GtkStyleContext *context,
+ GtkWidgetPath *path);
+G_CONST_RETURN GtkWidgetPath * gtk_style_context_get_path (GtkStyleContext *context);
+
+GList * gtk_style_context_list_classes (GtkStyleContext *context);
+
+void gtk_style_context_add_class (GtkStyleContext *context,
+ const gchar *class_name);
+void gtk_style_context_remove_class (GtkStyleContext *context,
+ const gchar *class_name);
+gboolean gtk_style_context_has_class (GtkStyleContext *context,
+ const gchar *class_name);
+
+GList * gtk_style_context_list_regions (GtkStyleContext *context);
+
+void gtk_style_context_add_region (GtkStyleContext *context,
+ const gchar *region_name,
+ GtkRegionFlags flags);
+void gtk_style_context_remove_region (GtkStyleContext *context,
+ const gchar *region_name);
+gboolean gtk_style_context_has_region (GtkStyleContext *context,
+ const gchar *region_name,
+ GtkRegionFlags *flags_return);
+
+void gtk_style_context_get_style_property (GtkStyleContext *context,
+ const gchar *property_name,
+ GValue *value);
+void gtk_style_context_get_style_valist (GtkStyleContext *context,
+ va_list args);
+void gtk_style_context_get_style (GtkStyleContext *context,
+ ...);
+
+GtkIconSet * gtk_style_context_lookup_icon_set (GtkStyleContext *context,
+ const gchar *stock_id);
+GdkPixbuf * gtk_icon_set_render_icon_pixbuf (GtkIconSet *icon_set,
+ GtkStyleContext *context,
+ GtkIconSize size);
+
+void gtk_style_context_set_screen (GtkStyleContext *context,
+ GdkScreen *screen);
+GdkScreen * gtk_style_context_get_screen (GtkStyleContext *context);
+
+void gtk_style_context_set_direction (GtkStyleContext *context,
+ GtkTextDirection direction);
+GtkTextDirection gtk_style_context_get_direction (GtkStyleContext *context);
+
+void gtk_style_context_set_junction_sides (GtkStyleContext *context,
+ GtkJunctionSides sides);
+GtkJunctionSides gtk_style_context_get_junction_sides (GtkStyleContext *context);
+
+gboolean gtk_style_context_lookup_color (GtkStyleContext *context,
+ const gchar *color_name,
+ GdkRGBA *color);
+
+void gtk_style_context_notify_state_change (GtkStyleContext *context,
+ GdkWindow *window,
+ gpointer region_id,
+ GtkStateType state,
+ gboolean state_value);
+void gtk_style_context_push_animatable_region (GtkStyleContext *context,
+ gpointer region_id);
+void gtk_style_context_pop_animatable_region (GtkStyleContext *context);
+
+/* Some helper functions to retrieve most common properties */
+void gtk_style_context_get_color (GtkStyleContext *context,
+ GtkStateFlags state,
+ GdkRGBA *color);
+void gtk_style_context_get_background_color (GtkStyleContext *context,
+ GtkStateFlags state,
+ GdkRGBA *color);
+void gtk_style_context_get_border_color (GtkStyleContext *context,
+ GtkStateFlags state,
+ GdkRGBA *color);
+
+void gtk_style_context_get_border (GtkStyleContext *context,
+ GtkStateFlags state,
+ GtkBorder *border);
+void gtk_style_context_get_padding (GtkStyleContext *context,
+ GtkStateFlags state,
+ GtkBorder *padding);
+void gtk_style_context_get_margin (GtkStyleContext *context,
+ GtkStateFlags state,
+ GtkBorder *margin);
+
+/* Semi-private API */
+const GValue * _gtk_style_context_peek_style_property (GtkStyleContext *context,
+ GType widget_type,
+ GtkStateFlags state,
+ GParamSpec *pspec);
+void _gtk_style_context_invalidate_animation_areas (GtkStyleContext *context);
+void _gtk_style_context_coalesce_animation_areas (GtkStyleContext *context,
+ gint rel_x,
+ gint rel_y);
+
+void gtk_style_context_invalidate (GtkStyleContext *context);
+void gtk_style_context_reset_widgets (GdkScreen *screen);
+
+void gtk_style_context_set_background (GtkStyleContext *context,
+ GdkWindow *window);
+
+/* Paint methods */
+void gtk_render_check (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+void gtk_render_option (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+void gtk_render_arrow (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble angle,
+ gdouble x,
+ gdouble y,
+ gdouble size);
+void gtk_render_background (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+void gtk_render_frame (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+void gtk_render_expander (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+void gtk_render_focus (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+void gtk_render_layout (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ PangoLayout *layout);
+void gtk_render_line (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1);
+void gtk_render_slider (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkOrientation orientation);
+void gtk_render_frame_gap (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkPositionType gap_side,
+ gdouble xy0_gap,
+ gdouble xy1_gap);
+void gtk_render_extension (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkPositionType gap_side);
+void gtk_render_handle (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+void gtk_render_activity (GtkStyleContext *context,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+
+GdkPixbuf * gtk_render_icon_pixbuf (GtkStyleContext *context,
+ const GtkIconSource *source,
+ GtkIconSize size);
+
+G_END_DECLS
+
+#endif /* __GTK_STYLE_CONTEXT_H__ */
diff --git a/gtk/gtkstyleproperties.c b/gtk/gtkstyleproperties.c
new file mode 100644
index 0000000000..c6592740fc
--- /dev/null
+++ b/gtk/gtkstyleproperties.c
@@ -0,0 +1,1226 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gtkstyleproperties.h"
+
+#include <stdlib.h>
+#include <gobject/gvaluecollector.h>
+#include <cairo-gobject.h>
+
+#include "gtktypebuiltins.h"
+#include "gtkstyleprovider.h"
+#include "gtksymboliccolor.h"
+#include "gtkprivate.h"
+#include "gtkthemingengine.h"
+#include "gtkanimationdescription.h"
+#include "gtkborder.h"
+#include "gtkgradient.h"
+#include "gtk9slice.h"
+#include "gtkintl.h"
+
+/**
+ * SECTION:gtkstyleproperties
+ * @Short_description: Store for style property information
+ * @Title: GtkStyleProperties
+ *
+ * GtkStyleProperties provides the storage for style information
+ * that is used by #GtkStyleContext and other #GtkStyleProvider
+ * implementations.
+ *
+ * Before style properties can be stored in GtkStyleProperties, they
+ * must be registered with gtk_style_properties_register_property().
+ *
+ * Unless you are writing a #GtkStyleProvider implementation, you
+ * are unlikely to use this API directly, as gtk_style_context_get()
+ * and its variants are the preferred way to access styling information
+ * from widget implementations and theming engine implementations
+ * should use the APIs provided by #GtkThemingEngine instead.
+ */
+
+typedef struct GtkStylePropertiesPrivate GtkStylePropertiesPrivate;
+typedef struct PropertyData PropertyData;
+typedef struct PropertyNode PropertyNode;
+typedef struct ValueData ValueData;
+
+struct PropertyNode
+{
+ GQuark property_quark;
+ GParamSpec *pspec;
+ GtkStylePropertyParser parse_func;
+};
+
+struct ValueData
+{
+ GtkStateFlags state;
+ GValue value;
+};
+
+struct PropertyData
+{
+ GArray *values;
+};
+
+struct GtkStylePropertiesPrivate
+{
+ GHashTable *color_map;
+ GHashTable *properties;
+};
+
+static GArray *properties = NULL;
+
+static void gtk_style_properties_provider_init (GtkStyleProviderIface *iface);
+static void gtk_style_properties_finalize (GObject *object);
+
+
+G_DEFINE_TYPE_EXTENDED (GtkStyleProperties, gtk_style_properties, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
+ gtk_style_properties_provider_init));
+
+static void
+gtk_style_properties_class_init (GtkStylePropertiesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_style_properties_finalize;
+
+ /* Initialize default property set */
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_boxed ("color",
+ "Foreground color",
+ "Foreground color",
+ GDK_TYPE_RGBA, 0));
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_boxed ("background-color",
+ "Background color",
+ "Background color",
+ GDK_TYPE_RGBA, 0));
+
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_boxed ("font",
+ "Font Description",
+ "Font Description",
+ PANGO_TYPE_FONT_DESCRIPTION, 0));
+
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_boxed ("margin",
+ "Margin",
+ "Margin",
+ GTK_TYPE_BORDER, 0));
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_boxed ("padding",
+ "Padding",
+ "Padding",
+ GTK_TYPE_BORDER, 0));
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_boxed ("border-width",
+ "Border width",
+ "Border width, in pixels",
+ GTK_TYPE_BORDER, 0));
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_int ("border-radius",
+ "Border radius",
+ "Border radius, in pixels",
+ 0, G_MAXINT, 0, 0));
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_enum ("border-style",
+ "Border style",
+ "Border style",
+ GTK_TYPE_BORDER_STYLE,
+ GTK_BORDER_STYLE_NONE, 0));
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_boxed ("border-color",
+ "Border color",
+ "Border color",
+ GDK_TYPE_RGBA, 0));
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_boxed ("background-image",
+ "Background Image",
+ "Background Image",
+ CAIRO_GOBJECT_TYPE_PATTERN, 0));
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_boxed ("border-image",
+ "Border Image",
+ "Border Image",
+ GTK_TYPE_9SLICE, 0));
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_object ("engine",
+ "Theming Engine",
+ "Theming Engine",
+ GTK_TYPE_THEMING_ENGINE, 0));
+ gtk_style_properties_register_property (NULL,
+ g_param_spec_boxed ("transition",
+ "Transition animation description",
+ "Transition animation description",
+ GTK_TYPE_ANIMATION_DESCRIPTION, 0));
+
+ g_type_class_add_private (object_class, sizeof (GtkStylePropertiesPrivate));
+}
+
+static PropertyData *
+property_data_new (void)
+{
+ PropertyData *data;
+
+ data = g_slice_new0 (PropertyData);
+ data->values = g_array_new (FALSE, FALSE, sizeof (ValueData));
+
+ return data;
+}
+
+static void
+property_data_free (PropertyData *data)
+{
+ guint i;
+
+ for (i = 0; i < data->values->len; i++)
+ {
+ ValueData *value_data;
+
+ value_data = &g_array_index (data->values, ValueData, i);
+
+ if (G_IS_VALUE (&value_data->value))
+ g_value_unset (&value_data->value);
+ }
+
+ g_array_free (data->values, TRUE);
+ g_slice_free (PropertyData, data);
+}
+
+static gboolean
+property_data_find_position (PropertyData *data,
+ GtkStateFlags state,
+ guint *pos)
+{
+ gint min, max, mid;
+ gboolean found = FALSE;
+ guint position;
+
+ if (pos)
+ *pos = 0;
+
+ if (data->values->len == 0)
+ return FALSE;
+
+ /* Find position for the given state, or the position where
+ * it would be if not found, the array is ordered by the
+ * state flags.
+ */
+ min = 0;
+ max = data->values->len - 1;
+
+ do
+ {
+ ValueData *value_data;
+
+ mid = (min + max) / 2;
+ value_data = &g_array_index (data->values, ValueData, mid);
+
+ if (value_data->state == state)
+ {
+ found = TRUE;
+ position = mid;
+ }
+ else if (value_data->state < state)
+ position = min = mid + 1;
+ else
+ {
+ max = mid - 1;
+ position = mid;
+ }
+ }
+ while (!found && min <= max);
+
+ if (pos)
+ *pos = position;
+
+ return found;
+}
+
+static GValue *
+property_data_get_value (PropertyData *data,
+ GtkStateFlags state)
+{
+ ValueData *val_data;
+ guint pos;
+
+ if (!property_data_find_position (data, state, &pos))
+ {
+ ValueData new = { 0 };
+
+ new.state = state;
+ g_array_insert_val (data->values, pos, new);
+ }
+
+ val_data = &g_array_index (data->values, ValueData, pos);
+
+ return &val_data->value;
+}
+
+static GValue *
+property_data_match_state (PropertyData *data,
+ GtkStateFlags state)
+{
+ guint pos;
+ gint i;
+
+ if (property_data_find_position (data, state, &pos))
+ {
+ ValueData *val_data;
+
+ /* Exact match */
+ val_data = &g_array_index (data->values, ValueData, pos);
+ return &val_data->value;
+ }
+
+ if (pos >= data->values->len)
+ pos = data->values->len - 1;
+
+ /* No exact match, go downwards the list to find
+ * the closest match to the given state flags, as
+ * a side effect, there is an implicit precedence
+ * of higher flags over the smaller ones.
+ */
+ for (i = pos; i >= 0; i--)
+ {
+ ValueData *val_data;
+
+ val_data = &g_array_index (data->values, ValueData, i);
+
+ /* Check whether any of the requested
+ * flags are set, and no other flags are.
+ *
+ * Also, no flags acts as a wildcard, such
+ * value should be always in the first position
+ * in the array (if present) anyways.
+ */
+ if (val_data->state == 0 ||
+ ((val_data->state & state) != 0 &&
+ (val_data->state & ~state) == 0))
+ return &val_data->value;
+ }
+
+ return NULL;
+}
+
+static void
+gtk_style_properties_init (GtkStyleProperties *props)
+{
+ GtkStylePropertiesPrivate *priv;
+
+ priv = props->priv = G_TYPE_INSTANCE_GET_PRIVATE (props,
+ GTK_TYPE_STYLE_PROPERTIES,
+ GtkStylePropertiesPrivate);
+
+ priv->properties = g_hash_table_new_full (NULL, NULL, NULL,
+ (GDestroyNotify) property_data_free);
+}
+
+static void
+gtk_style_properties_finalize (GObject *object)
+{
+ GtkStylePropertiesPrivate *priv;
+ GtkStyleProperties *props;
+
+ props = GTK_STYLE_PROPERTIES (object);
+ priv = props->priv;
+ g_hash_table_destroy (priv->properties);
+
+ if (priv->color_map)
+ g_hash_table_destroy (priv->color_map);
+
+ G_OBJECT_CLASS (gtk_style_properties_parent_class)->finalize (object);
+}
+
+GtkStyleProperties *
+gtk_style_properties_get_style (GtkStyleProvider *provider,
+ GtkWidgetPath *path)
+{
+ /* Return style set itself */
+ return g_object_ref (provider);
+}
+
+static void
+gtk_style_properties_provider_init (GtkStyleProviderIface *iface)
+{
+ iface->get_style = gtk_style_properties_get_style;
+}
+
+static int
+compare_property (gconstpointer p1,
+ gconstpointer p2)
+{
+ PropertyNode *key = (PropertyNode *) p1;
+ PropertyNode *node = (PropertyNode *) p2;
+
+ if (key->property_quark > node->property_quark)
+ return 1;
+ else if (key->property_quark < node->property_quark)
+ return -1;
+
+ return 0;
+}
+
+static PropertyNode *
+property_node_lookup (GQuark quark)
+{
+ PropertyNode key = { 0 };
+
+ if (!quark)
+ return NULL;
+
+ if (!properties)
+ return NULL;
+
+ key.property_quark = quark;
+
+ return bsearch (&key, properties->data, properties->len,
+ sizeof (PropertyNode), compare_property);
+}
+
+/* Property registration functions */
+
+/**
+ * gtk_style_properties_register_property:
+ * @parse_func: parsing function to use, or %NULL
+ * @pspec: the #GParamSpec for the new property
+ *
+ * Registers a property so it can be used in the CSS file format.
+ * This function is the low-level equivalent of
+ * gtk_theming_engine_register_property(), if you are implementing
+ * a theming engine, you want to use that function instead.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_properties_register_property (GtkStylePropertyParser parse_func,
+ GParamSpec *pspec)
+{
+ PropertyNode *node, new = { 0 };
+ GQuark quark;
+ gint i;
+
+ g_return_if_fail (G_IS_PARAM_SPEC (pspec));
+
+ if (G_UNLIKELY (!properties))
+ properties = g_array_new (FALSE, TRUE, sizeof (PropertyNode));
+
+ quark = g_quark_from_string (pspec->name);
+
+ if ((node = property_node_lookup (quark)) != NULL)
+ {
+ g_warning ("Property \"%s\" was already registered with type %s",
+ pspec->name, g_type_name (node->pspec->value_type));
+ return;
+ }
+
+ new.property_quark = quark;
+ new.pspec = pspec;
+
+ if (parse_func)
+ new.parse_func = parse_func;
+
+ for (i = 0; i < properties->len; i++)
+ {
+ node = &g_array_index (properties, PropertyNode, i);
+
+ if (node->property_quark > quark)
+ break;
+ }
+
+ g_array_insert_val (properties, i, new);
+}
+
+/**
+ * gtk_style_properties_lookup_property:
+ * @property_name: property name to look up
+ * @parse_func: (out): return location for the parse function
+ * @pspec: (out): return location for the #GParamSpec
+ *
+ * Returns %TRUE if a property has been registered, if @pspec or
+ * @parse_func are not %NULL, the #GParamSpec and parsing function
+ * will be respectively returned.
+ *
+ * Returns: %TRUE if the property is registered, %FALSE otherwise
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_style_properties_lookup_property (const gchar *property_name,
+ GtkStylePropertyParser *parse_func,
+ GParamSpec **pspec)
+{
+ PropertyNode *node;
+ GtkStylePropertiesClass *klass;
+ gboolean found = FALSE;
+ GQuark quark;
+ gint i;
+
+ g_return_val_if_fail (property_name != NULL, FALSE);
+
+ klass = g_type_class_ref (GTK_TYPE_STYLE_PROPERTIES);
+ quark = g_quark_try_string (property_name);
+
+ if (quark == 0)
+ {
+ g_type_class_unref (klass);
+ return FALSE;
+ }
+
+ for (i = 0; i < properties->len; i++)
+ {
+ node = &g_array_index (properties, PropertyNode, i);
+
+ if (node->property_quark == quark)
+ {
+ if (pspec)
+ *pspec = node->pspec;
+
+ if (parse_func)
+ *parse_func = node->parse_func;
+
+ found = TRUE;
+ break;
+ }
+ else if (node->property_quark > quark)
+ break;
+ }
+
+ g_type_class_unref (klass);
+
+ return found;
+}
+
+/* GtkStyleProperties methods */
+
+/**
+ * gtk_style_properties_new:
+ *
+ * Returns a newly created #GtkStyleProperties
+ *
+ * Returns: a new #GtkStyleProperties
+ **/
+GtkStyleProperties *
+gtk_style_properties_new (void)
+{
+ return g_object_new (GTK_TYPE_STYLE_PROPERTIES, NULL);
+}
+
+/**
+ * gtk_style_properties_map_color:
+ * @props: a #GtkStyleProperties
+ * @name: color name
+ * @color: #GtkSymbolicColor to map @name to
+ *
+ * Maps @color so it can be referenced by @name. See
+ * gtk_style_properties_lookup_color()
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_properties_map_color (GtkStyleProperties *props,
+ const gchar *name,
+ GtkSymbolicColor *color)
+{
+ GtkStylePropertiesPrivate *priv;
+
+ g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (color != NULL);
+
+ priv = props->priv;
+
+ if (G_UNLIKELY (!priv->color_map))
+ priv->color_map = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) gtk_symbolic_color_unref);
+
+ g_hash_table_replace (priv->color_map,
+ g_strdup (name),
+ gtk_symbolic_color_ref (color));
+}
+
+/**
+ * gtk_style_properties_lookup_color:
+ * @props: a #GtkStyleProperties
+ * @name: color name to lookup
+ *
+ * Returns the symbolic color that is mapped
+ * to @name.
+ *
+ * Returns: The mapped color
+ *
+ * Since: 3.0
+ **/
+GtkSymbolicColor *
+gtk_style_properties_lookup_color (GtkStyleProperties *props,
+ const gchar *name)
+{
+ GtkStylePropertiesPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ priv = props->priv;
+
+ if (!priv->color_map)
+ return NULL;
+
+ return g_hash_table_lookup (priv->color_map, name);
+}
+
+/**
+ * gtk_style_properties_set_property:
+ * @props: a #GtkStyleProperties
+ * @property: styling property to set
+ * @state: state to set the value for
+ * @value: new value for the property
+ *
+ * Sets a styling property in @props.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_properties_set_property (GtkStyleProperties *props,
+ const gchar *property,
+ GtkStateFlags state,
+ const GValue *value)
+{
+ GtkStylePropertiesPrivate *priv;
+ PropertyNode *node;
+ PropertyData *prop;
+ GType value_type;
+ GValue *val;
+
+ g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
+ g_return_if_fail (property != NULL);
+ g_return_if_fail (value != NULL);
+
+ value_type = G_VALUE_TYPE (value);
+ node = property_node_lookup (g_quark_try_string (property));
+
+ if (!node)
+ {
+ g_warning ("Style property \"%s\" is not registered", property);
+ return;
+ }
+
+ if (node->pspec->value_type == GDK_TYPE_RGBA ||
+ node->pspec->value_type == GDK_TYPE_COLOR)
+ {
+ /* Allow GtkSymbolicColor as well */
+ g_return_if_fail (value_type == GDK_TYPE_RGBA ||
+ value_type == GDK_TYPE_COLOR ||
+ value_type == GTK_TYPE_SYMBOLIC_COLOR);
+ }
+ else if (node->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN)
+ {
+ /* Allow GtkGradient as a substitute */
+ g_return_if_fail (value_type == CAIRO_GOBJECT_TYPE_PATTERN ||
+ value_type == GTK_TYPE_GRADIENT);
+ }
+ else
+ g_return_if_fail (node->pspec->value_type == value_type);
+
+ priv = props->priv;
+ prop = g_hash_table_lookup (priv->properties,
+ GINT_TO_POINTER (node->property_quark));
+
+ if (!prop)
+ {
+ prop = property_data_new ();
+ g_hash_table_insert (priv->properties,
+ GINT_TO_POINTER (node->property_quark),
+ prop);
+ }
+
+ val = property_data_get_value (prop, state);
+
+ if (G_VALUE_TYPE (val) == value_type)
+ g_value_reset (val);
+ else
+ {
+ if (G_IS_VALUE (val))
+ g_value_unset (val);
+
+ g_value_init (val, value_type);
+ }
+
+ g_value_copy (value, val);
+}
+
+/**
+ * gtk_style_properties_set_valist:
+ * @props: a #GtkStyleProperties
+ * @state: state to set the values for
+ * @args: va_list of property name/value pairs, followed by %NULL
+ *
+ * Sets several style properties on @props.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_properties_set_valist (GtkStyleProperties *props,
+ GtkStateFlags state,
+ va_list args)
+{
+ GtkStylePropertiesPrivate *priv;
+ const gchar *property_name;
+
+ g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
+
+ priv = props->priv;
+ property_name = va_arg (args, const gchar *);
+
+ while (property_name)
+ {
+ PropertyNode *node;
+ PropertyData *prop;
+ gchar *error = NULL;
+ GValue *val;
+
+ node = property_node_lookup (g_quark_try_string (property_name));
+
+ if (!node)
+ {
+ g_warning ("Style property \"%s\" is not registered", property_name);
+ break;
+ }
+
+ prop = g_hash_table_lookup (priv->properties,
+ GINT_TO_POINTER (node->property_quark));
+
+ if (!prop)
+ {
+ prop = property_data_new ();
+ g_hash_table_insert (priv->properties,
+ GINT_TO_POINTER (node->property_quark),
+ prop);
+ }
+
+ val = property_data_get_value (prop, state);
+
+ if (G_IS_VALUE (val))
+ g_value_unset (val);
+
+ g_value_init (val, node->pspec->value_type);
+ G_VALUE_COLLECT (val, args, 0, &error);
+
+ if (error)
+ {
+ g_warning ("Could not set style property \"%s\": %s", property_name, error);
+ g_value_unset (val);
+ g_free (error);
+ break;
+ }
+
+ property_name = va_arg (args, const gchar *);
+ }
+}
+
+/**
+ * gtk_style_properties_set:
+ * @props: a #GtkStyleProperties
+ * @state: state to set the values for
+ * @...: property name/value pairs, followed by %NULL
+ *
+ * Sets several style properties on @props.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_properties_set (GtkStyleProperties *props,
+ GtkStateFlags state,
+ ...)
+{
+ va_list args;
+
+ g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
+
+ va_start (args, state);
+ gtk_style_properties_set_valist (props, state, args);
+ va_end (args);
+}
+
+static gboolean
+resolve_color (GtkStyleProperties *props,
+ GValue *value)
+{
+ GdkRGBA color;
+
+ /* Resolve symbolic color to GdkRGBA */
+ if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &color))
+ return FALSE;
+
+ /* Store it back, this is where GdkRGBA caching happens */
+ g_value_unset (value);
+ g_value_init (value, GDK_TYPE_RGBA);
+ g_value_set_boxed (value, &color);
+
+ return TRUE;
+}
+
+static gboolean
+resolve_color_rgb (GtkStyleProperties *props,
+ GValue *value)
+{
+ GdkColor color = { 0 };
+ GdkRGBA rgba;
+
+ if (!gtk_symbolic_color_resolve (g_value_get_boxed (value), props, &rgba))
+ return FALSE;
+
+ color.red = rgba.red * 65535. + 0.5;
+ color.green = rgba.green * 65535. + 0.5;
+ color.blue = rgba.blue * 65535. + 0.5;
+
+ g_value_unset (value);
+ g_value_init (value, GDK_TYPE_COLOR);
+ g_value_set_boxed (value, &color);
+
+ return TRUE;
+}
+
+static gboolean
+resolve_gradient (GtkStyleProperties *props,
+ GValue *value)
+{
+ cairo_pattern_t *gradient;
+
+ if (!gtk_gradient_resolve (g_value_get_boxed (value), props, &gradient))
+ return FALSE;
+
+ /* Store it back, this is where cairo_pattern_t caching happens */
+ g_value_unset (value);
+ g_value_init (value, CAIRO_GOBJECT_TYPE_PATTERN);
+ g_value_take_boxed (value, gradient);
+
+ return TRUE;
+}
+
+static gboolean
+style_properties_resolve_type (GtkStyleProperties *props,
+ PropertyNode *node,
+ GValue *val)
+{
+ if (val && G_VALUE_TYPE (val) == GTK_TYPE_SYMBOLIC_COLOR)
+ {
+ if (node->pspec->value_type == GDK_TYPE_RGBA)
+ {
+ if (!resolve_color (props, val))
+ return FALSE;
+ }
+ else if (node->pspec->value_type == GDK_TYPE_COLOR)
+ {
+ if (!resolve_color_rgb (props, val))
+ return FALSE;
+ }
+ else
+ return FALSE;
+ }
+ else if (val && G_VALUE_TYPE (val) == GTK_TYPE_GRADIENT)
+ {
+ g_return_val_if_fail (node->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN, FALSE);
+
+ if (!resolve_gradient (props, val))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+lookup_default_value (PropertyNode *node,
+ GValue *value)
+{
+ if (node->pspec->value_type == GTK_TYPE_THEMING_ENGINE)
+ g_value_set_object (value, gtk_theming_engine_load (NULL));
+ else
+ g_param_value_set_default (node->pspec, value);
+}
+
+const GValue *
+_gtk_style_properties_peek_property (GtkStyleProperties *props,
+ const gchar *prop_name,
+ GtkStateFlags state)
+{
+ GtkStylePropertiesPrivate *priv;
+ PropertyNode *node;
+ PropertyData *prop;
+ GValue *val;
+
+ g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), NULL);
+ g_return_val_if_fail (prop_name != NULL, NULL);
+
+ node = property_node_lookup (g_quark_try_string (prop_name));
+
+ if (!node)
+ {
+ g_warning ("Style property \"%s\" is not registered", prop_name);
+ return NULL;
+ }
+
+ priv = props->priv;
+ prop = g_hash_table_lookup (priv->properties,
+ GINT_TO_POINTER (node->property_quark));
+
+ if (!prop)
+ return NULL;
+
+ val = property_data_match_state (prop, state);
+
+ if (val &&
+ !style_properties_resolve_type (props, node, val))
+ return NULL;
+
+ return val;
+}
+
+/**
+ * gtk_style_properties_get_property:
+ * @props: a #GtkStyleProperties
+ * @property: style property name
+ * @state: state to retrieve the property value for
+ * @value: (out) (transfer full): return location for the style property value.
+ *
+ * Gets a style property from @props for the given state. When done with @value,
+ * g_value_unset() needs to be called to free any allocated memory.
+ *
+ * Returns: %TRUE if the property exists in @props, %FALSE otherwise
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_style_properties_get_property (GtkStyleProperties *props,
+ const gchar *property,
+ GtkStateFlags state,
+ GValue *value)
+{
+ GtkStylePropertiesPrivate *priv;
+ PropertyNode *node;
+ PropertyData *prop;
+ GValue *val;
+
+ g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
+ g_return_val_if_fail (property != NULL, FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ node = property_node_lookup (g_quark_try_string (property));
+
+ if (!node)
+ {
+ g_warning ("Style property \"%s\" is not registered", property);
+ return FALSE;
+ }
+
+ priv = props->priv;
+ prop = g_hash_table_lookup (priv->properties,
+ GINT_TO_POINTER (node->property_quark));
+
+ if (!prop)
+ return FALSE;
+
+ g_value_init (value, node->pspec->value_type);
+ val = property_data_match_state (prop, state);
+
+ if (val &&
+ !style_properties_resolve_type (props, node, val))
+ return FALSE;
+
+ if (val)
+ {
+ g_param_value_validate (node->pspec, val);
+ g_value_copy (val, value);
+ }
+ else
+ lookup_default_value (node, value);
+
+ return TRUE;
+}
+
+/**
+ * gtk_style_properties_get_valist:
+ * @props: a #GtkStyleProperties
+ * @state: state to retrieve the property values for
+ * @args: va_list of property name/return location pairs, followed by %NULL
+ *
+ * Retrieves several style property values from @props for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_properties_get_valist (GtkStyleProperties *props,
+ GtkStateFlags state,
+ va_list args)
+{
+ GtkStylePropertiesPrivate *priv;
+ const gchar *property_name;
+
+ g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
+
+ priv = props->priv;
+ property_name = va_arg (args, const gchar *);
+
+ while (property_name)
+ {
+ PropertyNode *node;
+ PropertyData *prop;
+ gchar *error = NULL;
+ GValue *val = NULL;
+
+ node = property_node_lookup (g_quark_try_string (property_name));
+
+ if (!node)
+ {
+ g_warning ("Style property \"%s\" is not registered", property_name);
+ break;
+ }
+
+ prop = g_hash_table_lookup (priv->properties,
+ GINT_TO_POINTER (node->property_quark));
+
+ if (prop)
+ val = property_data_match_state (prop, state);
+
+ if (val &&
+ !style_properties_resolve_type (props, node, val))
+ val = NULL;
+
+ if (val)
+ {
+ g_param_value_validate (node->pspec, val);
+ G_VALUE_LCOPY (val, args, 0, &error);
+ }
+ else
+ {
+ GValue default_value = { 0 };
+
+ g_value_init (&default_value, node->pspec->value_type);
+ lookup_default_value (node, &default_value);
+ G_VALUE_LCOPY (&default_value, args, 0, &error);
+ g_value_unset (&default_value);
+ }
+
+ if (error)
+ {
+ g_warning ("Could not get style property \"%s\": %s", property_name, error);
+ g_free (error);
+ break;
+ }
+
+ property_name = va_arg (args, const gchar *);
+ }
+}
+
+/**
+ * gtk_style_properties_get:
+ * @props: a #GtkStyleProperties
+ * @state: state to retrieve the property values for
+ * @...: property name /return value pairs, followed by %NULL
+ *
+ * Retrieves several style property values from @props for a
+ * given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_properties_get (GtkStyleProperties *props,
+ GtkStateFlags state,
+ ...)
+{
+ va_list args;
+
+ g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
+
+ va_start (args, state);
+ gtk_style_properties_get_valist (props, state, args);
+ va_end (args);
+}
+
+/**
+ * gtk_style_properties_unset_property:
+ * @props: a #GtkStyleProperties
+ * @property: property to unset
+ * @state: state to unset
+ *
+ * Unsets a style property in @props.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_properties_unset_property (GtkStyleProperties *props,
+ const gchar *property,
+ GtkStateFlags state)
+{
+ GtkStylePropertiesPrivate *priv;
+ PropertyNode *node;
+ PropertyData *prop;
+ guint pos;
+
+ g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
+ g_return_if_fail (property != NULL);
+
+ node = property_node_lookup (g_quark_try_string (property));
+
+ if (!node)
+ {
+ g_warning ("Style property \"%s\" is not registered", property);
+ return;
+ }
+
+ priv = props->priv;
+ prop = g_hash_table_lookup (priv->properties,
+ GINT_TO_POINTER (node->property_quark));
+
+ if (!prop)
+ return;
+
+ if (property_data_find_position (prop, state, &pos))
+ {
+ ValueData *data;
+
+ data = &g_array_index (prop->values, ValueData, pos);
+
+ if (G_IS_VALUE (&data->value))
+ g_value_unset (&data->value);
+
+ g_array_remove_index (prop->values, pos);
+ }
+}
+
+/**
+ * gtk_style_properties_clear:
+ * @props: a #GtkStyleProperties
+ *
+ * Clears all style information from @props.
+ **/
+void
+gtk_style_properties_clear (GtkStyleProperties *props)
+{
+ GtkStylePropertiesPrivate *priv;
+
+ g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
+
+ priv = props->priv;
+ g_hash_table_remove_all (priv->properties);
+}
+
+/**
+ * gtk_style_properties_merge:
+ * @props: a #GtkStyleProperties
+ * @props_to_merge: a second #GtkStyleProperties
+ * @replace: whether to replace values or not
+ *
+ * Merges into @props all the style information contained
+ * in @props_to_merge. If @replace is %TRUE, the values
+ * will be overwritten, if it is %FALSE, the older values
+ * will prevail.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_style_properties_merge (GtkStyleProperties *props,
+ const GtkStyleProperties *props_to_merge,
+ gboolean replace)
+{
+ GtkStylePropertiesPrivate *priv, *priv_to_merge;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
+ g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props_to_merge));
+
+ priv = props->priv;
+ priv_to_merge = props_to_merge->priv;
+
+ /* Merge symbolic color map */
+ if (priv_to_merge->color_map)
+ {
+ g_hash_table_iter_init (&iter, priv_to_merge->color_map);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ const gchar *name;
+ GtkSymbolicColor *color;
+
+ name = key;
+ color = value;
+
+ if (!replace &&
+ g_hash_table_lookup (priv->color_map, name))
+ continue;
+
+ gtk_style_properties_map_color (props, name, color);
+ }
+ }
+
+ /* Merge symbolic style properties */
+ g_hash_table_iter_init (&iter, priv_to_merge->properties);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ PropertyData *prop_to_merge = value;
+ PropertyData *prop;
+ guint i;
+
+ prop = g_hash_table_lookup (priv->properties, key);
+
+ if (!prop)
+ {
+ prop = property_data_new ();
+ g_hash_table_insert (priv->properties, key, prop);
+ }
+
+ for (i = 0; i < prop_to_merge->values->len; i++)
+ {
+ ValueData *data;
+ GValue *value;
+
+ data = &g_array_index (prop_to_merge->values, ValueData, i);
+ value = property_data_get_value (prop, data->state);
+
+ if (G_VALUE_TYPE (&data->value) == PANGO_TYPE_FONT_DESCRIPTION &&
+ G_IS_VALUE (value))
+ {
+ PangoFontDescription *font_desc;
+ PangoFontDescription *font_desc_to_merge;
+
+ /* Handle merging of font descriptions */
+ font_desc = g_value_get_boxed (value);
+ font_desc_to_merge = g_value_get_boxed (&data->value);
+
+ pango_font_description_merge (font_desc, font_desc_to_merge, replace);
+ }
+ else if (replace || !G_IS_VALUE (value))
+ {
+ if (!G_IS_VALUE (value))
+ g_value_init (value, G_VALUE_TYPE (&data->value));
+ else if (G_VALUE_TYPE (value) != G_VALUE_TYPE (&data->value))
+ {
+ g_value_unset (value);
+ g_value_init (value, G_VALUE_TYPE (&data->value));
+ }
+
+ g_value_copy (&data->value, value);
+ }
+ }
+ }
+}
diff --git a/gtk/gtkstyleproperties.h b/gtk/gtkstyleproperties.h
new file mode 100644
index 0000000000..113fe44f93
--- /dev/null
+++ b/gtk/gtkstyleproperties.h
@@ -0,0 +1,117 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_STYLE_PROPERTIES_H__
+#define __GTK_STYLE_PROPERTIES_H__
+
+#include <glib-object.h>
+#include <gdk/gdk.h>
+#include <gtk/gtkenums.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_STYLE_PROPERTIES (gtk_style_properties_get_type ())
+#define GTK_STYLE_PROPERTIES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_STYLE_PROPERTIES, GtkStyleProperties))
+#define GTK_STYLE_PROPERTIES_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_STYLE_PROPERTIES, GtkStylePropertiesClass))
+#define GTK_IS_STYLE_PROPERTIES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_STYLE_PROPERTIES))
+#define GTK_IS_STYLE_PROPERTIES_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GTK_TYPE_STYLE_PROPERTIES))
+#define GTK_STYLE_PROPERTIES_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_STYLE_PROPERTIES, GtkStylePropertiesClass))
+
+typedef struct _GtkStyleProperties GtkStyleProperties;
+typedef struct _GtkStylePropertiesClass GtkStylePropertiesClass;
+
+typedef struct _GtkSymbolicColor GtkSymbolicColor;
+typedef struct _GtkGradient GtkGradient;
+
+struct _GtkStyleProperties
+{
+ GObject parent_object;
+ gpointer priv;
+};
+
+struct _GtkStylePropertiesClass
+{
+ GObjectClass parent_class;
+};
+
+typedef gboolean (* GtkStylePropertyParser) (const gchar *string,
+ GValue *value,
+ GError **error);
+
+GType gtk_style_properties_get_type (void) G_GNUC_CONST;
+
+/* Semi-private API */
+const GValue * _gtk_style_properties_peek_property (GtkStyleProperties *props,
+ const gchar *prop_name,
+ GtkStateFlags state);
+
+/* Functions to register style properties */
+void gtk_style_properties_register_property (GtkStylePropertyParser parse_func,
+ GParamSpec *pspec);
+gboolean gtk_style_properties_lookup_property (const gchar *property_name,
+ GtkStylePropertyParser *parse_func,
+ GParamSpec **pspec);
+
+GtkStyleProperties * gtk_style_properties_new (void);
+
+void gtk_style_properties_map_color (GtkStyleProperties *props,
+ const gchar *name,
+ GtkSymbolicColor *color);
+GtkSymbolicColor * gtk_style_properties_lookup_color (GtkStyleProperties *props,
+ const gchar *name);
+
+void gtk_style_properties_set_property (GtkStyleProperties *props,
+ const gchar *property,
+ GtkStateFlags state,
+ const GValue *value);
+void gtk_style_properties_set_valist (GtkStyleProperties *props,
+ GtkStateFlags state,
+ va_list args);
+void gtk_style_properties_set (GtkStyleProperties *props,
+ GtkStateFlags state,
+ ...) G_GNUC_NULL_TERMINATED;
+
+gboolean gtk_style_properties_get_property (GtkStyleProperties *props,
+ const gchar *property,
+ GtkStateFlags state,
+ GValue *value);
+void gtk_style_properties_get_valist (GtkStyleProperties *props,
+ GtkStateFlags state,
+ va_list args);
+void gtk_style_properties_get (GtkStyleProperties *props,
+ GtkStateFlags state,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gtk_style_properties_unset_property (GtkStyleProperties *props,
+ const gchar *property,
+ GtkStateFlags state);
+
+void gtk_style_properties_clear (GtkStyleProperties *props);
+
+void gtk_style_properties_merge (GtkStyleProperties *props,
+ const GtkStyleProperties *props_to_merge,
+ gboolean replace);
+
+G_END_DECLS
+
+#endif /* __GTK_STYLE_PROPERTIES_H__ */
diff --git a/gtk/gtkstyleprovider.c b/gtk/gtkstyleprovider.c
new file mode 100644
index 0000000000..8ed0d975ca
--- /dev/null
+++ b/gtk/gtkstyleprovider.c
@@ -0,0 +1,148 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gtkprivate.h"
+#include "gtkstyleprovider.h"
+#include "gtkintl.h"
+
+/**
+ * SECTION:gtkstyleprovider
+ * @Short_description: Interface to provide style information to GtkStyleContext
+ * @Title: GtkStyleProvider
+ * @See_also: #GtkStyleContext, #GtkCssProvider
+ *
+ * GtkStyleProvider is an interface used to provide style information to a #GtkStyleContext,
+ * see gtk_style_context_add_provider() and gtk_style_context_add_provider_for_screen().
+ */
+
+static void gtk_style_provider_iface_init (gpointer g_iface);
+
+GType
+gtk_style_provider_get_type (void)
+{
+ static GType style_provider_type = 0;
+
+ if (!style_provider_type)
+ style_provider_type = g_type_register_static_simple (G_TYPE_INTERFACE,
+ I_("GtkStyleProvider"),
+ sizeof (GtkStyleProviderIface),
+ (GClassInitFunc) gtk_style_provider_iface_init,
+ 0, NULL, 0);
+ return style_provider_type;
+}
+
+static void
+gtk_style_provider_iface_init (gpointer g_iface)
+{
+}
+
+/**
+ * gtk_style_provider_get_style:
+ * @provider: a #GtkStyleProvider
+ * @path: #GtkWidgetPath to query
+ *
+ * Returns the style settings affecting a widget defined by @path, or %NULL if
+ * @provider doesn't contemplate styling @path.
+ *
+ * Returns: a #GtkStyleProperties containing the style settings affecting @path
+ *
+ * Since: 3.0
+ **/
+GtkStyleProperties *
+gtk_style_provider_get_style (GtkStyleProvider *provider,
+ GtkWidgetPath *path)
+{
+ GtkStyleProviderIface *iface;
+
+ g_return_val_if_fail (GTK_IS_STYLE_PROVIDER (provider), NULL);
+
+ iface = GTK_STYLE_PROVIDER_GET_IFACE (provider);
+
+ if (!iface->get_style)
+ return NULL;
+
+ return iface->get_style (provider, path);
+}
+
+/**
+ * gtk_style_provider_get_style_property:
+ * @provider: a #GtkStyleProvider
+ * @path: #GtkWidgetPath to query
+ * @state: state to query the style property for
+ * @pspec: The #GParamSpec to query
+ * @value: (out): return location for the property value
+ *
+ * Looks up a widget style property as defined by @provider for
+ * the widget represented by @path.
+ *
+ * Returns: %TRUE if the property was found and has a value, %FALSE otherwise
+ **/
+gboolean
+gtk_style_provider_get_style_property (GtkStyleProvider *provider,
+ GtkWidgetPath *path,
+ GtkStateFlags state,
+ GParamSpec *pspec,
+ GValue *value)
+{
+ GtkStyleProviderIface *iface;
+
+ g_return_val_if_fail (GTK_IS_STYLE_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
+ g_return_val_if_fail (path != NULL, FALSE);
+ g_return_val_if_fail (g_type_is_a (gtk_widget_path_get_widget_type (path), pspec->owner_type), FALSE);
+ g_return_val_if_fail (value != NULL, FALSE);
+
+ iface = GTK_STYLE_PROVIDER_GET_IFACE (provider);
+
+ if (!iface->get_style_property)
+ return FALSE;
+
+ return iface->get_style_property (provider, path, state, pspec, value);
+}
+
+/**
+ * gtk_style_provider_get_icon_factory:
+ * @provider: a #GtkStyleProvider
+ * @path: #GtkWidgetPath to query
+ *
+ * Returns the #GtkIconFactory defined to be in use for @path, or %NULL if none
+ * is defined.
+ *
+ * Returns: The icon factory to use for @path, or %NULL
+ *
+ * Since: 3.0
+ **/
+GtkIconFactory *
+gtk_style_provider_get_icon_factory (GtkStyleProvider *provider,
+ GtkWidgetPath *path)
+{
+ GtkStyleProviderIface *iface;
+
+ g_return_val_if_fail (GTK_IS_STYLE_PROVIDER (provider), NULL);
+ g_return_val_if_fail (path != NULL, NULL);
+
+ iface = GTK_STYLE_PROVIDER_GET_IFACE (provider);
+
+ if (!iface->get_icon_factory)
+ return NULL;
+
+ return iface->get_icon_factory (provider, path);
+}
diff --git a/gtk/gtkstyleprovider.h b/gtk/gtkstyleprovider.h
new file mode 100644
index 0000000000..d996bb77fe
--- /dev/null
+++ b/gtk/gtkstyleprovider.h
@@ -0,0 +1,128 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_STYLE_PROVIDER_H__
+#define __GTK_STYLE_PROVIDER_H__
+
+#include <glib-object.h>
+#include "gtkwidgetpath.h"
+#include "gtkiconfactory.h"
+#include "gtkstyleproperties.h"
+#include "gtkenums.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_STYLE_PROVIDER (gtk_style_provider_get_type ())
+#define GTK_STYLE_PROVIDER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_STYLE_PROVIDER, GtkStyleProvider))
+#define GTK_IS_STYLE_PROVIDER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_STYLE_PROVIDER))
+#define GTK_STYLE_PROVIDER_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GTK_TYPE_STYLE_PROVIDER, GtkStyleProviderIface))
+
+/**
+ * GTK_STYLE_PROVIDER_PRIORITY_FALLBACK:
+ *
+ * The priority used for default style information
+ * that is used in the absence of themes.
+ */
+#define GTK_STYLE_PROVIDER_PRIORITY_FALLBACK 1
+
+/**
+ * GTK_STYLE_PROVIDER_PRIORITY_THEME:
+ *
+ * The priority used for style information provided
+ * by themes.
+ */
+#define GTK_STYLE_PROVIDER_PRIORITY_THEME 200
+
+/**
+ * GTK_STYLE_PROVIDER_PRIORITY_SETTINGS:
+ *
+ * The priority used for style information provided
+ * via #GtkSettings.
+ *
+ * This priority is higher than #GTK_STYLE_PROVIDER_PRIORITY_THEME
+ * to let settings override themes.
+ */
+#define GTK_STYLE_PROVIDER_PRIORITY_SETTINGS 400
+
+/**
+ * GTK_STYLE_PROVIDER_PRIORITY_APPLICATION:
+ *
+ * A priority that can be used when adding a #GtkStyleProvider
+ * for application-specific style information.
+ */
+#define GTK_STYLE_PROVIDER_PRIORITY_APPLICATION 600
+
+/**
+ * GTK_STYLE_PROVIDER_PRIORITY_USER:
+ *
+ * The priority used for the style information from
+ * <filename>~/.gtk-3.0.css</filename>.
+ *
+ * You should not use priorities higher than this, to
+ * give the user the last word.
+ */
+#define GTK_STYLE_PROVIDER_PRIORITY_USER 800
+
+typedef struct _GtkStyleProviderIface GtkStyleProviderIface;
+typedef struct _GtkStyleProvider GtkStyleProvider; /* dummy typedef */
+
+/**
+ * GtkStyleProviderIface
+ * @get_style: Gets a set of style information that applies to a widget path.
+ * @get_style_property: Gets the value of a widget style property that applies to a widget path.
+ * @get_icon_factory: Gets the icon factory that applies to a widget path.
+ */
+struct _GtkStyleProviderIface
+{
+ GTypeInterface g_iface;
+
+ GtkStyleProperties * (* get_style) (GtkStyleProvider *provider,
+ GtkWidgetPath *path);
+
+ gboolean (* get_style_property) (GtkStyleProvider *provider,
+ GtkWidgetPath *path,
+ GtkStateFlags state,
+ GParamSpec *pspec,
+ GValue *value);
+
+ GtkIconFactory * (* get_icon_factory) (GtkStyleProvider *provider,
+ GtkWidgetPath *path);
+};
+
+GType gtk_style_provider_get_type (void) G_GNUC_CONST;
+
+GtkStyleProperties *gtk_style_provider_get_style (GtkStyleProvider *provider,
+ GtkWidgetPath *path);
+
+gboolean gtk_style_provider_get_style_property (GtkStyleProvider *provider,
+ GtkWidgetPath *path,
+ GtkStateFlags state,
+ GParamSpec *pspec,
+ GValue *value);
+
+GtkIconFactory * gtk_style_provider_get_icon_factory (GtkStyleProvider *provider,
+ GtkWidgetPath *path);
+
+G_END_DECLS
+
+#endif /* __GTK_STYLE_PROVIDER_H__ */
diff --git a/gtk/gtksymboliccolor.c b/gtk/gtksymboliccolor.c
new file mode 100644
index 0000000000..2345cfa396
--- /dev/null
+++ b/gtk/gtksymboliccolor.c
@@ -0,0 +1,558 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gtksymboliccolor.h"
+#include "gtkstyleproperties.h"
+#include "gtkintl.h"
+
+/**
+ * SECTION:gtksymboliccolor
+ * @Short_description: Symbolic colors
+ * @Title: GtkSymbolicColor
+ *
+ * GtkSymbolicColor is a boxed type that represents a symbolic color.
+ * It is the result of parsing a
+ * <link linkend="gtkcssprovider-symbolic-colors">color expression</link>.
+ * To obtain the color represented by a GtkSymbolicColor, it has to
+ * be resolved with gtk_symbolic_color_resolve(), which replaces all
+ * symbolic color references by the colors they refer to (in a given
+ * context) and evaluates mix, shade and other expressions, resulting
+ * in a #GdkRGBA value.
+ *
+ * It is not normally necessary to deal directly with #GtkSymbolicColors,
+ * since they are mostly used behind the scenes by #GtkStyleContext and
+ * #GtkCssProvider.
+ */
+
+G_DEFINE_BOXED_TYPE (GtkSymbolicColor, gtk_symbolic_color,
+ gtk_symbolic_color_ref, gtk_symbolic_color_unref)
+
+/* Symbolic colors */
+typedef enum {
+ COLOR_TYPE_LITERAL,
+ COLOR_TYPE_NAME,
+ COLOR_TYPE_SHADE,
+ COLOR_TYPE_ALPHA,
+ COLOR_TYPE_MIX
+} ColorType;
+
+struct _GtkSymbolicColor
+{
+ ColorType type;
+ guint ref_count;
+
+ union
+ {
+ GdkRGBA color;
+ gchar *name;
+
+ struct
+ {
+ GtkSymbolicColor *color;
+ gdouble factor;
+ } shade, alpha;
+
+ struct
+ {
+ GtkSymbolicColor *color1;
+ GtkSymbolicColor *color2;
+ gdouble factor;
+ } mix;
+ };
+};
+
+/**
+ * gtk_symbolic_color_new_literal:
+ * @color: a #GdkRGBA
+ *
+ * Creates a symbolic color pointing to a literal color.
+ *
+ * Returns: a newly created #GtkSymbolicColor
+ *
+ * Since: 3.0
+ **/
+GtkSymbolicColor *
+gtk_symbolic_color_new_literal (const GdkRGBA *color)
+{
+ GtkSymbolicColor *symbolic_color;
+
+ g_return_val_if_fail (color != NULL, NULL);
+
+ symbolic_color = g_slice_new0 (GtkSymbolicColor);
+ symbolic_color->type = COLOR_TYPE_LITERAL;
+ symbolic_color->color = *color;
+ symbolic_color->ref_count = 1;
+
+ return symbolic_color;
+}
+
+/**
+ * gtk_symbolic_color_new_name:
+ * @name: color name
+ *
+ * Creates a symbolic color pointing to an unresolved named
+ * color. See gtk_style_context_lookup_color() and
+ * gtk_style_properties_lookup_color().
+ *
+ * Returns: a newly created #GtkSymbolicColor
+ *
+ * Since: 3.0
+ **/
+GtkSymbolicColor *
+gtk_symbolic_color_new_name (const gchar *name)
+{
+ GtkSymbolicColor *symbolic_color;
+
+ g_return_val_if_fail (name != NULL, NULL);
+
+ symbolic_color = g_slice_new0 (GtkSymbolicColor);
+ symbolic_color->type = COLOR_TYPE_NAME;
+ symbolic_color->name = g_strdup (name);
+ symbolic_color->ref_count = 1;
+
+ return symbolic_color;
+}
+
+/**
+ * gtk_symbolic_color_new_shade:
+ * @color: another #GtkSymbolicColor
+ * @factor: shading factor to apply to @color
+ *
+ * Creates a symbolic color defined as a shade of
+ * another color. A factor > 1.0 would resolve to
+ * a brighter color, while < 1.0 would resolve to
+ * a darker color.
+ *
+ * Returns: A newly created #GtkSymbolicColor
+ *
+ * Since: 3.0
+ **/
+GtkSymbolicColor *
+gtk_symbolic_color_new_shade (GtkSymbolicColor *color,
+ gdouble factor)
+{
+ GtkSymbolicColor *symbolic_color;
+
+ g_return_val_if_fail (color != NULL, NULL);
+
+ symbolic_color = g_slice_new0 (GtkSymbolicColor);
+ symbolic_color->type = COLOR_TYPE_SHADE;
+ symbolic_color->shade.color = gtk_symbolic_color_ref (color);
+ symbolic_color->shade.factor = factor;
+ symbolic_color->ref_count = 1;
+
+ return symbolic_color;
+}
+
+/**
+ * gtk_symbolic_color_new_alpha:
+ * @color: another #GtkSymbolicColor
+ * @factor: factor to apply to @color alpha
+ *
+ * Creates a symbolic color by modifying the relative alpha
+ * value of @color. A factor < 1.0 would resolve to a more
+ * transparent color, while > 1.0 would resolve to a more
+ * opaque color.
+ *
+ * Returns: A newly created #GtkSymbolicColor
+ *
+ * Since: 3.0
+ **/
+GtkSymbolicColor *
+gtk_symbolic_color_new_alpha (GtkSymbolicColor *color,
+ gdouble factor)
+{
+ GtkSymbolicColor *symbolic_color;
+
+ g_return_val_if_fail (color != NULL, NULL);
+
+ symbolic_color = g_slice_new0 (GtkSymbolicColor);
+ symbolic_color->type = COLOR_TYPE_ALPHA;
+ symbolic_color->alpha.color = gtk_symbolic_color_ref (color);
+ symbolic_color->alpha.factor = factor;
+ symbolic_color->ref_count = 1;
+
+ return symbolic_color;
+}
+
+/**
+ * gtk_symbolic_color_new_mix:
+ * @color1: color to mix
+ * @color2: another color to mix
+ * @factor: mix factor
+ *
+ * Creates a symbolic color defined as a mix of another
+ * two colors. a mix factor of 0 would resolve to @color1,
+ * while a factor of 1 would resolve to @color2.
+ *
+ * Returns: A newly created #GtkSymbolicColor
+ *
+ * Since: 3.0
+ **/
+GtkSymbolicColor *
+gtk_symbolic_color_new_mix (GtkSymbolicColor *color1,
+ GtkSymbolicColor *color2,
+ gdouble factor)
+{
+ GtkSymbolicColor *symbolic_color;
+
+ g_return_val_if_fail (color1 != NULL, NULL);
+ g_return_val_if_fail (color1 != NULL, NULL);
+
+ symbolic_color = g_slice_new0 (GtkSymbolicColor);
+ symbolic_color->type = COLOR_TYPE_MIX;
+ symbolic_color->mix.color1 = gtk_symbolic_color_ref (color1);
+ symbolic_color->mix.color2 = gtk_symbolic_color_ref (color2);
+ symbolic_color->mix.factor = factor;
+ symbolic_color->ref_count = 1;
+
+ return symbolic_color;
+}
+
+/**
+ * gtk_symbolic_color_ref:
+ * @color: a #GtkSymbolicColor
+ *
+ * Increases the reference count of @color
+ *
+ * Returns: the same @color
+ *
+ * Since: 3.0
+ **/
+GtkSymbolicColor *
+gtk_symbolic_color_ref (GtkSymbolicColor *color)
+{
+ g_return_val_if_fail (color != NULL, NULL);
+
+ color->ref_count++;
+
+ return color;
+}
+
+/**
+ * gtk_symbolic_color_unref:
+ * @color: a #GtkSymbolicColor
+ *
+ * Decreases the reference count of @color, freeing its memory if the
+ * reference count reaches 0.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_symbolic_color_unref (GtkSymbolicColor *color)
+{
+ g_return_if_fail (color != NULL);
+
+ color->ref_count--;
+
+ if (color->ref_count == 0)
+ {
+ switch (color->type)
+ {
+ case COLOR_TYPE_NAME:
+ g_free (color->name);
+ break;
+ case COLOR_TYPE_SHADE:
+ gtk_symbolic_color_unref (color->shade.color);
+ break;
+ case COLOR_TYPE_ALPHA:
+ gtk_symbolic_color_unref (color->alpha.color);
+ break;
+ case COLOR_TYPE_MIX:
+ gtk_symbolic_color_unref (color->mix.color1);
+ gtk_symbolic_color_unref (color->mix.color2);
+ break;
+ default:
+ break;
+ }
+
+ g_slice_free (GtkSymbolicColor, color);
+ }
+}
+
+static void
+rgb_to_hls (gdouble *r,
+ gdouble *g,
+ gdouble *b)
+{
+ gdouble min;
+ gdouble max;
+ gdouble red;
+ gdouble green;
+ gdouble blue;
+ gdouble h, l, s;
+ gdouble delta;
+
+ red = *r;
+ green = *g;
+ blue = *b;
+
+ if (red > green)
+ {
+ if (red > blue)
+ max = red;
+ else
+ max = blue;
+
+ if (green < blue)
+ min = green;
+ else
+ min = blue;
+ }
+ else
+ {
+ if (green > blue)
+ max = green;
+ else
+ max = blue;
+
+ if (red < blue)
+ min = red;
+ else
+ min = blue;
+ }
+
+ l = (max + min) / 2;
+ s = 0;
+ h = 0;
+
+ if (max != min)
+ {
+ if (l <= 0.5)
+ s = (max - min) / (max + min);
+ else
+ s = (max - min) / (2 - max - min);
+
+ delta = max -min;
+ if (red == max)
+ h = (green - blue) / delta;
+ else if (green == max)
+ h = 2 + (blue - red) / delta;
+ else if (blue == max)
+ h = 4 + (red - green) / delta;
+
+ h *= 60;
+ if (h < 0.0)
+ h += 360;
+ }
+
+ *r = h;
+ *g = l;
+ *b = s;
+}
+
+static void
+hls_to_rgb (gdouble *h,
+ gdouble *l,
+ gdouble *s)
+{
+ gdouble hue;
+ gdouble lightness;
+ gdouble saturation;
+ gdouble m1, m2;
+ gdouble r, g, b;
+
+ lightness = *l;
+ saturation = *s;
+
+ if (lightness <= 0.5)
+ m2 = lightness * (1 + saturation);
+ else
+ m2 = lightness + saturation - lightness * saturation;
+ m1 = 2 * lightness - m2;
+
+ if (saturation == 0)
+ {
+ *h = lightness;
+ *l = lightness;
+ *s = lightness;
+ }
+ else
+ {
+ hue = *h + 120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ r = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ r = m2;
+ else if (hue < 240)
+ r = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ r = m1;
+
+ hue = *h;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ g = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ g = m2;
+ else if (hue < 240)
+ g = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ g = m1;
+
+ hue = *h - 120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ b = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ b = m2;
+ else if (hue < 240)
+ b = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ b = m1;
+
+ *h = r;
+ *l = g;
+ *s = b;
+ }
+}
+
+static void
+_shade_color (GdkRGBA *color,
+ gdouble factor)
+{
+ GdkRGBA temp;
+
+ temp = *color;
+ rgb_to_hls (&temp.red, &temp.green, &temp.blue);
+
+ temp.green *= factor;
+ if (temp.green > 1.0)
+ temp.green = 1.0;
+ else if (temp.green < 0.0)
+ temp.green = 0.0;
+
+ temp.blue *= factor;
+ if (temp.blue > 1.0)
+ temp.blue = 1.0;
+ else if (temp.blue < 0.0)
+ temp.blue = 0.0;
+
+ hls_to_rgb (&temp.red, &temp.green, &temp.blue);
+ *color = temp;
+}
+
+/**
+ * gtk_symbolic_color_resolve:
+ * @color: a #GtkSymbolicColor
+ * @props: #GtkStyleProperties to use when resolving named colors
+ * @resolved_color: (out): return location for the resolved color
+ *
+ * If @color is resolvable, @resolved_color will be filled in
+ * with the resolved color, and %TRUE will be returned. Generally,
+ * if @color can't be resolved, it is due to it being defined on
+ * top of a named color that doesn't exist in @props.
+ *
+ * Returns: %TRUE if the color has been resolved
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_symbolic_color_resolve (GtkSymbolicColor *color,
+ GtkStyleProperties *props,
+ GdkRGBA *resolved_color)
+{
+ g_return_val_if_fail (color != NULL, FALSE);
+ g_return_val_if_fail (resolved_color != NULL, FALSE);
+
+ switch (color->type)
+ {
+ case COLOR_TYPE_LITERAL:
+ *resolved_color = color->color;
+ return TRUE;
+ case COLOR_TYPE_NAME:
+ {
+ GtkSymbolicColor *named_color;
+
+ g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
+
+ named_color = gtk_style_properties_lookup_color (props, color->name);
+
+ if (!named_color)
+ return FALSE;
+
+ return gtk_symbolic_color_resolve (named_color, props, resolved_color);
+ }
+
+ break;
+ case COLOR_TYPE_SHADE:
+ {
+ GdkRGBA shade;
+
+ if (!gtk_symbolic_color_resolve (color->shade.color, props, &shade))
+ return FALSE;
+
+ _shade_color (&shade, color->shade.factor);
+ *resolved_color = shade;
+
+ return TRUE;
+ }
+
+ break;
+ case COLOR_TYPE_ALPHA:
+ {
+ GdkRGBA alpha;
+
+ if (!gtk_symbolic_color_resolve (color->alpha.color, props, &alpha))
+ return FALSE;
+
+ *resolved_color = alpha;
+ resolved_color->alpha = CLAMP (alpha.alpha * color->alpha.factor, 0, 1);
+
+ return TRUE;
+ }
+ case COLOR_TYPE_MIX:
+ {
+ GdkRGBA color1, color2;
+
+ if (!gtk_symbolic_color_resolve (color->mix.color1, props, &color1))
+ return FALSE;
+
+ if (!gtk_symbolic_color_resolve (color->mix.color2, props, &color2))
+ return FALSE;
+
+ resolved_color->red = CLAMP (color1.red + ((color2.red - color1.red) * color->mix.factor), 0, 1);
+ resolved_color->green = CLAMP (color1.green + ((color2.green - color1.green) * color->mix.factor), 0, 1);
+ resolved_color->blue = CLAMP (color1.blue + ((color2.blue - color1.blue) * color->mix.factor), 0, 1);
+ resolved_color->alpha = CLAMP (color1.alpha + ((color2.alpha - color1.alpha) * color->mix.factor), 0, 1);
+
+ return TRUE;
+ }
+
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return FALSE;
+}
diff --git a/gtk/gtksymboliccolor.h b/gtk/gtksymboliccolor.h
new file mode 100644
index 0000000000..4f4b8137b4
--- /dev/null
+++ b/gtk/gtksymboliccolor.h
@@ -0,0 +1,55 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_SYMBOLIC_COLOR_H__
+#define __GTK_SYMBOLIC_COLOR_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkstyleproperties.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SYMBOLIC_COLOR (gtk_symbolic_color_get_type ())
+
+GType gtk_symbolic_color_get_type (void) G_GNUC_CONST;
+
+GtkSymbolicColor * gtk_symbolic_color_new_literal (const GdkRGBA *color);
+GtkSymbolicColor * gtk_symbolic_color_new_name (const gchar *name);
+GtkSymbolicColor * gtk_symbolic_color_new_shade (GtkSymbolicColor *color,
+ gdouble factor);
+GtkSymbolicColor * gtk_symbolic_color_new_alpha (GtkSymbolicColor *color,
+ gdouble factor);
+GtkSymbolicColor * gtk_symbolic_color_new_mix (GtkSymbolicColor *color1,
+ GtkSymbolicColor *color2,
+ gdouble factor);
+
+GtkSymbolicColor * gtk_symbolic_color_ref (GtkSymbolicColor *color);
+void gtk_symbolic_color_unref (GtkSymbolicColor *color);
+
+gboolean gtk_symbolic_color_resolve (GtkSymbolicColor *color,
+ GtkStyleProperties *props,
+ GdkRGBA *resolved_color);
+
+G_END_DECLS
+
+#endif /* __GTK_SYMBOLIC_COLOR_H__ */
diff --git a/gtk/gtktextbufferserialize.c b/gtk/gtktextbufferserialize.c
index 16a36d3994..526e9518aa 100644
--- a/gtk/gtktextbufferserialize.c
+++ b/gtk/gtktextbufferserialize.c
@@ -70,10 +70,6 @@ serialize_value (GValue *value)
return g_strdup_printf ("%x:%x:%x", color->red, color->green, color->blue);
}
- else if (g_type_is_a (value->g_type, GDK_TYPE_DRAWABLE))
- {
- /* Don't do anything */
- }
else
{
g_warning ("Type %s is not serializable\n", g_type_name (value->g_type));
diff --git a/gtk/gtkthemes.h b/gtk/gtkthemes.h
index 49d3e511dc..91edccc109 100644
--- a/gtk/gtkthemes.h
+++ b/gtk/gtkthemes.h
@@ -39,10 +39,14 @@ G_BEGIN_DECLS
#define GTK_THEME_ENGINE(theme_engine) (G_TYPE_CHECK_INSTANCE_CAST ((theme_engine), GTK_TYPE_THEME_ENGINE, GtkThemeEngine))
#define GTK_IS_THEME_ENGINE(theme_engine) (G_TYPE_CHECK_INSTANCE_TYPE ((theme_engine), GTK_TYPE_THEME_ENGINE))
+#if !defined(GTK_DISABLE_DEPRECATED) || defined(GTK_COMPILATION)
+
GType gtk_theme_engine_get_type (void) G_GNUC_CONST;
GtkThemeEngine *gtk_theme_engine_get (const gchar *name);
GtkRcStyle *gtk_theme_engine_create_rc_style (GtkThemeEngine *engine);
+#endif
+
G_END_DECLS
#endif /* __GTK_THEMES_H__ */
diff --git a/gtk/gtkthemingengine.c b/gtk/gtkthemingengine.c
new file mode 100644
index 0000000000..e254161141
--- /dev/null
+++ b/gtk/gtkthemingengine.c
@@ -0,0 +1,3059 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <math.h>
+#include <gtk/gtk.h>
+
+#include <gtk/gtkthemingengine.h>
+#include <gtk/gtkstylecontext.h>
+#include <gtk/gtkintl.h>
+
+#include "gtkprivate.h"
+#include "gtk9slice.h"
+#include "gtkpango.h"
+
+/**
+ * SECTION:gtkthemingengine
+ * @Short_description: Theming renderers
+ * @Title: GtkThemingEngine
+ * @See_also: #GtkStyleContext
+ *
+ * #GtkThemingEngine is the object used for rendering themed content
+ * in GTK+ widgets. Even though GTK+ has a default implementation,
+ * it can be overridden in CSS files by enforcing a #GtkThemingEngine
+ * object to be loaded as a module.
+ *
+ * In order to implement a theming engine, a #GtkThemingEngine subclass
+ * must be created, alongside the CSS file that will reference it, the
+ * theming engine would be created as an .so library, and installed in
+ * $(gtk-modules-dir)/theming-engines/.
+ *
+ * #GtkThemingEngine<!-- -->s have limited access to the object they are
+ * rendering, the #GtkThemingEngine API has read-only accessors to the
+ * style information contained in the rendered object's #GtkStyleContext.
+ */
+
+typedef struct GtkThemingEnginePrivate GtkThemingEnginePrivate;
+
+enum {
+ SIDE_LEFT = 1,
+ SIDE_BOTTOM = 1 << 1,
+ SIDE_RIGHT = 1 << 2,
+ SIDE_TOP = 1 << 3,
+ SIDE_ALL = 0xF
+};
+
+enum {
+ PROP_0,
+ PROP_NAME
+};
+
+struct GtkThemingEnginePrivate
+{
+ GtkStyleContext *context;
+ gchar *name;
+};
+
+#define GTK_THEMING_ENGINE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_THEMING_ENGINE, GtkThemingEnginePrivate))
+
+static void gtk_theming_engine_finalize (GObject *object);
+static void gtk_theming_engine_impl_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_theming_engine_impl_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+static void gtk_theming_engine_render_check (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+static void gtk_theming_engine_render_option (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+static void gtk_theming_engine_render_arrow (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble angle,
+ gdouble x,
+ gdouble y,
+ gdouble size);
+static void gtk_theming_engine_render_background (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+static void gtk_theming_engine_render_frame (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+static void gtk_theming_engine_render_expander (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+static void gtk_theming_engine_render_focus (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+static void gtk_theming_engine_render_layout (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ PangoLayout *layout);
+static void gtk_theming_engine_render_line (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1);
+static void gtk_theming_engine_render_slider (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkOrientation orientation);
+static void gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkPositionType gap_side,
+ gdouble xy0_gap,
+ gdouble xy1_gap);
+static void gtk_theming_engine_render_extension (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkPositionType gap_side);
+static void gtk_theming_engine_render_handle (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+static void gtk_theming_engine_render_activity (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+static GdkPixbuf * gtk_theming_engine_render_icon_pixbuf (GtkThemingEngine *engine,
+ const GtkIconSource *source,
+ GtkIconSize size);
+
+G_DEFINE_TYPE (GtkThemingEngine, gtk_theming_engine, G_TYPE_OBJECT)
+
+
+typedef struct GtkThemingModule GtkThemingModule;
+typedef struct GtkThemingModuleClass GtkThemingModuleClass;
+
+struct GtkThemingModule
+{
+ GTypeModule parent_instance;
+ GModule *module;
+ gchar *name;
+
+ void (*init) (GTypeModule *module);
+ void (*exit) (void);
+ GtkThemingEngine * (*create_engine) (void);
+};
+
+struct GtkThemingModuleClass
+{
+ GTypeModuleClass parent_class;
+};
+
+#define GTK_TYPE_THEMING_MODULE (gtk_theming_module_get_type ())
+#define GTK_THEMING_MODULE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_THEMING_MODULE, GtkThemingModule))
+#define GTK_IS_THEMING_MODULE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_THEMING_MODULE))
+
+G_DEFINE_TYPE (GtkThemingModule, gtk_theming_module, G_TYPE_TYPE_MODULE);
+
+static void
+gtk_theming_engine_class_init (GtkThemingEngineClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gtk_theming_engine_finalize;
+ object_class->set_property = gtk_theming_engine_impl_set_property;
+ object_class->get_property = gtk_theming_engine_impl_get_property;
+
+ klass->render_check = gtk_theming_engine_render_check;
+ klass->render_option = gtk_theming_engine_render_option;
+ klass->render_arrow = gtk_theming_engine_render_arrow;
+ klass->render_background = gtk_theming_engine_render_background;
+ klass->render_frame = gtk_theming_engine_render_frame;
+ klass->render_expander = gtk_theming_engine_render_expander;
+ klass->render_focus = gtk_theming_engine_render_focus;
+ klass->render_layout = gtk_theming_engine_render_layout;
+ klass->render_line = gtk_theming_engine_render_line;
+ klass->render_slider = gtk_theming_engine_render_slider;
+ klass->render_frame_gap = gtk_theming_engine_render_frame_gap;
+ klass->render_extension = gtk_theming_engine_render_extension;
+ klass->render_handle = gtk_theming_engine_render_handle;
+ klass->render_activity = gtk_theming_engine_render_activity;
+ klass->render_icon_pixbuf = gtk_theming_engine_render_icon_pixbuf;
+
+ /**
+ * GtkThemingEngine:name:
+ *
+ * The theming engine name, this name will be used when registering
+ * custom properties, for a theming engine named "Clearlooks" registering
+ * a "glossy" custom property, it could be referenced in the CSS file as
+ *
+ * <programlisting>
+ * -Clearlooks-glossy: true;
+ * </programlisting>
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ P_("Name"),
+ P_("Theming engine name"),
+ NULL,
+ G_PARAM_CONSTRUCT_ONLY | GTK_PARAM_READWRITE));
+
+ g_type_class_add_private (object_class, sizeof (GtkThemingEnginePrivate));
+}
+
+static void
+gtk_theming_engine_init (GtkThemingEngine *engine)
+{
+ engine->priv = GTK_THEMING_ENGINE_GET_PRIVATE (engine);
+}
+
+static void
+gtk_theming_engine_finalize (GObject *object)
+{
+ GtkThemingEnginePrivate *priv;
+
+ priv = GTK_THEMING_ENGINE (object)->priv;
+ g_free (priv->name);
+
+ G_OBJECT_GET_CLASS (gtk_theming_engine_parent_class)->finalize (object);
+}
+
+static void
+gtk_theming_engine_impl_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkThemingEnginePrivate *priv;
+
+ priv = GTK_THEMING_ENGINE (object)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_NAME:
+ if (priv->name)
+ g_free (priv->name);
+
+ priv->name = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gtk_theming_engine_impl_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkThemingEnginePrivate *priv;
+
+ priv = GTK_THEMING_ENGINE (object)->priv;
+
+ switch (prop_id)
+ {
+ case PROP_NAME:
+ g_value_set_string (value, priv->name);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+void
+_gtk_theming_engine_set_context (GtkThemingEngine *engine,
+ GtkStyleContext *context)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+ g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
+
+ priv = engine->priv;
+ priv->context = context;
+}
+
+/**
+ * gtk_theming_engine_register_property:
+ * @name_space: namespace for the property name
+ * @parse_func: parsing function to use, or %NULL
+ * @pspec: the #GParamSpec for the new property
+ *
+ * Registers a property so it can be used in the CSS file format,
+ * on the CSS file the property will look like
+ * "-${@name_space}-${property_name}". being
+ * ${property_name} the given to @pspec. @name_space will usually
+ * be the theme engine name.
+ *
+ * For any type a @parse_func may be provided, being this function
+ * used for turning any property value (between ':' and ';') in
+ * CSS to the #GValue needed. For basic types there is already
+ * builtin parsing support, so %NULL may be provided for these
+ * cases.
+ *
+ * <note>
+ * Engines must ensure property registration happens exactly once,
+ * usually GTK+ deals with theming engines as singletons, so this
+ * should be guaranteed to happen once, but bear this in mind
+ * when creating #GtkThemeEngine<!-- -->s yourself.
+ * </note>
+ *
+ * <note>
+ * In order to make use of the custom registered properties in
+ * the CSS file, make sure the engine is loaded first by specifying
+ * the engine property, either in a previous rule or within the same
+ * one.
+ * <programlisting>
+ * &ast; {
+ * engine: someengine;
+ * -SomeEngine-custom-property: 2;
+ * }
+ * </programlisting>
+ * </note>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_register_property (const gchar *name_space,
+ GtkStylePropertyParser parse_func,
+ GParamSpec *pspec)
+{
+ gchar *name;
+
+ g_return_if_fail (name_space != NULL);
+ g_return_if_fail (strchr (name_space, ' ') == NULL);
+ g_return_if_fail (G_IS_PARAM_SPEC (pspec));
+
+ /* FIXME: hack hack hack, replacing pspec->name to include namespace */
+ name = g_strdup_printf ("-%s-%s", name_space, pspec->name);
+ g_free (pspec->name);
+ pspec->name = name;
+
+ gtk_style_properties_register_property (parse_func, pspec);
+}
+
+/**
+ * gtk_theming_engine_get_property:
+ * @engine: a #GtkThemingEngine
+ * @property: the property name
+ * @state: state to retrieve the value for
+ * @value: (out) (transfer full): return location for the property value,
+ * you must free this memory using g_value_unset() once you are
+ * done with it.
+ *
+ * Gets a property value as retrieved from the style settings that apply
+ * to the currently rendered element.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_property (GtkThemingEngine *engine,
+ const gchar *property,
+ GtkStateFlags state,
+ GValue *value)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+ g_return_if_fail (property != NULL);
+ g_return_if_fail (value != NULL);
+
+ priv = engine->priv;
+ gtk_style_context_get_property (priv->context, property, state, value);
+}
+
+/**
+ * gtk_theming_engine_get_valist:
+ * @engine: a #GtkThemingEngine
+ * @state: state to retrieve values for
+ * @args: va_list of property name/return location pairs, followed by %NULL
+ *
+ * Retrieves several style property values that apply to the currently
+ * rendered element.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_valist (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ va_list args)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+
+ priv = engine->priv;
+ gtk_style_context_get_valist (priv->context, state, args);
+}
+
+/**
+ * gtk_theming_engine_get:
+ * @engine: a #GtkThemingEngine
+ * @state: state to retrieve values for
+ * @...: property name /return value pairs, followed by %NULL
+ *
+ * Retrieves several style property values that apply to the currently
+ * rendered element.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ ...)
+{
+ GtkThemingEnginePrivate *priv;
+ va_list args;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+
+ priv = engine->priv;
+
+ va_start (args, state);
+ gtk_style_context_get_valist (priv->context, state, args);
+ va_end (args);
+}
+
+/**
+ * gtk_theming_engine_get_style_property:
+ * @engine: a #GtkThemingEngine
+ * @property_name: the name of the widget style property
+ * @value: (out) (transfer full): Return location for the property value, free with
+ * g_value_unset() after use.
+ *
+ * Gets the value for a widget style property.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_style_property (GtkThemingEngine *engine,
+ const gchar *property_name,
+ GValue *value)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+ g_return_if_fail (property_name != NULL);
+
+ priv = engine->priv;
+ gtk_style_context_get_style_property (priv->context, property_name, value);
+}
+
+/**
+ * gtk_theming_engine_get_style_valist:
+ * @engine: a #GtkThemingEngine
+ * @args: va_list of property name/return location pairs, followed by %NULL
+ *
+ * Retrieves several widget style properties from @engine according to the
+ * currently rendered content's style.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_style_valist (GtkThemingEngine *engine,
+ va_list args)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+
+ priv = engine->priv;
+ gtk_style_context_get_style_valist (priv->context, args);
+}
+
+/**
+ * gtk_theming_engine_get_style:
+ * @engine: a #GtkThemingEngine
+ * @...: property name /return value pairs, followed by %NULL
+ *
+ * Retrieves several widget style properties from @engine according
+ * to the currently rendered content's style.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_style (GtkThemingEngine *engine,
+ ...)
+{
+ GtkThemingEnginePrivate *priv;
+ va_list args;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+
+ priv = engine->priv;
+
+ va_start (args, engine);
+ gtk_style_context_get_style_valist (priv->context, args);
+ va_end (args);
+}
+
+/**
+ * gtk_theming_engine_lookup_color:
+ * @engine: a #GtkThemingEngine
+ * @color_name: color name to lookup
+ * @color: (out): Return location for the looked up color
+ *
+ * Looks up and resolves a color name in the current style's color map.
+ *
+ * Returns: %TRUE if @color_name was found and resolved, %FALSE otherwise
+ **/
+gboolean
+gtk_theming_engine_lookup_color (GtkThemingEngine *engine,
+ const gchar *color_name,
+ GdkRGBA *color)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), FALSE);
+ g_return_val_if_fail (color_name != NULL, FALSE);
+
+ priv = engine->priv;
+ return gtk_style_context_lookup_color (priv->context, color_name, color);
+}
+
+/**
+ * gtk_theming_engine_get_state:
+ * @engine: a #GtkThemingEngine
+ *
+ * returns the state used when rendering.
+ *
+ * Returns: the state flags
+ *
+ * Since: 3.0
+ **/
+GtkStateFlags
+gtk_theming_engine_get_state (GtkThemingEngine *engine)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), 0);
+
+ priv = engine->priv;
+ return gtk_style_context_get_state (priv->context);
+}
+
+/**
+ * gtk_theming_engine_state_is_running:
+ * @engine: a #GtkThemingEngine
+ * @state: a widget state
+ * @progress: (out): return location for the transition progress
+ *
+ * Returns %TRUE if there is a transition animation running for the
+ * current region (see gtk_style_context_push_animatable_region()).
+ *
+ * If @progress is not %NULL, the animation progress will be returned
+ * there, 0.0 means the state is closest to being %FALSE, while 1.0 means
+ * it's closest to being %TRUE. This means transition animations will
+ * run from 0 to 1 when @state is being set to %TRUE and from 1 to 0 when
+ * it's being set to %FALSE.
+ *
+ * Returns: %TRUE if there is a running transition animation for @state.
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_theming_engine_state_is_running (GtkThemingEngine *engine,
+ GtkStateType state,
+ gdouble *progress)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), FALSE);
+
+ priv = engine->priv;
+ return gtk_style_context_state_is_running (priv->context, state, progress);
+}
+
+/**
+ * gtk_theming_engine_get_path:
+ * @engine: a #GtkThemingEngine
+ *
+ * Returns the widget path used for style matching.
+ *
+ * Returns: (transfer none): A #GtkWidgetPath
+ *
+ * Since: 3.0
+ **/
+G_CONST_RETURN GtkWidgetPath *
+gtk_theming_engine_get_path (GtkThemingEngine *engine)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), NULL);
+
+ priv = engine->priv;
+ return gtk_style_context_get_path (priv->context);
+}
+
+/**
+ * gtk_theming_engine_has_class:
+ * @engine: a #GtkThemingEngine
+ * @style_class: class name to look up
+ *
+ * Returns %TRUE if the currently rendered contents have
+ * defined the given class name.
+ *
+ * Returns: %TRUE if @engine has @class_name defined
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_theming_engine_has_class (GtkThemingEngine *engine,
+ const gchar *style_class)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), FALSE);
+
+ priv = engine->priv;
+ return gtk_style_context_has_class (priv->context, style_class);
+}
+
+/**
+ * gtk_theming_engine_has_region:
+ * @engine: a #GtkThemingEngine
+ * @style_region: a region name
+ * @flags: (out) (allow-none): return location for region flags
+ *
+ * Returns %TRUE if the currently rendered contents have the
+ * region defined. If @flags_return is not %NULL, it is set
+ * to the flags affecting the region.
+ *
+ * Returns: %TRUE if region is defined
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_theming_engine_has_region (GtkThemingEngine *engine,
+ const gchar *style_region,
+ GtkRegionFlags *flags)
+{
+ GtkThemingEnginePrivate *priv;
+
+ if (flags)
+ *flags = 0;
+
+ g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), FALSE);
+
+ priv = engine->priv;
+ return gtk_style_context_has_region (priv->context, style_region, flags);
+}
+
+/**
+ * gtk_theming_engine_get_direction:
+ * @engine: a #GtkThemingEngine
+ *
+ * Returns the widget direction used for rendering.
+ *
+ * Returns: the widget direction
+ *
+ * Since: 3.0
+ **/
+GtkTextDirection
+gtk_theming_engine_get_direction (GtkThemingEngine *engine)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), GTK_TEXT_DIR_LTR);
+
+ priv = engine->priv;
+ return gtk_style_context_get_direction (priv->context);
+}
+
+/**
+ * gtk_theming_engine_get_junction_sides:
+ * @engine: a #GtkThemingEngine
+ *
+ * Returns the widget direction used for rendering.
+ *
+ * Returns: the widget direction
+ *
+ * Since: 3.0
+ **/
+GtkJunctionSides
+gtk_theming_engine_get_junction_sides (GtkThemingEngine *engine)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), 0);
+
+ priv = engine->priv;
+ return gtk_style_context_get_junction_sides (priv->context);
+}
+
+/**
+ * gtk_theming_engine_get_color:
+ * @engine: a #GtkThemingEngine
+ * @state: state to retrieve the color for
+ * @color: (out): return value for the foreground color
+ *
+ * Gets the foreground color for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_color (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GdkRGBA *color)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+
+ priv = engine->priv;
+ gtk_style_context_get_color (priv->context, state, color);
+}
+
+/**
+ * gtk_theming_engine_get_background_color:
+ * @engine: a #GtkThemingEngine
+ * @state: state to retrieve the color for
+ * @color: (out): return value for the background color
+ *
+ * Gets the background color for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_background_color (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GdkRGBA *color)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+
+ priv = engine->priv;
+ gtk_style_context_get_background_color (priv->context, state, color);
+}
+
+/**
+ * gtk_theming_engine_get_border_color:
+ * @engine: a #GtkThemingEngine
+ * @state: state to retrieve the color for
+ * @color: (out): return value for the border color
+ *
+ * Gets the border color for a given state.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_border_color (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GdkRGBA *color)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+
+ priv = engine->priv;
+ gtk_style_context_get_border_color (priv->context, state, color);
+}
+
+/**
+ * gtk_theming_engine_get_border:
+ * @engine: a #GtkthemingEngine
+ * @state: state to retrieve the border for
+ * @color: (out): return value for the border settings
+ *
+ * Gets the border for a given state as a #GtkBorder.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_border (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GtkBorder *border)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+
+ priv = engine->priv;
+ gtk_style_context_get_border (priv->context, state, border);
+}
+
+/**
+ * gtk_theming_engine_get_padding:
+ * @engine: a #GtkthemingEngine
+ * @state: state to retrieve the padding for
+ * @color: (out): return value for the padding settings
+ *
+ * Gets the padding for a given state as a #GtkBorder.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_padding (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GtkBorder *padding)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+
+ priv = engine->priv;
+ gtk_style_context_get_padding (priv->context, state, padding);
+}
+
+/**
+ * gtk_theming_engine_get_margin:
+ * @engien: a #GtkThemingEngine
+ * @state: state to retrieve the border for
+ * @color: (out): return value for the margin settings
+ *
+ * Gets the margin for a given state as a #GtkBorder.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_theming_engine_get_margin (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GtkBorder *margin)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_if_fail (GTK_IS_THEMING_ENGINE (engine));
+
+ priv = engine->priv;
+ gtk_style_context_get_margin (priv->context, state, margin);
+}
+
+
+/* GtkThemingModule */
+
+static gboolean
+gtk_theming_module_load (GTypeModule *type_module)
+{
+ GtkThemingModule *theming_module;
+ GModule *module;
+ gchar *name, *module_path;
+
+ theming_module = GTK_THEMING_MODULE (type_module);
+ name = theming_module->name;
+ module_path = _gtk_find_module (name, "theming-engines");
+
+ if (!module_path)
+ {
+ g_warning (_("Unable to locate theme engine in module path: \"%s\","), name);
+ return FALSE;
+ }
+
+ module = g_module_open (module_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+ g_free (module_path);
+
+ if (!module)
+ {
+ g_warning ("%s", g_module_error ());
+ return FALSE;
+ }
+
+ if (!g_module_symbol (module, "theme_init",
+ (gpointer *) &theming_module->init) ||
+ !g_module_symbol (module, "theme_exit",
+ (gpointer *) &theming_module->exit) ||
+ !g_module_symbol (module, "create_engine",
+ (gpointer *) &theming_module->create_engine))
+ {
+ g_warning ("%s", g_module_error ());
+ g_module_close (module);
+
+ return FALSE;
+ }
+
+ theming_module->module = module;
+
+ theming_module->init (G_TYPE_MODULE (theming_module));
+
+ return TRUE;
+}
+
+static void
+gtk_theming_module_unload (GTypeModule *type_module)
+{
+ GtkThemingModule *theming_module;
+
+ theming_module = GTK_THEMING_MODULE (type_module);
+
+ theming_module->exit ();
+
+ g_module_close (theming_module->module);
+
+ theming_module->module = NULL;
+ theming_module->init = NULL;
+ theming_module->exit = NULL;
+ theming_module->create_engine = NULL;
+}
+
+static void
+gtk_theming_module_class_init (GtkThemingModuleClass *klass)
+{
+ GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (klass);
+
+ module_class->load = gtk_theming_module_load;
+ module_class->unload = gtk_theming_module_unload;
+}
+
+static void
+gtk_theming_module_init (GtkThemingModule *module)
+{
+}
+
+/**
+ * gtk_theming_engine_load:
+ * @name: Theme engine name to load
+ *
+ * Loads and initializes a theming engine module from the
+ * standard directories.
+ *
+ * Returns: (transfer none): A theming engine, or %NULL if
+ * the engine @name doesn't exist.
+ **/
+GtkThemingEngine *
+gtk_theming_engine_load (const gchar *name)
+{
+ static GHashTable *engines = NULL;
+ static GtkThemingEngine *default_engine;
+ GtkThemingEngine *engine = NULL;
+
+ if (name)
+ {
+ if (!engines)
+ engines = g_hash_table_new (g_str_hash, g_str_equal);
+
+ engine = g_hash_table_lookup (engines, name);
+
+ if (!engine)
+ {
+ GtkThemingModule *module;
+
+ module = g_object_new (GTK_TYPE_THEMING_MODULE, NULL);
+ g_type_module_set_name (G_TYPE_MODULE (module), name);
+ module->name = g_strdup (name);
+
+ if (module && g_type_module_use (G_TYPE_MODULE (module)))
+ {
+ engine = (module->create_engine) ();
+
+ if (engine)
+ g_hash_table_insert (engines, module->name, engine);
+ }
+ }
+ }
+
+ if (!engine)
+ {
+ if (G_UNLIKELY (!default_engine))
+ default_engine = g_object_new (GTK_TYPE_THEMING_ENGINE, NULL);
+
+ engine = default_engine;
+ }
+
+ return engine;
+}
+
+/**
+ * gtk_theming_engine_get_screen:
+ * @engine: a #GtkThemingEngine
+ *
+ * Returns the #GdkScreen to which @engine currently rendering to.
+ *
+ * Returns: a #GdkScreen, or %NULL.
+ **/
+GdkScreen *
+gtk_theming_engine_get_screen (GtkThemingEngine *engine)
+{
+ GtkThemingEnginePrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_THEMING_ENGINE (engine), NULL);
+
+ priv = engine->priv;
+ return gtk_style_context_get_screen (priv->context);
+}
+
+/* Paint method implementations */
+static void
+gtk_theming_engine_render_check (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GdkRGBA *fg_color, *bg_color, *border_color;
+ GtkStateFlags flags;
+ gint exterior_size, interior_size, thickness, pad;
+ GtkBorderStyle border_style;
+ GtkBorder *border;
+ gint border_width;
+
+ flags = gtk_theming_engine_get_state (engine);
+ cairo_save (cr);
+
+ gtk_theming_engine_get (engine, flags,
+ "color", &fg_color,
+ "background-color", &bg_color,
+ "border-color", &border_color,
+ "border-style", &border_style,
+ "border-width", &border,
+ NULL);
+
+ border_width = MIN (MIN (border->top, border->bottom),
+ MIN (border->left, border->right));
+ exterior_size = MIN (width, height);
+
+ if (exterior_size % 2 == 0) /* Ensure odd */
+ exterior_size -= 1;
+
+ /* FIXME: thickness */
+ thickness = 1;
+ pad = thickness + MAX (1, (exterior_size - 2 * thickness) / 9);
+ interior_size = MAX (1, exterior_size - 2 * pad);
+
+ if (interior_size < 7)
+ {
+ interior_size = 7;
+ pad = MAX (0, (exterior_size - interior_size) / 2);
+ }
+
+ x -= (1 + exterior_size - (gint) width) / 2;
+ y -= (1 + exterior_size - (gint) height) / 2;
+
+ if (border_style == GTK_BORDER_STYLE_SOLID)
+ {
+ cairo_set_line_width (cr, border_width);
+
+ cairo_rectangle (cr, x + 0.5, y + 0.5, exterior_size - 1, exterior_size - 1);
+ gdk_cairo_set_source_rgba (cr, bg_color);
+ cairo_fill_preserve (cr);
+
+ if (border_color)
+ gdk_cairo_set_source_rgba (cr, border_color);
+ else
+ gdk_cairo_set_source_rgba (cr, fg_color);
+
+ cairo_stroke (cr);
+ }
+
+ gdk_cairo_set_source_rgba (cr, fg_color);
+
+ if (flags & GTK_STATE_FLAG_INCONSISTENT)
+ {
+ int line_thickness = MAX (1, (3 + interior_size * 2) / 7);
+
+ cairo_rectangle (cr,
+ x + pad,
+ y + pad + (1 + interior_size - line_thickness) / 2,
+ interior_size,
+ line_thickness);
+ cairo_fill (cr);
+ }
+ else
+ {
+ gdouble progress;
+ gboolean running;
+
+ running = gtk_theming_engine_state_is_running (engine, GTK_STATE_ACTIVE, &progress);
+
+ if ((flags & GTK_STATE_FLAG_ACTIVE) || running)
+ {
+ if (!running)
+ progress = 1;
+
+ cairo_translate (cr,
+ x + pad, y + pad);
+
+ cairo_scale (cr, interior_size / 7., interior_size / 7.);
+
+ cairo_rectangle (cr, 0, 0, 7 * progress, 7);
+ cairo_clip (cr);
+
+ cairo_move_to (cr, 7.0, 0.0);
+ cairo_line_to (cr, 7.5, 1.0);
+ cairo_curve_to (cr, 5.3, 2.0,
+ 4.3, 4.0,
+ 3.5, 7.0);
+ cairo_curve_to (cr, 3.0, 5.7,
+ 1.3, 4.7,
+ 0.0, 4.7);
+ cairo_line_to (cr, 0.2, 3.5);
+ cairo_curve_to (cr, 1.1, 3.5,
+ 2.3, 4.3,
+ 3.0, 5.0);
+ cairo_curve_to (cr, 1.0, 3.9,
+ 2.4, 4.1,
+ 3.2, 4.9);
+ cairo_curve_to (cr, 3.5, 3.1,
+ 5.2, 2.0,
+ 7.0, 0.0);
+
+ cairo_fill (cr);
+ }
+ }
+
+ cairo_restore (cr);
+
+ gdk_rgba_free (fg_color);
+ gdk_rgba_free (bg_color);
+ gdk_rgba_free (border_color);
+ gtk_border_free (border);
+}
+
+static void
+gtk_theming_engine_render_option (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStateFlags flags;
+ GdkRGBA *fg_color, *bg_color, *border_color;
+ gint exterior_size, interior_size, pad, thickness, border_width;
+ GtkBorderStyle border_style;
+ GtkBorder *border;
+ gdouble radius;
+
+ flags = gtk_theming_engine_get_state (engine);
+ radius = MIN (width, height) / 2 - 0.5;
+
+ cairo_save (cr);
+
+ gtk_theming_engine_get (engine, flags,
+ "color", &fg_color,
+ "background-color", &bg_color,
+ "border-color", &border_color,
+ "border-style", &border_style,
+ "border-width", &border,
+ NULL);
+
+ exterior_size = MIN (width, height);
+ border_width = MIN (MIN (border->top, border->bottom),
+ MIN (border->left, border->right));
+
+ if (exterior_size % 2 == 0) /* Ensure odd */
+ exterior_size -= 1;
+
+ x -= (1 + exterior_size - width) / 2;
+ y -= (1 + exterior_size - height) / 2;
+
+ if (border_style == GTK_BORDER_STYLE_SOLID)
+ {
+ cairo_set_line_width (cr, border_width);
+ cairo_arc (cr,
+ x + exterior_size / 2.,
+ y + exterior_size / 2.,
+ (exterior_size - 1) / 2.,
+ 0, 2 * G_PI);
+
+ gdk_cairo_set_source_rgba (cr, bg_color);
+ cairo_fill_preserve (cr);
+
+ if (border_color)
+ gdk_cairo_set_source_rgba (cr, border_color);
+ else
+ gdk_cairo_set_source_rgba (cr, fg_color);
+
+ cairo_stroke (cr);
+ }
+
+ gdk_cairo_set_source_rgba (cr, fg_color);
+
+ /* FIXME: thickness */
+ thickness = 1;
+
+ if (flags & GTK_STATE_FLAG_INCONSISTENT)
+ {
+ gint line_thickness;
+
+ pad = thickness + MAX (1, (exterior_size - 2 * thickness) / 9);
+ interior_size = MAX (1, exterior_size - 2 * pad);
+
+ if (interior_size < 7)
+ {
+ interior_size = 7;
+ pad = MAX (0, (exterior_size - interior_size) / 2);
+ }
+
+ line_thickness = MAX (1, (3 + interior_size * 2) / 7);
+
+ cairo_rectangle (cr,
+ x + pad,
+ y + pad + (interior_size - line_thickness) / 2.,
+ interior_size,
+ line_thickness);
+ cairo_fill (cr);
+ }
+ if (flags & GTK_STATE_FLAG_ACTIVE)
+ {
+ pad = thickness + MAX (1, 2 * (exterior_size - 2 * thickness) / 9);
+ interior_size = MAX (1, exterior_size - 2 * pad);
+
+ if (interior_size < 5)
+ {
+ interior_size = 7;
+ pad = MAX (0, (exterior_size - interior_size) / 2);
+ }
+
+ cairo_arc (cr,
+ x + pad + interior_size / 2.,
+ y + pad + interior_size / 2.,
+ interior_size / 2.,
+ 0, 2 * G_PI);
+ cairo_fill (cr);
+ }
+
+ cairo_restore (cr);
+
+ gdk_rgba_free (fg_color);
+ gdk_rgba_free (bg_color);
+ gdk_rgba_free (border_color);
+ gtk_border_free (border);
+}
+
+static void
+add_path_arrow (cairo_t *cr,
+ gdouble angle,
+ gdouble x,
+ gdouble y,
+ gdouble size)
+{
+ cairo_save (cr);
+
+ cairo_translate (cr, x + (size / 2), y + (size / 2));
+ cairo_rotate (cr, angle);
+
+ cairo_move_to (cr, 0, - (size / 4));
+ cairo_line_to (cr, - (size / 2), (size / 4));
+ cairo_line_to (cr, (size / 2), (size / 4));
+ cairo_close_path (cr);
+
+ cairo_restore (cr);
+}
+
+static void
+gtk_theming_engine_render_arrow (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble angle,
+ gdouble x,
+ gdouble y,
+ gdouble size)
+{
+ GtkStateFlags flags;
+ GdkRGBA *fg_color;
+
+ cairo_save (cr);
+
+ flags = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, flags,
+ "color", &fg_color,
+ NULL);
+
+ if (flags & GTK_STATE_FLAG_INSENSITIVE)
+ {
+ add_path_arrow (cr, angle, x + 1, y + 1, size);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ }
+
+ add_path_arrow (cr, angle, x, y, size);
+ gdk_cairo_set_source_rgba (cr, fg_color);
+ cairo_fill (cr);
+
+ cairo_restore (cr);
+
+ gdk_rgba_free (fg_color);
+}
+
+static void
+add_path_line (cairo_t *cr,
+ gdouble x1,
+ gdouble y1,
+ gdouble x2,
+ gdouble y2)
+{
+ /* Adjust endpoints */
+ if (y1 == y2)
+ {
+ y1 += 0.5;
+ y2 += 0.5;
+ x2 += 1;
+ }
+ else if (x1 == x2)
+ {
+ x1 += 0.5;
+ x2 += 0.5;
+ y2 += 1;
+ }
+
+ cairo_move_to (cr, x1, y1);
+ cairo_line_to (cr, x2, y2);
+}
+
+static void
+color_shade (const GdkRGBA *color,
+ gdouble factor,
+ GdkRGBA *color_return)
+{
+ GtkSymbolicColor *literal, *shade;
+
+ literal = gtk_symbolic_color_new_literal (color);
+ shade = gtk_symbolic_color_new_shade (literal, factor);
+ gtk_symbolic_color_unref (literal);
+
+ gtk_symbolic_color_resolve (shade, NULL, color_return);
+ gtk_symbolic_color_unref (shade);
+}
+
+static void
+_cairo_round_rectangle_sides (cairo_t *cr,
+ gdouble radius,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ guint sides,
+ GtkJunctionSides junction)
+{
+ radius = CLAMP (radius, 0, MIN (width / 2, height / 2));
+
+ if (sides & SIDE_RIGHT)
+ {
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_TOPRIGHT))
+ cairo_move_to (cr, x + width, y);
+ else
+ {
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x + width - radius, y + radius, radius, - G_PI / 4, 0);
+ }
+
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT))
+ cairo_line_to (cr, x + width, y + height);
+ else
+ cairo_arc (cr, x + width - radius, y + height - radius, radius, 0, G_PI / 4);
+ }
+
+ if (sides & SIDE_BOTTOM)
+ {
+ if (radius != 0 &&
+ ! (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT))
+ {
+ if ((sides & SIDE_RIGHT) == 0)
+ cairo_new_sub_path (cr);
+
+ cairo_arc (cr, x + width - radius, y + height - radius, radius, G_PI / 4, G_PI / 2);
+ }
+ else if ((sides & SIDE_RIGHT) == 0)
+ cairo_move_to (cr, x + width, y + height);
+
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT))
+ cairo_line_to (cr, x, y + height);
+ else
+ cairo_arc (cr, x + radius, y + height - radius, radius, G_PI / 2, 3 * (G_PI / 4));
+ }
+ else
+ cairo_move_to (cr, x, y + height);
+
+ if (sides & SIDE_LEFT)
+ {
+ if (radius != 0 &&
+ ! (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT))
+ {
+ if ((sides & SIDE_BOTTOM) == 0)
+ cairo_new_sub_path (cr);
+
+ cairo_arc (cr, x + radius, y + height - radius, radius, 3 * (G_PI / 4), G_PI);
+ }
+ else if ((sides & SIDE_BOTTOM) == 0)
+ cairo_move_to (cr, x, y + height);
+
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_TOPLEFT))
+ cairo_line_to (cr, x, y);
+ else
+ cairo_arc (cr, x + radius, y + radius, radius, G_PI, G_PI + G_PI / 4);
+ }
+
+ if (sides & SIDE_TOP)
+ {
+ if (radius != 0 &&
+ ! (junction & GTK_JUNCTION_CORNER_TOPLEFT))
+ {
+ if ((sides & SIDE_LEFT) == 0)
+ cairo_new_sub_path (cr);
+
+ cairo_arc (cr, x + radius, y + radius, radius, 5 * (G_PI / 4), 3 * (G_PI / 2));
+ }
+ else if ((sides & SIDE_LEFT) == 0)
+ cairo_move_to (cr, x, y);
+
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_TOPRIGHT))
+ cairo_line_to (cr, x + width, y);
+ else
+ cairo_arc (cr, x + width - radius, y + radius, radius, 3 * (G_PI / 2), - G_PI / 4);
+ }
+}
+
+static void
+render_background_internal (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkJunctionSides junction)
+{
+ GdkRGBA *bg_color;
+ cairo_pattern_t *pattern;
+ GtkStateFlags flags;
+ gboolean running;
+ gdouble progress, alpha = 1;
+ gint radius, border_width;
+ GtkBorder *border;
+
+ flags = gtk_theming_engine_get_state (engine);
+ cairo_save (cr);
+
+ gtk_theming_engine_get (engine, flags,
+ "background-image", &pattern,
+ "background-color", &bg_color,
+ "border-width", &border,
+ "border-radius", &radius,
+ NULL);
+
+ running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
+ border_width = MIN (MIN (border->top, border->bottom),
+ MIN (border->left, border->right));
+
+ _cairo_round_rectangle_sides (cr, (gdouble) radius,
+ x, y, width, height,
+ SIDE_ALL, junction);
+ cairo_clip (cr);
+
+ if (gtk_theming_engine_has_class (engine, "background"))
+ {
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); /* transparent */
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ }
+
+ cairo_translate (cr, x, y);
+ cairo_scale (cr, width, height);
+
+ if (running)
+ {
+ cairo_pattern_t *other_pattern;
+ GtkStateFlags other_flags;
+ GdkRGBA *other_bg;
+ cairo_pattern_t *new_pattern = NULL;
+
+ if (flags & GTK_STATE_FLAG_PRELIGHT)
+ {
+ other_flags = flags & ~(GTK_STATE_FLAG_PRELIGHT);
+ progress = 1 - progress;
+ }
+ else
+ other_flags = flags | GTK_STATE_FLAG_PRELIGHT;
+
+ gtk_theming_engine_get (engine, other_flags,
+ "background-image", &other_pattern,
+ "background-color", &other_bg,
+ NULL);
+
+ if (pattern && other_pattern)
+ {
+ cairo_pattern_type_t type, other_type;
+ gint n0, n1;
+
+ cairo_pattern_get_color_stop_count (pattern, &n0);
+ cairo_pattern_get_color_stop_count (other_pattern, &n1);
+ type = cairo_pattern_get_type (pattern);
+ other_type = cairo_pattern_get_type (other_pattern);
+
+ if (type == other_type && n0 == n1)
+ {
+ gdouble offset0, red0, green0, blue0, alpha0;
+ gdouble offset1, red1, green1, blue1, alpha1;
+ gdouble x00, x01, y00, y01, x10, x11, y10, y11;
+ gdouble r00, r01, r10, r11;
+ guint i;
+
+ if (type == CAIRO_PATTERN_TYPE_LINEAR)
+ {
+ cairo_pattern_get_linear_points (pattern, &x00, &y00, &x01, &y01);
+ cairo_pattern_get_linear_points (other_pattern, &x10, &y10, &x11, &y11);
+
+ new_pattern = cairo_pattern_create_linear (x00 + (x10 - x00) * progress,
+ y00 + (y10 - y00) * progress,
+ x01 + (x11 - x01) * progress,
+ y01 + (y11 - y01) * progress);
+ }
+ else
+ {
+ cairo_pattern_get_radial_circles (pattern, &x00, &y00, &r00, &x01, &y01, &r01);
+ cairo_pattern_get_radial_circles (other_pattern, &x10, &y10, &r10, &x11, &y11, &r11);
+
+ new_pattern = cairo_pattern_create_radial (x00 + (x10 - x00) * progress,
+ y00 + (y10 - y00) * progress,
+ r00 + (r10 - r00) * progress,
+ x01 + (x11 - x01) * progress,
+ y01 + (y11 - y01) * progress,
+ r01 + (r11 - r01) * progress);
+ }
+
+ cairo_pattern_set_filter (new_pattern, CAIRO_FILTER_FAST);
+ i = 0;
+
+ /* Blend both gradients into one */
+ while (i < n0 && i < n1)
+ {
+ cairo_pattern_get_color_stop_rgba (pattern, i,
+ &offset0,
+ &red0, &green0, &blue0,
+ &alpha0);
+ cairo_pattern_get_color_stop_rgba (other_pattern, i,
+ &offset1,
+ &red1, &green1, &blue1,
+ &alpha1);
+
+ cairo_pattern_add_color_stop_rgba (new_pattern,
+ offset0 + ((offset1 - offset0) * progress),
+ red0 + ((red1 - red0) * progress),
+ green0 + ((green1 - green0) * progress),
+ blue0 + ((blue1 - blue0) * progress),
+ alpha0 + ((alpha1 - alpha0) * progress));
+ i++;
+ }
+ }
+ else
+ {
+ /* Different pattern types, or different color
+ * stop counts, alpha blend both patterns.
+ */
+ cairo_rectangle (cr, 0, 0, 1, 1);
+ cairo_set_source (cr, other_pattern);
+ cairo_fill_preserve (cr);
+
+ /* Set alpha for posterior drawing
+ * of the target pattern
+ */
+ alpha = 1 - progress;
+ }
+ }
+ else if (pattern || other_pattern)
+ {
+ cairo_pattern_t *p;
+ const GdkRGBA *c;
+ gdouble x0, y0, x1, y1, r0, r1;
+ gint n, i;
+
+ /* Blend a pattern with a color */
+ if (pattern)
+ {
+ p = pattern;
+ c = other_bg;
+ progress = 1 - progress;
+ }
+ else
+ {
+ p = other_pattern;
+ c = bg_color;
+ }
+
+ if (cairo_pattern_get_type (p) == CAIRO_PATTERN_TYPE_LINEAR)
+ {
+ cairo_pattern_get_linear_points (p, &x0, &y0, &x1, &y1);
+ new_pattern = cairo_pattern_create_linear (x0, y0, x1, y1);
+ }
+ else
+ {
+ cairo_pattern_get_radial_circles (p, &x0, &y0, &r0, &x1, &y1, &r1);
+ new_pattern = cairo_pattern_create_radial (x0, y0, r0, x1, y1, r1);
+ }
+
+ cairo_pattern_get_color_stop_count (p, &n);
+
+ for (i = 0; i < n; i++)
+ {
+ gdouble red1, green1, blue1, alpha1;
+ gdouble offset;
+
+ cairo_pattern_get_color_stop_rgba (p, i,
+ &offset,
+ &red1, &green1, &blue1,
+ &alpha1);
+ cairo_pattern_add_color_stop_rgba (new_pattern, offset,
+ c->red + ((red1 - c->red) * progress),
+ c->green + ((green1 - c->green) * progress),
+ c->blue + ((blue1 - c->blue) * progress),
+ c->alpha + ((alpha1 - c->alpha) * progress));
+ }
+ }
+ else
+ {
+ const GdkRGBA *color, *other_color;
+
+ /* Merge just colors */
+ color = bg_color;
+ other_color = other_bg;
+
+ new_pattern = cairo_pattern_create_rgba (CLAMP (color->red + ((other_color->red - color->red) * progress), 0, 1),
+ CLAMP (color->green + ((other_color->green - color->green) * progress), 0, 1),
+ CLAMP (color->blue + ((other_color->blue - color->blue) * progress), 0, 1),
+ CLAMP (color->alpha + ((other_color->alpha - color->alpha) * progress), 0, 1));
+ }
+
+ if (new_pattern)
+ {
+ /* Replace pattern to use */
+ cairo_pattern_destroy (pattern);
+ pattern = new_pattern;
+ }
+
+ if (other_pattern)
+ cairo_pattern_destroy (other_pattern);
+
+ if (other_bg)
+ gdk_rgba_free (other_bg);
+ }
+
+ cairo_rectangle (cr, 0, 0, 1, 1);
+
+ if (pattern)
+ {
+ cairo_set_source (cr, pattern);
+ cairo_pattern_destroy (pattern);
+ }
+ else
+ gdk_cairo_set_source_rgba (cr, bg_color);
+
+ if (alpha == 1)
+ cairo_fill (cr);
+ else
+ {
+ cairo_pattern_t *mask;
+
+ mask = cairo_pattern_create_rgba (1, 1, 1, alpha);
+ cairo_mask (cr, mask);
+ cairo_pattern_destroy (mask);
+ }
+
+ cairo_restore (cr);
+
+ gdk_rgba_free (bg_color);
+ gtk_border_free (border);
+}
+
+static void
+gtk_theming_engine_render_background (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkJunctionSides junction;
+ GtkStateFlags flags;
+ GtkBorder *border;
+
+ junction = gtk_theming_engine_get_junction_sides (engine);
+
+ if (gtk_theming_engine_has_class (engine, "spinbutton") &&
+ gtk_theming_engine_has_class (engine, "button"))
+ {
+ x += 2;
+ y += 2;
+ width -= 4;
+ height -= 4;
+ }
+
+ flags = gtk_theming_engine_get_state (engine);
+ gtk_theming_engine_get (engine, flags,
+ "border-width", &border,
+ NULL);
+
+ x += border->left;
+ y += border->top;
+ width -= border->left + border->right;
+ height -= border->top + border->bottom;
+
+ render_background_internal (engine, cr,
+ x, y, width, height,
+ junction);
+
+ gtk_border_free (border);
+}
+
+/* Renders the small triangle on corners so
+ * frames with 0 radius have a 3D-like effect
+ */
+static void
+_cairo_corner_triangle (cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gint size)
+{
+ gint i;
+
+ cairo_move_to (cr, x + 0.5, y + size - 0.5);
+ cairo_line_to (cr, x + size - 0.5, y + size - 0.5);
+ cairo_line_to (cr, x + size - 0.5, y + 0.5);
+
+ for (i = 1; i < size - 1; i++)
+ {
+ cairo_move_to (cr, x + size - 0.5, y + i + 0.5);
+ cairo_line_to (cr, x + (size - i) - 0.5, y + i + 0.5);
+ }
+}
+
+static void
+render_frame_internal (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ guint hidden_side,
+ GtkJunctionSides junction)
+{
+ GtkStateFlags state;
+ GdkRGBA lighter;
+ GdkRGBA *border_color;
+ GtkBorderStyle border_style;
+ gint border_width, radius;
+ gdouble progress, d1, d2, m;
+ gboolean running;
+ GtkBorder *border;
+
+ state = gtk_theming_engine_get_state (engine);
+ gtk_theming_engine_get (engine, state,
+ "border-color", &border_color,
+ "border-style", &border_style,
+ "border-width", &border,
+ "border-radius", &radius,
+ NULL);
+
+ running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
+ border_width = MIN (MIN (border->top, border->bottom),
+ MIN (border->left, border->right));
+
+ if (running)
+ {
+ GtkStateFlags other_state;
+ GdkRGBA *other_color;
+
+ if (state & GTK_STATE_FLAG_PRELIGHT)
+ {
+ other_state = state & ~(GTK_STATE_FLAG_PRELIGHT);
+ progress = 1 - progress;
+ }
+ else
+ other_state = state | GTK_STATE_FLAG_PRELIGHT;
+
+ gtk_theming_engine_get (engine, other_state,
+ "border-color", &other_color,
+ NULL);
+
+ border_color->red = CLAMP (border_color->red + ((other_color->red - border_color->red) * progress), 0, 1);
+ border_color->green = CLAMP (border_color->green + ((other_color->green - border_color->green) * progress), 0, 1);
+ border_color->blue = CLAMP (border_color->blue + ((other_color->blue - border_color->blue) * progress), 0, 1);
+ border_color->alpha = CLAMP (border_color->alpha + ((other_color->alpha - border_color->alpha) * progress), 0, 1);
+
+ gdk_rgba_free (other_color);
+ }
+
+ cairo_save (cr);
+
+ color_shade (border_color, 1.8, &lighter);
+
+ switch (border_style)
+ {
+ case GTK_BORDER_STYLE_NONE:
+ break;
+ case GTK_BORDER_STYLE_SOLID:
+ cairo_set_line_width (cr, border_width);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+
+ if (border_width > 1)
+ {
+ x += (gdouble) border_width / 2;
+ y += (gdouble) border_width / 2;
+ width -= border_width;
+ height -= border_width;
+ }
+ else if (border_width == 1)
+ {
+ x += 0.5;
+ y += 0.5;
+ width -= 1;
+ height -= 1;
+ }
+
+ _cairo_round_rectangle_sides (cr, (gdouble) radius,
+ x, y, width, height,
+ SIDE_ALL & ~(hidden_side),
+ junction);
+ gdk_cairo_set_source_rgba (cr, border_color);
+ cairo_stroke (cr);
+
+ break;
+ case GTK_BORDER_STYLE_INSET:
+ case GTK_BORDER_STYLE_OUTSET:
+ cairo_set_line_width (cr, border_width);
+
+ if (radius == 0)
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+ else
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+
+ if (border_width > 1)
+ {
+ d1 = (gdouble) border_width / 2;
+ d2 = border_width;
+ }
+ else
+ {
+ d1 = 0.5;
+ d2 = 1;
+ }
+
+ cairo_save (cr);
+
+ m = MIN (width, height);
+ m /= 2;
+
+ if (border_style == GTK_BORDER_STYLE_INSET)
+ gdk_cairo_set_source_rgba (cr, &lighter);
+ else
+ gdk_cairo_set_source_rgba (cr, border_color);
+
+ _cairo_round_rectangle_sides (cr, (gdouble) radius,
+ x + d1, y + d1,
+ width - d2, height - d2,
+ (SIDE_BOTTOM | SIDE_RIGHT) & ~(hidden_side),
+ junction);
+ cairo_stroke (cr);
+
+ if (border_style == GTK_BORDER_STYLE_INSET)
+ gdk_cairo_set_source_rgba (cr, border_color);
+ else
+ gdk_cairo_set_source_rgba (cr, &lighter);
+
+ _cairo_round_rectangle_sides (cr, (gdouble) radius,
+ x + d1, y + d1,
+ width - d2, height - d2,
+ (SIDE_TOP | SIDE_LEFT) & ~(hidden_side),
+ junction);
+ cairo_stroke (cr);
+
+ if (border_width > 1)
+ {
+ /* overprint top/right and bottom/left corner
+ * triangles if there are square corners there,
+ * to give the box a 3D-like appearance.
+ */
+ cairo_save (cr);
+
+ if (border_style == GTK_BORDER_STYLE_INSET)
+ gdk_cairo_set_source_rgba (cr, &lighter);
+ else
+ gdk_cairo_set_source_rgba (cr, border_color);
+
+ cairo_set_line_width (cr, 1);
+
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_TOPRIGHT) != 0)
+ _cairo_corner_triangle (cr,
+ x + width - border_width, y,
+ border_width);
+
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT) != 0)
+ _cairo_corner_triangle (cr,
+ x, y + height - border_width,
+ border_width);
+ cairo_stroke (cr);
+ cairo_restore (cr);
+ }
+
+ cairo_restore (cr);
+ break;
+ }
+
+ cairo_restore (cr);
+
+ if (border_color)
+ gdk_rgba_free (border_color);
+
+ gtk_border_free (border);
+}
+
+static void
+gtk_theming_engine_render_frame (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStateFlags flags;
+ Gtk9Slice *slice;
+ GtkBorderStyle border_style;
+ GtkJunctionSides junction;
+
+ flags = gtk_theming_engine_get_state (engine);
+ junction = gtk_theming_engine_get_junction_sides (engine);
+
+ gtk_theming_engine_get (engine, flags,
+ "border-image", &slice,
+ "border-style", &border_style,
+ NULL);
+
+ if (slice)
+ {
+ gtk_9slice_render (slice, cr, x, y, width, height);
+ gtk_9slice_unref (slice);
+ }
+ else if (border_style != GTK_BORDER_STYLE_NONE)
+ render_frame_internal (engine, cr,
+ x, y, width, height,
+ 0, junction);
+}
+
+static void
+gtk_theming_engine_render_expander (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStateFlags flags;
+ GdkRGBA *outline_color, *fg_color;
+ double vertical_overshoot;
+ int diameter;
+ double radius;
+ double interp; /* interpolation factor for center position */
+ double x_double_horz, y_double_horz;
+ double x_double_vert, y_double_vert;
+ double x_double, y_double;
+ gdouble angle;
+ gint line_width;
+
+ cairo_save (cr);
+ flags = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, flags,
+ "color", &fg_color,
+ NULL);
+ gtk_theming_engine_get (engine, 0,
+ "color", &outline_color,
+ NULL);
+
+ line_width = 1;
+
+ /* FIXME: LTR/RTL */
+ if (flags & GTK_STATE_FLAG_ACTIVE)
+ {
+ angle = G_PI / 2;
+ interp = 1.0;
+ }
+ else
+ {
+ angle = 0;
+ interp = 0;
+ }
+
+ /* Compute distance that the stroke extends beyonds the end
+ * of the triangle we draw.
+ */
+ vertical_overshoot = line_width / 2.0 * (1. / tan (G_PI / 8));
+
+ /* For odd line widths, we end the vertical line of the triangle
+ * at a half pixel, so we round differently.
+ */
+ if (line_width % 2 == 1)
+ vertical_overshoot = ceil (0.5 + vertical_overshoot) - 0.5;
+ else
+ vertical_overshoot = ceil (vertical_overshoot);
+
+ /* Adjust the size of the triangle we draw so that the entire stroke fits
+ */
+ diameter = (gint) MAX (3, width - 2 * vertical_overshoot);
+
+ /* If the line width is odd, we want the diameter to be even,
+ * and vice versa, so force the sum to be odd. This relationship
+ * makes the point of the triangle look right.
+ */
+ diameter -= (1 - (diameter + line_width) % 2);
+
+ radius = diameter / 2.;
+
+ /* Adjust the center so that the stroke is properly aligned with
+ * the pixel grid. The center adjustment is different for the
+ * horizontal and vertical orientations. For intermediate positions
+ * we interpolate between the two.
+ */
+ x_double_vert = floor ((x + width / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
+ y_double_vert = (y + height / 2) - 0.5;
+
+ x_double_horz = (x + width / 2) - 0.5;
+ y_double_horz = floor ((y + height / 2) - (radius + line_width) / 2.) + (radius + line_width) / 2.;
+
+ x_double = x_double_vert * (1 - interp) + x_double_horz * interp;
+ y_double = y_double_vert * (1 - interp) + y_double_horz * interp;
+
+ cairo_translate (cr, x_double, y_double);
+ cairo_rotate (cr, angle);
+
+ cairo_move_to (cr, - radius / 2., - radius);
+ cairo_line_to (cr, radius / 2., 0);
+ cairo_line_to (cr, - radius / 2., radius);
+ cairo_close_path (cr);
+
+ cairo_set_line_width (cr, line_width);
+
+ gdk_cairo_set_source_rgba (cr, fg_color);
+
+ cairo_fill_preserve (cr);
+
+ gdk_cairo_set_source_rgba (cr, outline_color);
+ cairo_stroke (cr);
+
+ cairo_restore (cr);
+
+ gdk_rgba_free (fg_color);
+ gdk_rgba_free (outline_color);
+}
+
+static void
+gtk_theming_engine_render_focus (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStateFlags flags;
+ GdkRGBA *color;
+ gint line_width;
+ gint8 *dash_list;
+
+ cairo_save (cr);
+ flags = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, flags,
+ "color", &color,
+ NULL);
+
+ gtk_theming_engine_get_style (engine,
+ "focus-line-width", &line_width,
+ "focus-line-pattern", (gchar *) &dash_list,
+ NULL);
+
+ cairo_set_line_width (cr, (gdouble) line_width);
+
+ if (dash_list[0])
+ {
+ gint n_dashes = strlen ((const gchar *) dash_list);
+ gdouble *dashes = g_new (gdouble, n_dashes);
+ gdouble total_length = 0;
+ gdouble dash_offset;
+ gint i;
+
+ for (i = 0; i < n_dashes; i++)
+ {
+ dashes[i] = dash_list[i];
+ total_length += dash_list[i];
+ }
+
+ /* The dash offset here aligns the pattern to integer pixels
+ * by starting the dash at the right side of the left border
+ * Negative dash offsets in cairo don't work
+ * (https://bugs.freedesktop.org/show_bug.cgi?id=2729)
+ */
+ dash_offset = - line_width / 2.;
+
+ while (dash_offset < 0)
+ dash_offset += total_length;
+
+ cairo_set_dash (cr, dashes, n_dashes, dash_offset);
+ g_free (dashes);
+ }
+
+ cairo_rectangle (cr,
+ x + line_width / 2.,
+ y + line_width / 2.,
+ width - line_width,
+ height - line_width);
+
+ gdk_cairo_set_source_rgba (cr, color);
+ cairo_stroke (cr);
+
+ cairo_restore (cr);
+
+ gdk_rgba_free (color);
+ g_free (dash_list);
+}
+
+static void
+gtk_theming_engine_render_line (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1)
+{
+ GdkRGBA *bg_color, darker, lighter;
+ GtkStateFlags flags;
+ gint i, thickness, thickness_dark, thickness_light, len;
+ cairo_matrix_t matrix;
+ gdouble angle;
+
+ /* FIXME: thickness */
+ thickness = 2;
+ thickness_dark = thickness / 2;
+ thickness_light = thickness - thickness_dark;
+
+ flags = gtk_theming_engine_get_state (engine);
+ cairo_save (cr);
+
+ gtk_theming_engine_get (engine, flags,
+ "background-color", &bg_color,
+ NULL);
+ color_shade (bg_color, 0.7, &darker);
+ color_shade (bg_color, 1.3, &lighter);
+
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+ cairo_set_line_width (cr, 1);
+
+ angle = atan2 (x1 - x0, y1 - y0);
+ angle = (2 * G_PI) - angle;
+ angle += G_PI / 2;
+
+ cairo_get_matrix (cr, &matrix);
+ cairo_matrix_translate (&matrix, x0, y0);
+ cairo_matrix_rotate (&matrix, angle);
+ cairo_set_matrix (cr, &matrix);
+
+ x1 -= x0;
+ y1 -= y0;
+
+ len = (gint) sqrt ((x1 * x1) + (y1 * y1));
+
+ y0 = -thickness_dark;
+
+ for (i = 0; i < thickness_dark; i++)
+ {
+ gdk_cairo_set_source_rgba (cr, &lighter);
+ add_path_line (cr, len - i - 1.5, y0, len - 0.5, y0);
+ cairo_stroke (cr);
+
+ gdk_cairo_set_source_rgba (cr, &darker);
+ add_path_line (cr, 0.5, y0, len - i - 1.5, y0);
+ cairo_stroke (cr);
+
+ y0++;
+ }
+
+ for (i = 0; i < thickness_light; i++)
+ {
+ gdk_cairo_set_source_rgba (cr, &darker);
+ add_path_line (cr, 0.5, y0, thickness_light - i + 0.5, y0);
+ cairo_stroke (cr);
+
+ gdk_cairo_set_source_rgba (cr, &lighter);
+ add_path_line (cr, thickness_light - i + 0.5, y0, len - 0.5, y0);
+ cairo_stroke (cr);
+
+ y0++;
+ }
+
+ cairo_restore (cr);
+
+ gdk_rgba_free (bg_color);
+}
+
+static void
+gtk_theming_engine_render_layout (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ PangoLayout *layout)
+{
+ const PangoMatrix *matrix;
+ GdkRGBA *fg_color;
+ GtkStateFlags flags;
+ GdkScreen *screen;
+ gdouble progress;
+ gboolean running;
+
+ cairo_save (cr);
+ flags = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, flags,
+ "color", &fg_color,
+ NULL);
+
+ screen = gtk_theming_engine_get_screen (engine);
+ matrix = pango_context_get_matrix (pango_layout_get_context (layout));
+
+ running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
+
+ if (running)
+ {
+ GtkStateFlags other_flags;
+ GdkRGBA *other_fg;
+
+ if (flags & GTK_STATE_FLAG_PRELIGHT)
+ {
+ other_flags = flags & ~(GTK_STATE_FLAG_PRELIGHT);
+ progress = 1 - progress;
+ }
+ else
+ other_flags = flags | GTK_STATE_FLAG_PRELIGHT;
+
+ gtk_theming_engine_get (engine, other_flags,
+ "color", &other_fg,
+ NULL);
+
+ if (fg_color && other_fg)
+ {
+ fg_color->red = CLAMP (fg_color->red + ((other_fg->red - fg_color->red) * progress), 0, 1);
+ fg_color->green = CLAMP (fg_color->green + ((other_fg->green - fg_color->green) * progress), 0, 1);
+ fg_color->blue = CLAMP (fg_color->blue + ((other_fg->blue - fg_color->blue) * progress), 0, 1);
+ fg_color->alpha = CLAMP (fg_color->alpha + ((other_fg->alpha - fg_color->alpha) * progress), 0, 1);
+ }
+
+ if (other_fg)
+ gdk_rgba_free (other_fg);
+ }
+
+ if (matrix)
+ {
+ cairo_matrix_t cairo_matrix;
+ PangoMatrix tmp_matrix;
+ PangoRectangle rect;
+
+ cairo_matrix_init (&cairo_matrix,
+ matrix->xx, matrix->yx,
+ matrix->xy, matrix->yy,
+ matrix->x0, matrix->y0);
+
+ pango_layout_get_extents (layout, NULL, &rect);
+ pango_matrix_transform_rectangle (matrix, &rect);
+ pango_extents_to_pixels (&rect, NULL);
+
+ tmp_matrix = *matrix;
+ cairo_matrix.x0 += x - rect.x;
+ cairo_matrix.y0 += y - rect.y;
+
+ cairo_set_matrix (cr, &cairo_matrix);
+ }
+ else
+ cairo_translate (cr, x, y);
+
+ if (flags & GTK_STATE_FLAG_INSENSITIVE)
+ {
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_move_to (cr, 1, 1);
+ _gtk_pango_fill_layout (cr, layout);
+ cairo_restore (cr);
+ }
+
+ gdk_cairo_set_source_rgba (cr, fg_color);
+ pango_cairo_show_layout (cr, layout);
+
+ cairo_restore (cr);
+
+ gdk_rgba_free (fg_color);
+}
+
+static void
+gtk_theming_engine_render_slider (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkOrientation orientation)
+{
+ const GtkWidgetPath *path;
+ gint thickness;
+
+ path = gtk_theming_engine_get_path (engine);
+
+ gtk_theming_engine_render_background (engine, cr, x, y, width, height);
+ gtk_theming_engine_render_frame (engine, cr, x, y, width, height);
+
+ /* FIXME: thickness */
+ thickness = 2;
+
+ if (gtk_widget_path_is_type (path, GTK_TYPE_SCALE))
+ {
+ if (orientation == GTK_ORIENTATION_VERTICAL)
+ gtk_theming_engine_render_line (engine, cr,
+ x + thickness,
+ y + (gint) height / 2,
+ x + width - thickness - 1,
+ y + (gint) height / 2);
+ else
+ gtk_theming_engine_render_line (engine, cr,
+ x + (gint) width / 2,
+ y + thickness,
+ x + (gint) width / 2,
+ y + height - thickness - 1);
+ }
+}
+
+static void
+gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkPositionType gap_side,
+ gdouble xy0_gap,
+ gdouble xy1_gap)
+{
+ GtkJunctionSides junction;
+ GtkStateFlags state;
+ gint border_width, radius;
+ gdouble x0, y0, x1, y1, xc, yc, wc, hc;
+ GtkBorder *border;
+
+ xc = yc = wc = hc = 0;
+ state = gtk_theming_engine_get_state (engine);
+ junction = gtk_theming_engine_get_junction_sides (engine);
+ gtk_theming_engine_get (engine, state,
+ "border-width", &border,
+ "border-radius", &radius,
+ NULL);
+
+ border_width = MIN (MIN (border->top, border->bottom),
+ MIN (border->left, border->right));
+
+ cairo_save (cr);
+
+ switch (gap_side)
+ {
+ case GTK_POS_TOP:
+ xc = x + xy0_gap + border_width;
+ yc = y;
+ wc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
+ hc = border_width;
+
+ if (xy0_gap < radius)
+ junction |= GTK_JUNCTION_CORNER_TOPLEFT;
+
+ if (xy1_gap > width - radius)
+ junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
+ break;
+ case GTK_POS_BOTTOM:
+ xc = x + xy0_gap + border_width;
+ yc = y + height - border_width;
+ wc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
+ hc = border_width;
+
+ if (xy0_gap < radius)
+ junction |= GTK_JUNCTION_CORNER_BOTTOMLEFT;
+
+ if (xy1_gap > width - radius)
+ junction |= GTK_JUNCTION_CORNER_BOTTOMRIGHT;
+
+ break;
+ case GTK_POS_LEFT:
+ xc = x;
+ yc = y + xy0_gap + border_width;
+ wc = border_width;
+ hc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
+
+ if (xy0_gap < radius)
+ junction |= GTK_JUNCTION_CORNER_TOPLEFT;
+
+ if (xy1_gap > height - radius)
+ junction |= GTK_JUNCTION_CORNER_BOTTOMLEFT;
+
+ break;
+ case GTK_POS_RIGHT:
+ xc = x + width - border_width;
+ yc = y + xy0_gap + border_width;
+ wc = border_width;
+ hc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
+
+ if (xy0_gap < radius)
+ junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
+
+ if (xy1_gap > height - radius)
+ junction |= GTK_JUNCTION_CORNER_BOTTOMRIGHT;
+
+ break;
+ }
+
+ cairo_clip_extents (cr, &x0, &y0, &x1, &y1);
+ cairo_rectangle (cr, x0, y0, x1 - x0, yc - y0);
+ cairo_rectangle (cr, x0, yc, xc - x0, hc);
+ cairo_rectangle (cr, xc + wc, yc, x1 - (xc + wc), hc);
+ cairo_rectangle (cr, x0, yc + hc, x1 - x0, y1 - (yc + hc));
+ cairo_clip (cr);
+
+ render_frame_internal (engine, cr,
+ x, y, width, height,
+ 0, junction);
+
+ cairo_restore (cr);
+
+ gtk_border_free (border);
+}
+
+static void
+gtk_theming_engine_render_extension (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkPositionType gap_side)
+{
+ GtkJunctionSides junction = 0;
+ guint hidden_side = 0;
+
+ cairo_save (cr);
+
+ switch (gap_side)
+ {
+ case GTK_POS_LEFT:
+ junction = GTK_JUNCTION_LEFT;
+ hidden_side = SIDE_LEFT;
+
+ cairo_translate (cr, x + width, y);
+ cairo_rotate (cr, G_PI / 2);
+ break;
+ case GTK_POS_RIGHT:
+ junction = GTK_JUNCTION_RIGHT;
+ hidden_side = SIDE_RIGHT;
+
+ cairo_translate (cr, x, y + height);
+ cairo_rotate (cr, - G_PI / 2);
+ break;
+ case GTK_POS_TOP:
+ junction = GTK_JUNCTION_TOP;
+ hidden_side = SIDE_TOP;
+
+ cairo_translate (cr, x + width, y + height);
+ cairo_rotate (cr, G_PI);
+ break;
+ case GTK_POS_BOTTOM:
+ junction = GTK_JUNCTION_BOTTOM;
+ hidden_side = SIDE_BOTTOM;
+
+ cairo_translate (cr, x, y);
+ break;
+ }
+
+ if (gap_side == GTK_POS_TOP ||
+ gap_side == GTK_POS_BOTTOM)
+ render_background_internal (engine, cr,
+ 0, 0, width, height,
+ junction);
+ else
+ render_background_internal (engine, cr,
+ 0, 0, height, width,
+ junction);
+
+ cairo_restore (cr);
+
+ cairo_save (cr);
+
+ render_frame_internal (engine, cr,
+ x, y, width, height,
+ hidden_side, junction);
+
+ cairo_restore (cr);
+}
+
+static void
+render_dot (cairo_t *cr,
+ const GdkRGBA *lighter,
+ const GdkRGBA *darker,
+ gdouble x,
+ gdouble y,
+ gdouble size)
+{
+ size = CLAMP ((gint) size, 2, 3);
+
+ if (size == 2)
+ {
+ gdk_cairo_set_source_rgba (cr, lighter);
+ cairo_rectangle (cr, x, y, 1, 1);
+ cairo_rectangle (cr, x + 1, y + 1, 1, 1);
+ cairo_fill (cr);
+ }
+ else if (size == 3)
+ {
+ gdk_cairo_set_source_rgba (cr, lighter);
+ cairo_rectangle (cr, x, y, 2, 1);
+ cairo_rectangle (cr, x, y, 1, 2);
+ cairo_fill (cr);
+
+ gdk_cairo_set_source_rgba (cr, darker);
+ cairo_rectangle (cr, x + 1, y + 1, 2, 1);
+ cairo_rectangle (cr, x + 2, y, 1, 2);
+ cairo_fill (cr);
+ }
+}
+
+static void
+gtk_theming_engine_render_handle (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStateFlags flags;
+ GdkRGBA *bg_color;
+ GdkRGBA lighter, darker;
+ gint xx, yy;
+
+ cairo_save (cr);
+ flags = gtk_theming_engine_get_state (engine);
+
+ cairo_set_line_width (cr, 1);
+
+ gtk_theming_engine_get (engine, flags,
+ "background-color", &bg_color,
+ NULL);
+ color_shade (bg_color, 0.7, &darker);
+ color_shade (bg_color, 1.3, &lighter);
+
+ gdk_cairo_set_source_rgba (cr, bg_color);
+ cairo_rectangle (cr, x, y, width, height);
+ cairo_fill (cr);
+
+ if (gtk_theming_engine_has_class (engine, "grip"))
+ {
+ GtkJunctionSides sides;
+ gint skip = -1;
+
+ cairo_save (cr);
+
+ cairo_set_line_width (cr, 1.0);
+ sides = gtk_theming_engine_get_junction_sides (engine);
+
+ /* reduce confusing values to a meaningful state */
+ if ((sides & (GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT)) == (GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT))
+ sides &= ~GTK_JUNCTION_CORNER_TOPLEFT;
+
+ if ((sides & (GTK_JUNCTION_CORNER_TOPRIGHT | GTK_JUNCTION_CORNER_BOTTOMLEFT)) == (GTK_JUNCTION_CORNER_TOPRIGHT | GTK_JUNCTION_CORNER_BOTTOMLEFT))
+ sides &= ~GTK_JUNCTION_CORNER_TOPRIGHT;
+
+ if (sides == 0)
+ sides = GTK_JUNCTION_CORNER_BOTTOMRIGHT;
+
+ /* align drawing area to the connected side */
+ if (sides == GTK_JUNCTION_LEFT)
+ {
+ if (height < width)
+ width = height;
+ }
+ else if (sides == GTK_JUNCTION_CORNER_TOPLEFT)
+ {
+ if (width < height)
+ height = width;
+ else if (height < width)
+ width = height;
+
+ skip = 2;
+ }
+ else if (sides == GTK_JUNCTION_CORNER_BOTTOMLEFT)
+ {
+ /* make it square, aligning to bottom left */
+ if (width < height)
+ {
+ y += (height - width);
+ height = width;
+ }
+ else if (height < width)
+ width = height;
+
+ skip = 1;
+ }
+ else if (sides == GTK_JUNCTION_RIGHT)
+ {
+ /* aligning to right */
+ if (height < width)
+ {
+ x += (width - height);
+ width = height;
+ }
+ }
+ else if (sides == GTK_JUNCTION_CORNER_TOPRIGHT)
+ {
+ if (width < height)
+ height = width;
+ else if (height < width)
+ {
+ x += (width - height);
+ width = height;
+ }
+
+ skip = 3;
+ }
+ else if (sides == GTK_JUNCTION_CORNER_BOTTOMRIGHT)
+ {
+ /* make it square, aligning to bottom right */
+ if (width < height)
+ {
+ y += (height - width);
+ height = width;
+ }
+ else if (height < width)
+ {
+ x += (width - height);
+ width = height;
+ }
+
+ skip = 0;
+ }
+ else if (sides == GTK_JUNCTION_TOP)
+ {
+ if (width < height)
+ height = width;
+ }
+ else if (sides == GTK_JUNCTION_BOTTOM)
+ {
+ /* align to bottom */
+ if (width < height)
+ {
+ y += (height - width);
+ height = width;
+ }
+ }
+ else
+ g_assert_not_reached ();
+
+ if (sides == GTK_JUNCTION_LEFT ||
+ sides == GTK_JUNCTION_RIGHT)
+ {
+ gint xi;
+
+ xi = x;
+
+ while (xi < x + width)
+ {
+ gdk_cairo_set_source_rgba (cr, &lighter);
+ add_path_line (cr, x, y, x, y + height);
+ cairo_stroke (cr);
+ xi++;
+
+ gdk_cairo_set_source_rgba (cr, &darker);
+ add_path_line (cr, xi, y, xi, y + height);
+ cairo_stroke (cr);
+ xi += 2;
+ }
+ }
+ else if (sides == GTK_JUNCTION_TOP ||
+ sides == GTK_JUNCTION_BOTTOM)
+ {
+ gint yi;
+
+ yi = y;
+
+ while (yi < y + height)
+ {
+ gdk_cairo_set_source_rgba (cr, &lighter);
+ add_path_line (cr, x, yi, x + width, yi);
+ cairo_stroke (cr);
+ yi++;
+
+ gdk_cairo_set_source_rgba (cr, &darker);
+ add_path_line (cr, x, yi, x + width, yi);
+ cairo_stroke (cr);
+ yi+= 2;
+ }
+ }
+ else if (sides == GTK_JUNCTION_CORNER_TOPLEFT)
+ {
+ gint xi, yi;
+
+ xi = x + width;
+ yi = y + height;
+
+ while (xi > x + 3)
+ {
+ gdk_cairo_set_source_rgba (cr, &darker);
+ add_path_line (cr, xi, y, x, yi);
+ cairo_stroke (cr);
+
+ --xi;
+ --yi;
+
+ add_path_line (cr, xi, y, x, yi);
+ cairo_stroke (cr);
+
+ --xi;
+ --yi;
+
+ gdk_cairo_set_source_rgba (cr, &lighter);
+ add_path_line (cr, xi, y, x, yi);
+ cairo_stroke (cr);
+
+ xi -= 3;
+ yi -= 3;
+ }
+ }
+ else if (sides == GTK_JUNCTION_CORNER_TOPRIGHT)
+ {
+ gint xi, yi;
+
+ xi = x;
+ yi = y + height;
+
+ while (xi < (x + width - 3))
+ {
+ gdk_cairo_set_source_rgba (cr, &lighter);
+ add_path_line (cr, xi, y, x + width, yi);
+ cairo_stroke (cr);
+
+ ++xi;
+ --yi;
+
+ gdk_cairo_set_source_rgba (cr, &darker);
+ add_path_line (cr, xi, y, x + width, yi);
+ cairo_stroke (cr);
+
+ ++xi;
+ --yi;
+
+ add_path_line (cr, xi, y, x + width, yi);
+ cairo_stroke (cr);
+
+ xi += 3;
+ yi -= 3;
+ }
+ }
+ else if (sides == GTK_JUNCTION_CORNER_BOTTOMLEFT)
+ {
+ gint xi, yi;
+
+ xi = x + width;
+ yi = y;
+
+ while (xi > x + 3)
+ {
+ gdk_cairo_set_source_rgba (cr, &darker);
+ add_path_line (cr, x, yi, xi, y + height);
+ cairo_stroke (cr);
+
+ --xi;
+ ++yi;
+
+ add_path_line (cr, x, yi, xi, y + height);
+ cairo_stroke (cr);
+
+ --xi;
+ ++yi;
+
+ gdk_cairo_set_source_rgba (cr, &lighter);
+ add_path_line (cr, x, yi, xi, y + height);
+ cairo_stroke (cr);
+
+ xi -= 3;
+ yi += 3;
+ }
+ }
+ else if (sides == GTK_JUNCTION_CORNER_BOTTOMRIGHT)
+ {
+ gint xi, yi;
+
+ xi = x;
+ yi = y;
+
+ while (xi < (x + width - 3))
+ {
+ gdk_cairo_set_source_rgba (cr, &lighter);
+ add_path_line (cr, xi, y + height, x + width, yi);
+ cairo_stroke (cr);
+
+ ++xi;
+ ++yi;
+
+ gdk_cairo_set_source_rgba (cr, &darker);
+ add_path_line (cr, xi, y + height, x + width, yi);
+ cairo_stroke (cr);
+
+ ++xi;
+ ++yi;
+
+ add_path_line (cr, xi, y + height, x + width, yi);
+ cairo_stroke (cr);
+
+ xi += 3;
+ yi += 3;
+ }
+ }
+
+ cairo_restore (cr);
+ }
+ else if (gtk_theming_engine_has_class (engine, "paned"))
+ {
+ if (width > height)
+ for (xx = x + width / 2 - 15; xx <= x + width / 2 + 15; xx += 5)
+ render_dot (cr, &lighter, &darker, xx, y + height / 2 - 1, 3);
+ else
+ for (yy = y + height / 2 - 15; yy <= y + height / 2 + 15; yy += 5)
+ render_dot (cr, &lighter, &darker, x + width / 2 - 1, yy, 3);
+ }
+ else
+ {
+ for (yy = y; yy < y + height; yy += 3)
+ for (xx = x; xx < x + width; xx += 6)
+ {
+ render_dot (cr, &lighter, &darker, xx, yy, 2);
+ render_dot (cr, &lighter, &darker, xx + 3, yy + 1, 2);
+ }
+ }
+
+ cairo_restore (cr);
+
+ gdk_rgba_free (bg_color);
+}
+
+static void
+gtk_theming_engine_render_activity (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_SPINNER))
+ {
+ GtkStateFlags state;
+ guint num_steps, step;
+ GdkRGBA *color;
+ gdouble dx, dy;
+ gdouble progress;
+ gdouble radius;
+ gdouble half;
+ gint i;
+
+ num_steps = 0;
+
+ gtk_theming_engine_get_style (engine,
+ "num-steps", &num_steps,
+ NULL);
+
+ state = gtk_theming_engine_get_state (engine);
+ gtk_theming_engine_get (engine, state,
+ "color", &color,
+ NULL);
+ if (num_steps == 0)
+ num_steps = 12;
+
+ if (gtk_theming_engine_state_is_running (engine, GTK_STATE_ACTIVE, &progress))
+ step = (guint) (progress * num_steps);
+ else
+ step = 0;
+
+ cairo_save (cr);
+
+ cairo_translate (cr, x, y);
+
+ /* draw clip region */
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+ dx = width / 2;
+ dy = height / 2;
+ radius = MIN (width / 2, height / 2);
+ half = num_steps / 2;
+
+ for (i = 0; i < num_steps; i++)
+ {
+ gint inset = 0.7 * radius;
+
+ /* transparency is a function of time and intial value */
+ gdouble t = (gdouble) ((i + num_steps - step)
+ % num_steps) / num_steps;
+
+ cairo_save (cr);
+
+ cairo_set_source_rgba (cr,
+ color->red,
+ color->green,
+ color->blue,
+ color->alpha * t);
+
+ cairo_set_line_width (cr, 2.0);
+ cairo_move_to (cr,
+ dx + (radius - inset) * cos (i * G_PI / half),
+ dy + (radius - inset) * sin (i * G_PI / half));
+ cairo_line_to (cr,
+ dx + radius * cos (i * G_PI / half),
+ dy + radius * sin (i * G_PI / half));
+ cairo_stroke (cr);
+
+ cairo_restore (cr);
+ }
+
+ cairo_restore (cr);
+
+ gdk_rgba_free (color);
+ }
+ else
+ {
+ gtk_theming_engine_render_background (engine, cr, x, y, width, height);
+ gtk_theming_engine_render_frame (engine, cr, x, y, width, height);
+ }
+}
+
+static GdkPixbuf *
+scale_or_ref (GdkPixbuf *src,
+ gint width,
+ gint height)
+{
+ if (width == gdk_pixbuf_get_width (src) &&
+ height == gdk_pixbuf_get_height (src))
+ return g_object_ref (src);
+ else
+ return gdk_pixbuf_scale_simple (src,
+ width, height,
+ GDK_INTERP_BILINEAR);
+}
+
+static gboolean
+lookup_icon_size (GtkThemingEngine *engine,
+ GtkIconSize size,
+ gint *width,
+ gint *height)
+{
+ GdkScreen *screen;
+ GtkSettings *settings;
+
+ screen = gtk_theming_engine_get_screen (engine);
+ settings = gtk_settings_get_for_screen (screen);
+
+ return gtk_icon_size_lookup_for_settings (settings, size, width, height);
+}
+
+static GdkPixbuf *
+gtk_theming_engine_render_icon_pixbuf (GtkThemingEngine *engine,
+ const GtkIconSource *source,
+ GtkIconSize size)
+{
+ GdkPixbuf *scaled;
+ GdkPixbuf *stated;
+ GdkPixbuf *base_pixbuf;
+ GtkStateFlags state;
+ gint width = 1;
+ gint height = 1;
+
+ base_pixbuf = gtk_icon_source_get_pixbuf (source);
+ state = gtk_theming_engine_get_state (engine);
+
+ g_return_val_if_fail (base_pixbuf != NULL, NULL);
+
+ if (size != (GtkIconSize) -1 &&
+ !lookup_icon_size (engine, size, &width, &height))
+ {
+ g_warning (G_STRLOC ": invalid icon size '%d'", size);
+ return NULL;
+ }
+
+ /* If the size was wildcarded, and we're allowed to scale, then scale; otherwise,
+ * leave it alone.
+ */
+ if (size != (GtkIconSize) -1 &&
+ gtk_icon_source_get_size_wildcarded (source))
+ scaled = scale_or_ref (base_pixbuf, width, height);
+ else
+ scaled = g_object_ref (base_pixbuf);
+
+ /* If the state was wildcarded, then generate a state. */
+ if (gtk_icon_source_get_state_wildcarded (source))
+ {
+ if (state & GTK_STATE_FLAG_INSENSITIVE)
+ {
+ stated = gdk_pixbuf_copy (scaled);
+ gdk_pixbuf_saturate_and_pixelate (scaled, stated,
+ 0.8, TRUE);
+ g_object_unref (scaled);
+ }
+ else if (state & GTK_STATE_FLAG_PRELIGHT)
+ {
+ stated = gdk_pixbuf_copy (scaled);
+ gdk_pixbuf_saturate_and_pixelate (scaled, stated,
+ 1.2, FALSE);
+ g_object_unref (scaled);
+ }
+ else
+ stated = scaled;
+ }
+ else
+ stated = scaled;
+
+ return stated;
+}
diff --git a/gtk/gtkthemingengine.h b/gtk/gtkthemingengine.h
new file mode 100644
index 0000000000..64b39216e2
--- /dev/null
+++ b/gtk/gtkthemingengine.h
@@ -0,0 +1,251 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_THEMING_ENGINE_H__
+#define __GTK_THEMING_ENGINE_H__
+
+#include <glib-object.h>
+#include <cairo.h>
+
+#include <gtk/gtkstylecontext.h>
+#include <gtk/gtkwidgetpath.h>
+#include <gtk/gtkenums.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_THEMING_ENGINE (gtk_theming_engine_get_type ())
+#define GTK_THEMING_ENGINE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_THEMING_ENGINE, GtkThemingEngine))
+#define GTK_THEMING_ENGINE_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GTK_TYPE_THEMING_ENGINE, GtkThemingEngineClass))
+#define GTK_IS_THEMING_ENGINE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_THEMING_ENGINE))
+#define GTK_IS_THEMING_ENGINE_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GTK_TYPE_THEMING_ENGINE))
+#define GTK_THEMING_ENGINE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_THEMING_ENGINE, GtkThemingEngineClass))
+
+typedef struct _GtkThemingEngine GtkThemingEngine;
+typedef struct _GtkThemingEngineClass GtkThemingEngineClass;
+
+struct _GtkThemingEngine
+{
+ GObject parent_object;
+ gpointer priv;
+};
+
+/**
+ * GtkThemingEngineClass
+ * @parent_class: The parent class.
+ * @render_line: Renders a line between two points.
+ * @render_background: Renders the background area of a widget region.
+ * @render_frame: Renders the frame around a widget area.
+ * @render_frame_gap: Renders the frame around a widget area with a gap in it.
+ * @render_extension: Renders a extension to a box, usually a notebook tab.
+ * @render_check: Renders a checkmark, as in #GtkCheckButton.
+ * @render_option: Renders an option, as in #GtkRadioButton.
+ * @render_arrow: Renders an arrow pointing to a certain direction.
+ * @render_expander: Renders an element what will expose/expand part of
+ * the UI, as in #GtkExpander.
+ * @render_focus: Renders the focus indicator.
+ * @render_layout: Renders a #PangoLayout
+ * @render_slider: Renders a slider control, as in #GtkScale.
+ * @render_handle: Renders a handle to drag UI elements, as in #GtkPaned.
+ * @render_activity: Renders an area displaying activity, such as in #GtkSpinner,
+ * or #GtkProgressBar.
+ * @render_icon_pixbuf: Renders an icon as a #GdkPixbuf.
+ *
+ * Base class for theming engines.
+ */
+struct _GtkThemingEngineClass
+{
+ GObjectClass parent_class;
+
+ void (* render_line) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x0,
+ gdouble y0,
+ gdouble x1,
+ gdouble y1);
+ void (* render_background) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+ void (* render_frame) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+ void (* render_frame_gap) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkPositionType gap_side,
+ gdouble xy0_gap,
+ gdouble xy1_gap);
+ void (* render_extension) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkPositionType gap_side);
+ void (* render_check) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+ void (* render_option) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+ void (* render_arrow) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble angle,
+ gdouble x,
+ gdouble y,
+ gdouble size);
+ void (* render_expander) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+ void (* render_focus) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+ void (* render_layout) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ PangoLayout *layout);
+ void (* render_slider) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ GtkOrientation orientation);
+ void (* render_handle) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+ void (* render_activity) (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height);
+
+ GdkPixbuf * (* render_icon_pixbuf) (GtkThemingEngine *engine,
+ const GtkIconSource *source,
+ GtkIconSize size);
+};
+
+GType gtk_theming_engine_get_type (void) G_GNUC_CONST;
+
+void _gtk_theming_engine_set_context (GtkThemingEngine *engine,
+ GtkStyleContext *context);
+
+void gtk_theming_engine_register_property (const gchar *name_space,
+ GtkStylePropertyParser parse_func,
+ GParamSpec *pspec);
+
+void gtk_theming_engine_get_property (GtkThemingEngine *engine,
+ const gchar *property,
+ GtkStateFlags state,
+ GValue *value);
+void gtk_theming_engine_get_valist (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ va_list args);
+void gtk_theming_engine_get (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ ...) G_GNUC_NULL_TERMINATED;
+
+void gtk_theming_engine_get_style_property (GtkThemingEngine *engine,
+ const gchar *property_name,
+ GValue *value);
+void gtk_theming_engine_get_style_valist (GtkThemingEngine *engine,
+ va_list args);
+void gtk_theming_engine_get_style (GtkThemingEngine *engine,
+ ...);
+
+gboolean gtk_theming_engine_lookup_color (GtkThemingEngine *engine,
+ const gchar *color_name,
+ GdkRGBA *color);
+
+G_CONST_RETURN GtkWidgetPath * gtk_theming_engine_get_path (GtkThemingEngine *engine);
+
+gboolean gtk_theming_engine_has_class (GtkThemingEngine *engine,
+ const gchar *style_class);
+gboolean gtk_theming_engine_has_region (GtkThemingEngine *engine,
+ const gchar *style_region,
+ GtkRegionFlags *flags);
+
+GtkStateFlags gtk_theming_engine_get_state (GtkThemingEngine *engine);
+gboolean gtk_theming_engine_state_is_running (GtkThemingEngine *engine,
+ GtkStateType state,
+ gdouble *progress);
+
+GtkTextDirection gtk_theming_engine_get_direction (GtkThemingEngine *engine);
+
+GtkJunctionSides gtk_theming_engine_get_junction_sides (GtkThemingEngine *engine);
+
+/* Helper functions */
+void gtk_theming_engine_get_color (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GdkRGBA *color);
+void gtk_theming_engine_get_background_color (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GdkRGBA *color);
+void gtk_theming_engine_get_border_color (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GdkRGBA *color);
+
+void gtk_theming_engine_get_border (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GtkBorder *border);
+void gtk_theming_engine_get_padding (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GtkBorder *padding);
+void gtk_theming_engine_get_margin (GtkThemingEngine *engine,
+ GtkStateFlags state,
+ GtkBorder *margin);
+
+
+GtkThemingEngine * gtk_theming_engine_load (const gchar *name);
+
+GdkScreen * gtk_theming_engine_get_screen (GtkThemingEngine *engine);
+
+
+G_END_DECLS
+
+#endif /* __GTK_THEMING_ENGINE_H__ */
diff --git a/gtk/gtktimeline.c b/gtk/gtktimeline.c
new file mode 100644
index 0000000000..348011c768
--- /dev/null
+++ b/gtk/gtktimeline.c
@@ -0,0 +1,735 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
+ *
+ * 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; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtktimeline.h>
+#include <gtk/gtktypebuiltins.h>
+#include <gtk/gtksettings.h>
+#include <math.h>
+
+#define MSECS_PER_SEC 1000
+#define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes)
+#define DEFAULT_FPS 30
+
+typedef struct GtkTimelinePriv GtkTimelinePriv;
+
+struct GtkTimelinePriv
+{
+ guint duration;
+ guint fps;
+ guint source_id;
+
+ GTimer *timer;
+
+ gdouble progress;
+ gdouble last_progress;
+
+ GdkScreen *screen;
+
+ GtkTimelineProgressType progress_type;
+
+ guint animations_enabled : 1;
+ guint loop : 1;
+ guint direction : 1;
+};
+
+enum {
+ PROP_0,
+ PROP_FPS,
+ PROP_DURATION,
+ PROP_LOOP,
+ PROP_DIRECTION,
+ PROP_SCREEN
+};
+
+enum {
+ STARTED,
+ PAUSED,
+ FINISHED,
+ FRAME,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+
+static void gtk_timeline_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_timeline_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gtk_timeline_finalize (GObject *object);
+
+
+G_DEFINE_TYPE (GtkTimeline, gtk_timeline, G_TYPE_OBJECT)
+
+
+static void
+gtk_timeline_class_init (GtkTimelineClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = gtk_timeline_set_property;
+ object_class->get_property = gtk_timeline_get_property;
+ object_class->finalize = gtk_timeline_finalize;
+
+ g_object_class_install_property (object_class,
+ PROP_FPS,
+ g_param_spec_uint ("fps",
+ "FPS",
+ "Frames per second for the timeline",
+ 1, G_MAXUINT,
+ DEFAULT_FPS,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_DURATION,
+ g_param_spec_uint ("duration",
+ "Animation Duration",
+ "Animation Duration",
+ 0, G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOOP,
+ g_param_spec_boolean ("loop",
+ "Loop",
+ "Whether the timeline loops or not",
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_SCREEN,
+ g_param_spec_object ("screen",
+ "Screen",
+ "Screen to get the settings from",
+ GDK_TYPE_SCREEN,
+ G_PARAM_READWRITE));
+
+ signals[STARTED] =
+ g_signal_new ("started",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkTimelineClass, started),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[PAUSED] =
+ g_signal_new ("paused",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkTimelineClass, paused),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[FINISHED] =
+ g_signal_new ("finished",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkTimelineClass, finished),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[FRAME] =
+ g_signal_new ("frame",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GtkTimelineClass, frame),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__DOUBLE,
+ G_TYPE_NONE, 1,
+ G_TYPE_DOUBLE);
+
+ g_type_class_add_private (klass, sizeof (GtkTimelinePriv));
+}
+
+static void
+gtk_timeline_init (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ priv = timeline->priv = G_TYPE_INSTANCE_GET_PRIVATE (timeline,
+ GTK_TYPE_TIMELINE,
+ GtkTimelinePriv);
+
+ priv->fps = DEFAULT_FPS;
+ priv->duration = 0.0;
+ priv->direction = GTK_TIMELINE_DIRECTION_FORWARD;
+ priv->screen = gdk_screen_get_default ();
+
+ priv->last_progress = 0;
+}
+
+static void
+gtk_timeline_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GtkTimeline *timeline;
+ GtkTimelinePriv *priv;
+
+ timeline = GTK_TIMELINE (object);
+ priv = timeline->priv;
+
+ switch (prop_id)
+ {
+ case PROP_FPS:
+ gtk_timeline_set_fps (timeline, g_value_get_uint (value));
+ break;
+ case PROP_DURATION:
+ gtk_timeline_set_duration (timeline, g_value_get_uint (value));
+ break;
+ case PROP_LOOP:
+ gtk_timeline_set_loop (timeline, g_value_get_boolean (value));
+ break;
+ case PROP_DIRECTION:
+ gtk_timeline_set_direction (timeline, g_value_get_enum (value));
+ break;
+ case PROP_SCREEN:
+ gtk_timeline_set_screen (timeline,
+ GDK_SCREEN (g_value_get_object (value)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtk_timeline_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GtkTimeline *timeline;
+ GtkTimelinePriv *priv;
+
+ timeline = GTK_TIMELINE (object);
+ priv = timeline->priv;
+
+ switch (prop_id)
+ {
+ case PROP_FPS:
+ g_value_set_uint (value, priv->fps);
+ break;
+ case PROP_DURATION:
+ g_value_set_uint (value, priv->duration);
+ break;
+ case PROP_LOOP:
+ g_value_set_boolean (value, priv->loop);
+ break;
+ case PROP_DIRECTION:
+ g_value_set_enum (value, priv->direction);
+ break;
+ case PROP_SCREEN:
+ g_value_set_object (value, priv->screen);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gtk_timeline_finalize (GObject *object)
+{
+ GtkTimelinePriv *priv;
+ GtkTimeline *timeline;
+
+ timeline = (GtkTimeline *) object;
+ priv = timeline->priv;
+
+ if (priv->source_id)
+ {
+ g_source_remove (priv->source_id);
+ priv->source_id = 0;
+ }
+
+ if (priv->timer)
+ g_timer_destroy (priv->timer);
+
+ G_OBJECT_CLASS (gtk_timeline_parent_class)->finalize (object);
+}
+
+gdouble
+calculate_progress (gdouble linear_progress,
+ GtkTimelineProgressType progress_type)
+{
+ gdouble progress;
+
+ progress = linear_progress;
+
+ switch (progress_type)
+ {
+ case GTK_TIMELINE_PROGRESS_LINEAR:
+ break;
+ case GTK_TIMELINE_PROGRESS_EASE_IN_OUT:
+ progress *= 2;
+
+ if (progress < 1)
+ progress = pow (progress, 3) / 2;
+ else
+ progress = (pow (progress - 2, 3) + 2) / 2;
+
+ break;
+ case GTK_TIMELINE_PROGRESS_EASE:
+ progress = (sinf ((progress - 0.5) * G_PI) + 1) / 2;
+ break;
+ case GTK_TIMELINE_PROGRESS_EASE_IN:
+ progress = pow (progress, 3);
+ break;
+ case GTK_TIMELINE_PROGRESS_EASE_OUT:
+ progress = pow (progress - 1, 3) + 1;
+ break;
+ default:
+ g_warning ("Timeline progress type not implemented");
+ }
+
+ return progress;
+}
+
+static gboolean
+gtk_timeline_run_frame (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+ gdouble delta_progress, progress;
+ guint elapsed_time;
+
+ priv = timeline->priv;
+
+ elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000);
+ g_timer_start (priv->timer);
+
+ if (priv->animations_enabled)
+ {
+ delta_progress = (gdouble) elapsed_time / priv->duration;
+ progress = priv->last_progress;
+
+ if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD)
+ progress -= delta_progress;
+ else
+ progress += delta_progress;
+
+ priv->last_progress = progress;
+
+ progress = CLAMP (progress, 0., 1.);
+ }
+ else
+ progress = (priv->direction == GTK_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0;
+
+ priv->progress = progress;
+ g_signal_emit (timeline, signals [FRAME], 0,
+ calculate_progress (progress, priv->progress_type));
+
+ if ((priv->direction == GTK_TIMELINE_DIRECTION_FORWARD && progress == 1.0) ||
+ (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD && progress == 0.0))
+ {
+ if (!priv->loop)
+ {
+ if (priv->source_id)
+ {
+ g_source_remove (priv->source_id);
+ priv->source_id = 0;
+ }
+ g_timer_stop (priv->timer);
+ g_signal_emit (timeline, signals [FINISHED], 0);
+ return FALSE;
+ }
+ else
+ gtk_timeline_rewind (timeline);
+ }
+
+ return TRUE;
+}
+
+/**
+ * gtk_timeline_new:
+ * @duration: duration in milliseconds for the timeline
+ *
+ * Creates a new #GtkTimeline with the specified number of frames.
+ *
+ * Return Value: the newly created #GtkTimeline
+ **/
+GtkTimeline *
+gtk_timeline_new (guint duration)
+{
+ return g_object_new (GTK_TYPE_TIMELINE,
+ "duration", duration,
+ NULL);
+}
+
+GtkTimeline *
+gtk_timeline_new_for_screen (guint duration,
+ GdkScreen *screen)
+{
+ return g_object_new (GTK_TYPE_TIMELINE,
+ "duration", duration,
+ "screen", screen,
+ NULL);
+}
+
+/**
+ * gtk_timeline_start:
+ * @timeline: A #GtkTimeline
+ *
+ * Runs the timeline from the current frame.
+ **/
+void
+gtk_timeline_start (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+ GtkSettings *settings;
+ gboolean enable_animations = FALSE;
+
+ g_return_if_fail (GTK_IS_TIMELINE (timeline));
+
+ priv = timeline->priv;
+
+ if (!priv->source_id)
+ {
+ if (priv->timer)
+ g_timer_continue (priv->timer);
+ else
+ priv->timer = g_timer_new ();
+
+ /* sanity check */
+ g_assert (priv->fps > 0);
+
+ if (priv->screen)
+ {
+ settings = gtk_settings_get_for_screen (priv->screen);
+ g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL);
+ }
+
+ priv->animations_enabled = (enable_animations == TRUE);
+
+ g_signal_emit (timeline, signals [STARTED], 0);
+
+ if (enable_animations)
+ priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
+ (GSourceFunc) gtk_timeline_run_frame,
+ timeline);
+ else
+ priv->source_id = gdk_threads_add_idle ((GSourceFunc) gtk_timeline_run_frame,
+ timeline);
+ }
+}
+
+/**
+ * gtk_timeline_pause:
+ * @timeline: A #GtkTimeline
+ *
+ * Pauses the timeline.
+ **/
+void
+gtk_timeline_pause (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_if_fail (GTK_IS_TIMELINE (timeline));
+
+ priv = timeline->priv;
+
+ if (priv->source_id)
+ {
+ g_timer_stop (priv->timer);
+ g_source_remove (priv->source_id);
+ priv->source_id = 0;
+ g_signal_emit (timeline, signals [PAUSED], 0);
+ }
+}
+
+/**
+ * gtk_timeline_rewind:
+ * @timeline: A #GtkTimeline
+ *
+ * Rewinds the timeline.
+ **/
+void
+gtk_timeline_rewind (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_if_fail (GTK_IS_TIMELINE (timeline));
+
+ priv = timeline->priv;
+
+ if (gtk_timeline_get_direction(timeline) != GTK_TIMELINE_DIRECTION_FORWARD)
+ priv->progress = priv->last_progress = 1.;
+ else
+ priv->progress = priv->last_progress = 0.;
+
+ /* reset timer */
+ if (priv->timer)
+ {
+ g_timer_start (priv->timer);
+
+ if (!priv->source_id)
+ g_timer_stop (priv->timer);
+ }
+}
+
+/**
+ * gtk_timeline_is_running:
+ * @timeline: A #GtkTimeline
+ *
+ * Returns whether the timeline is running or not.
+ *
+ * Return Value: %TRUE if the timeline is running
+ **/
+gboolean
+gtk_timeline_is_running (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
+
+ priv = timeline->priv;
+
+ return (priv->source_id != 0);
+}
+
+/**
+ * gtk_timeline_get_fps:
+ * @timeline: A #GtkTimeline
+ *
+ * Returns the number of frames per second.
+ *
+ * Return Value: frames per second
+ **/
+guint
+gtk_timeline_get_fps (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 1);
+
+ priv = timeline->priv;
+ return priv->fps;
+}
+
+/**
+ * gtk_timeline_set_fps:
+ * @timeline: A #GtkTimeline
+ * @fps: frames per second
+ *
+ * Sets the number of frames per second that
+ * the timeline will play.
+ **/
+void
+gtk_timeline_set_fps (GtkTimeline *timeline,
+ guint fps)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_if_fail (GTK_IS_TIMELINE (timeline));
+ g_return_if_fail (fps > 0);
+
+ priv = timeline->priv;
+
+ priv->fps = fps;
+
+ if (gtk_timeline_is_running (timeline))
+ {
+ g_source_remove (priv->source_id);
+ priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (priv->fps),
+ (GSourceFunc) gtk_timeline_run_frame,
+ timeline);
+ }
+
+ g_object_notify (G_OBJECT (timeline), "fps");
+}
+
+/**
+ * gtk_timeline_get_loop:
+ * @timeline: A #GtkTimeline
+ *
+ * Returns whether the timeline loops to the
+ * beginning when it has reached the end.
+ *
+ * Return Value: %TRUE if the timeline loops
+ **/
+gboolean
+gtk_timeline_get_loop (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE);
+
+ priv = timeline->priv;
+ return priv->loop;
+}
+
+/**
+ * gtk_timeline_set_loop:
+ * @timeline: A #GtkTimeline
+ * @loop: %TRUE to make the timeline loop
+ *
+ * Sets whether the timeline loops to the beginning
+ * when it has reached the end.
+ **/
+void
+gtk_timeline_set_loop (GtkTimeline *timeline,
+ gboolean loop)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_if_fail (GTK_IS_TIMELINE (timeline));
+
+ priv = timeline->priv;
+
+ if (loop != priv->loop)
+ {
+ priv->loop = loop;
+ g_object_notify (G_OBJECT (timeline), "loop");
+ }
+}
+
+void
+gtk_timeline_set_duration (GtkTimeline *timeline,
+ guint duration)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_if_fail (GTK_IS_TIMELINE (timeline));
+
+ priv = timeline->priv;
+
+ if (duration != priv->duration)
+ {
+ priv->duration = duration;
+ g_object_notify (G_OBJECT (timeline), "duration");
+ }
+}
+
+guint
+gtk_timeline_get_duration (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0);
+
+ priv = timeline->priv;
+
+ return priv->duration;
+}
+
+/**
+ * gtk_timeline_set_direction:
+ * @timeline: A #GtkTimeline
+ * @direction: direction
+ *
+ * Sets the direction of the timeline.
+ **/
+void
+gtk_timeline_set_direction (GtkTimeline *timeline,
+ GtkTimelineDirection direction)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_if_fail (GTK_IS_TIMELINE (timeline));
+
+ priv = timeline->priv;
+ priv->direction = direction;
+}
+
+/**
+ * gtk_timeline_get_direction:
+ * @timeline: A #GtkTimeline
+ *
+ * Returns the direction of the timeline.
+ *
+ * Return Value: direction
+ **/
+GtkTimelineDirection
+gtk_timeline_get_direction (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD);
+
+ priv = timeline->priv;
+ return priv->direction;
+}
+
+void
+gtk_timeline_set_screen (GtkTimeline *timeline,
+ GdkScreen *screen)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_if_fail (GTK_IS_TIMELINE (timeline));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ priv = timeline->priv;
+
+ if (priv->screen)
+ g_object_unref (priv->screen);
+
+ priv->screen = g_object_ref (screen);
+
+ g_object_notify (G_OBJECT (timeline), "screen");
+}
+
+GdkScreen *
+gtk_timeline_get_screen (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
+
+ priv = timeline->priv;
+ return priv->screen;
+}
+
+gdouble
+gtk_timeline_get_progress (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.);
+
+ priv = timeline->priv;
+ return calculate_progress (priv->progress, priv->progress_type);
+}
+
+GtkTimelineProgressType
+gtk_timeline_get_progress_type (GtkTimeline *timeline)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR);
+
+ priv = timeline->priv;
+ return priv->progress_type;
+}
+
+void
+gtk_timeline_set_progress_type (GtkTimeline *timeline,
+ GtkTimelineProgressType progress_type)
+{
+ GtkTimelinePriv *priv;
+
+ g_return_if_fail (GTK_IS_TIMELINE (timeline));
+
+ priv = timeline->priv;
+ priv->progress_type = progress_type;
+}
diff --git a/gtk/gtktimeline.h b/gtk/gtktimeline.h
new file mode 100644
index 0000000000..d9a6ae5d2c
--- /dev/null
+++ b/gtk/gtktimeline.h
@@ -0,0 +1,117 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com>
+ *
+ * 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; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_TIMELINE_H__
+#define __GTK_TIMELINE_H__
+
+#include <glib-object.h>
+#include <gtk/gtkenums.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_TIMELINE (gtk_timeline_get_type ())
+#define GTK_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TIMELINE, GtkTimeline))
+#define GTK_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TIMELINE, GtkTimelineClass))
+#define GTK_IS_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TIMELINE))
+#define GTK_IS_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TIMELINE))
+#define GTK_TIMELINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TIMELINE, GtkTimelineClass))
+
+typedef struct GtkTimeline GtkTimeline;
+typedef struct GtkTimelineClass GtkTimelineClass;
+
+typedef enum {
+ GTK_TIMELINE_DIRECTION_FORWARD,
+ GTK_TIMELINE_DIRECTION_BACKWARD
+} GtkTimelineDirection;
+
+typedef enum {
+ GTK_TIMELINE_PROGRESS_LINEAR,
+ GTK_TIMELINE_PROGRESS_EASE,
+ GTK_TIMELINE_PROGRESS_EASE_IN,
+ GTK_TIMELINE_PROGRESS_EASE_OUT,
+ GTK_TIMELINE_PROGRESS_EASE_IN_OUT
+} GtkTimelineProgressType;
+
+struct GtkTimeline
+{
+ GObject parent_instance;
+ gpointer priv;
+};
+
+struct GtkTimelineClass
+{
+ GObjectClass parent_class;
+
+ void (* started) (GtkTimeline *timeline);
+ void (* finished) (GtkTimeline *timeline);
+ void (* paused) (GtkTimeline *timeline);
+
+ void (* frame) (GtkTimeline *timeline,
+ gdouble progress);
+
+ void (* __gtk_reserved1) (void);
+ void (* __gtk_reserved2) (void);
+ void (* __gtk_reserved3) (void);
+ void (* __gtk_reserved4) (void);
+};
+
+
+GType gtk_timeline_get_type (void) G_GNUC_CONST;
+
+GtkTimeline *gtk_timeline_new (guint duration);
+GtkTimeline *gtk_timeline_new_for_screen (guint duration,
+ GdkScreen *screen);
+
+void gtk_timeline_start (GtkTimeline *timeline);
+void gtk_timeline_pause (GtkTimeline *timeline);
+void gtk_timeline_rewind (GtkTimeline *timeline);
+
+gboolean gtk_timeline_is_running (GtkTimeline *timeline);
+
+guint gtk_timeline_get_fps (GtkTimeline *timeline);
+void gtk_timeline_set_fps (GtkTimeline *timeline,
+ guint fps);
+
+gboolean gtk_timeline_get_loop (GtkTimeline *timeline);
+void gtk_timeline_set_loop (GtkTimeline *timeline,
+ gboolean loop);
+
+guint gtk_timeline_get_duration (GtkTimeline *timeline);
+void gtk_timeline_set_duration (GtkTimeline *timeline,
+ guint duration);
+
+GdkScreen *gtk_timeline_get_screen (GtkTimeline *timeline);
+void gtk_timeline_set_screen (GtkTimeline *timeline,
+ GdkScreen *screen);
+
+GtkTimelineDirection gtk_timeline_get_direction (GtkTimeline *timeline);
+void gtk_timeline_set_direction (GtkTimeline *timeline,
+ GtkTimelineDirection direction);
+
+gdouble gtk_timeline_get_progress (GtkTimeline *timeline);
+
+GtkTimelineProgressType gtk_timeline_get_progress_type (GtkTimeline *timeline);
+void gtk_timeline_set_progress_type (GtkTimeline *timeline,
+ GtkTimelineProgressType progress_type);
+
+
+G_END_DECLS
+
+#endif /* __GTK_TIMELINE_H__ */
diff --git a/gtk/gtktogglebutton.c b/gtk/gtktogglebutton.c
index a4979b3fa4..7b8ae49f62 100644
--- a/gtk/gtktogglebutton.c
+++ b/gtk/gtktogglebutton.c
@@ -329,6 +329,8 @@ gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
if (priv->draw_indicator != draw_indicator)
{
+ GtkStyleContext *context;
+
priv->draw_indicator = draw_indicator;
GTK_BUTTON (toggle_button)->priv->depress_on_activate = !draw_indicator;
@@ -336,6 +338,16 @@ gtk_toggle_button_set_mode (GtkToggleButton *toggle_button,
gtk_widget_queue_resize (GTK_WIDGET (toggle_button));
g_object_notify (G_OBJECT (toggle_button), "draw-indicator");
+
+ /* Make toggle buttons conditionally have the "button"
+ * class depending on draw_indicator.
+ */
+ context = gtk_widget_get_style_context (GTK_WIDGET (toggle_button));
+
+ if (draw_indicator)
+ gtk_style_context_remove_class (context, GTK_STYLE_CLASS_BUTTON);
+ else
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
}
}
@@ -551,13 +563,16 @@ gtk_toggle_button_update_state (GtkButton *button)
GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
GtkToggleButtonPrivate *priv = toggle_button->priv;
gboolean depressed, touchscreen;
- GtkStateType new_state;
+ GtkStateFlags new_state = 0;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (button)),
"gtk-touchscreen-mode", &touchscreen,
NULL);
if (priv->inconsistent)
+ new_state |= GTK_STATE_FLAG_INCONSISTENT;
+
+ if (priv->inconsistent)
depressed = FALSE;
else if (button->priv->in_button && button->priv->button_down)
depressed = TRUE;
@@ -565,10 +580,11 @@ gtk_toggle_button_update_state (GtkButton *button)
depressed = priv->active;
if (!touchscreen && button->priv->in_button && (!button->priv->button_down || priv->draw_indicator))
- new_state = GTK_STATE_PRELIGHT;
- else
- new_state = depressed ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL;
+ new_state |= GTK_STATE_FLAG_PRELIGHT;
+
+ if (depressed)
+ new_state |= GTK_STATE_FLAG_ACTIVE;
- _gtk_button_set_depressed (button, depressed);
- gtk_widget_set_state (GTK_WIDGET (toggle_button), new_state);
+ _gtk_button_set_depressed (button, depressed);
+ gtk_widget_set_state_flags (GTK_WIDGET (toggle_button), new_state, TRUE);
}
diff --git a/gtk/gtktoolitemgroup.c b/gtk/gtktoolitemgroup.c
index 7983667ffc..abcc64014b 100644
--- a/gtk/gtktoolitemgroup.c
+++ b/gtk/gtktoolitemgroup.c
@@ -1752,7 +1752,7 @@ gtk_tool_item_group_set_label_widget (GtkToolItemGroup *group,
if (priv->label_widget)
{
- gtk_widget_set_state (priv->label_widget, GTK_STATE_NORMAL);
+ gtk_widget_set_state_flags (priv->label_widget, 0, TRUE);
gtk_container_remove (GTK_CONTAINER (alignment), priv->label_widget);
}
diff --git a/gtk/gtktrayicon-x11.c b/gtk/gtktrayicon-x11.c
index 0d9b3afd17..150b634823 100644
--- a/gtk/gtktrayicon-x11.c
+++ b/gtk/gtktrayicon-x11.c
@@ -879,9 +879,9 @@ gtk_tray_icon_realize (GtkWidget *widget)
GTK_NOTE (PLUGSOCKET,
g_print ("GtkStatusIcon %p: realized, window: %lx, socket window: %lx\n",
widget,
- (gulong) GDK_WINDOW_XWINDOW (window),
+ (gulong) GDK_WINDOW_XID (window),
gtk_plug_get_socket_window (GTK_PLUG (icon)) ?
- (gulong) GDK_WINDOW_XWINDOW (gtk_plug_get_socket_window (GTK_PLUG (icon))) : 0UL));
+ (gulong) GDK_WINDOW_XID (gtk_plug_get_socket_window (GTK_PLUG (icon))) : 0UL));
if (icon->priv->manager_window != None)
gtk_tray_icon_send_dock_request (icon);
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index aca9989d16..93d5ee4505 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -58,6 +58,12 @@
#include "gtkbuildable.h"
#include "gtkbuilderprivate.h"
#include "gtksizerequest.h"
+#include "gtkstylecontext.h"
+#include "gtksymboliccolor.h"
+#include "gtkcssprovider.h"
+#include "gtkanimationdescription.h"
+#include "gtkmodifierstyle.h"
+#include "gtkversion.h"
#include "gtkdebug.h"
@@ -287,15 +293,7 @@ struct _GtkWidgetPrivate
* 5 widget states (defined in "gtkenums.h")
* so 3 bits.
*/
- guint state : 3;
-
- /* The saved state of the widget. When a widget's state
- * is changed to GTK_STATE_INSENSITIVE via
- * "gtk_widget_set_state" or "gtk_widget_set_sensitive"
- * the old state is kept around in this field. The state
- * will be restored once the widget gets sensitive again.
- */
- guint saved_state : 3;
+ guint state_flags : 6;
guint direction : 2;
@@ -354,6 +352,7 @@ struct _GtkWidgetPrivate
* the font to use for text.
*/
GtkStyle *style;
+ GtkStyleContext *context;
/* The widget's allocated size.
*/
@@ -371,6 +370,9 @@ struct _GtkWidgetPrivate
/* The widget's parent.
*/
GtkWidget *parent;
+
+ /* Widget's path for styling */
+ GtkWidgetPath *path;
};
enum {
@@ -382,6 +384,7 @@ enum {
REALIZE,
UNREALIZE,
SIZE_ALLOCATE,
+ STATE_FLAGS_CHANGED,
STATE_CHANGED,
PARENT_SET,
HIERARCHY_CHANGED,
@@ -422,7 +425,6 @@ enum {
PROXIMITY_IN_EVENT,
PROXIMITY_OUT_EVENT,
CLIENT_EVENT,
- NO_EXPOSE_EVENT,
VISIBILITY_NOTIFY_EVENT,
WINDOW_STATE_EVENT,
DAMAGE_EVENT,
@@ -443,6 +445,7 @@ enum {
COMPOSITED_CHANGED,
QUERY_TOOLTIP,
DRAG_FAILED,
+ STYLE_UPDATED,
LAST_SIGNAL
};
@@ -487,10 +490,16 @@ enum {
typedef struct _GtkStateData GtkStateData;
+enum {
+ STATE_CHANGE_REPLACE,
+ STATE_CHANGE_SET,
+ STATE_CHANGE_UNSET
+};
+
struct _GtkStateData
{
- GtkStateType state;
- guint state_restoration : 1;
+ guint flags : 6;
+ guint operation : 2;
guint parent_sensitive : 1;
guint use_forall : 1;
};
@@ -529,6 +538,7 @@ static gboolean gtk_widget_real_query_tooltip (GtkWidget *widget,
gint y,
gboolean keyboard_tip,
GtkTooltip *tooltip);
+static void gtk_widget_real_style_updated (GtkWidget *widget);
static gboolean gtk_widget_real_show_help (GtkWidget *widget,
GtkWidgetHelpType help_type);
@@ -671,6 +681,7 @@ static GQuark quark_tooltip_markup = 0;
static GQuark quark_has_tooltip = 0;
static GQuark quark_tooltip_window = 0;
static GQuark quark_visual = 0;
+static GQuark quark_modifier_style = 0;
GParamSpecPool *_gtk_widget_child_property_pool = NULL;
GObjectNotifyContext *_gtk_widget_child_property_notify_context = NULL;
@@ -784,6 +795,7 @@ gtk_widget_class_init (GtkWidgetClass *klass)
quark_has_tooltip = g_quark_from_static_string ("gtk-has-tooltip");
quark_tooltip_window = g_quark_from_static_string ("gtk-tooltip-window");
quark_visual = g_quark_from_static_string ("gtk-widget-visual");
+ quark_modifier_style = g_quark_from_static_string ("gtk-widget-modifier-style");
style_property_spec_pool = g_param_spec_pool_new (FALSE);
_gtk_widget_child_property_pool = g_param_spec_pool_new (TRUE);
@@ -859,14 +871,13 @@ gtk_widget_class_init (GtkWidgetClass *klass)
klass->can_activate_accel = gtk_widget_real_can_activate_accel;
klass->grab_broken_event = NULL;
klass->query_tooltip = gtk_widget_real_query_tooltip;
+ klass->style_updated = gtk_widget_real_style_updated;
klass->show_help = gtk_widget_real_show_help;
/* Accessibility support */
klass->get_accessible = gtk_widget_real_get_accessible;
- klass->no_expose_event = NULL;
-
klass->adjust_size_request = gtk_widget_real_adjust_size_request;
klass->adjust_size_allocation = gtk_widget_real_adjust_size_allocation;
@@ -1425,6 +1436,8 @@ gtk_widget_class_init (GtkWidgetClass *klass)
*
* The ::state-changed signal is emitted when the widget state changes.
* See gtk_widget_get_state().
+ *
+ * Deprecated: 3.0. Use #GtkWidget::state-flags-changed instead.
*/
widget_signals[STATE_CHANGED] =
g_signal_new (I_("state-changed"),
@@ -1437,6 +1450,26 @@ gtk_widget_class_init (GtkWidgetClass *klass)
GTK_TYPE_STATE_TYPE);
/**
+ * GtkWidget::state-flags-changed:
+ * @widget: the object which received the signal.
+ * @flags: The previous state flags.
+ *
+ * The ::state-flags-changed signal is emitted when the widget state
+ * changes, see gtk_widget_get_state_flags().
+ *
+ * Since: 3.0
+ */
+ widget_signals[STATE_FLAGS_CHANGED] =
+ g_signal_new (I_("state-flags-changed"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkWidgetClass, state_flags_changed),
+ NULL, NULL,
+ _gtk_marshal_VOID__FLAGS,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_STATE_FLAGS);
+
+ /**
* GtkWidget::parent-set:
* @widget: the object on which the signal is emitted
* @old_parent: (allow-none): the previous parent, or %NULL if the widget
@@ -1497,6 +1530,15 @@ gtk_widget_class_init (GtkWidgetClass *klass)
G_TYPE_NONE, 1,
GTK_TYPE_STYLE);
+ widget_signals[STYLE_UPDATED] =
+ g_signal_new (I_("style-updated"),
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkWidgetClass, style_updated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
/**
* GtkWidget::direction-changed:
* @widget: the object on which the signal is emitted
@@ -2704,30 +2746,6 @@ gtk_widget_class_init (GtkWidgetClass *klass)
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
- * GtkWidget::no-expose-event:
- * @widget: the object which received the signal
- * @event: (type Gdk.EventNoExpose): the #GdkEventNoExpose which triggered
- * this signal.
- *
- * The ::no-expose-event will be emitted when the @widget's window is
- * drawn as a copy of another #GdkDrawable which was completely unobscured.
- * If the source window was partially obscured #GdkEventExpose events will
- * be generated for those areas.
- *
- * Returns: %TRUE to stop other handlers from being invoked for the event.
- * %FALSE to propagate the event further.
- */
- widget_signals[NO_EXPOSE_EVENT] =
- g_signal_new (I_("no-expose-event"),
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GtkWidgetClass, no_expose_event),
- _gtk_boolean_handled_accumulator, NULL,
- _gtk_marshal_BOOLEAN__BOXED,
- G_TYPE_BOOLEAN, 1,
- GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
-
- /**
* GtkWidget::window-state-event:
* @widget: the object which received the signal
* @event: (type Gdk.EventWindowState): the #GdkEventWindowState which
@@ -3473,8 +3491,6 @@ gtk_widget_init (GtkWidget *widget)
priv = widget->priv;
priv->child_visible = TRUE;
- priv->state = GTK_STATE_NORMAL;
- priv->saved_state = GTK_STATE_NORMAL;
priv->name = NULL;
priv->allocation.x = -1;
priv->allocation.y = -1;
@@ -4015,6 +4031,82 @@ gtk_widget_show_all (GtkWidget *widget)
class->show_all (widget);
}
+static void
+_gtk_widget_notify_state_change (GtkWidget *widget,
+ GtkStateFlags flag,
+ gboolean target)
+{
+ GtkStateType state;
+
+ switch (flag)
+ {
+ case GTK_STATE_FLAG_ACTIVE:
+ state = GTK_STATE_ACTIVE;
+ break;
+ case GTK_STATE_FLAG_PRELIGHT:
+ state = GTK_STATE_PRELIGHT;
+ break;
+ case GTK_STATE_FLAG_SELECTED:
+ state = GTK_STATE_SELECTED;
+ break;
+ case GTK_STATE_FLAG_INSENSITIVE:
+ state = GTK_STATE_INSENSITIVE;
+ break;
+ case GTK_STATE_FLAG_INCONSISTENT:
+ state = GTK_STATE_INCONSISTENT;
+ break;
+ case GTK_STATE_FLAG_FOCUSED:
+ state = GTK_STATE_FOCUSED;
+ break;
+ default:
+ return;
+ }
+
+ gtk_style_context_notify_state_change (widget->priv->context,
+ gtk_widget_get_window (widget),
+ NULL, state, target);
+}
+
+/* Initializes state transitions for those states that
+ * were enabled before mapping and have a looping animation.
+ */
+static void
+_gtk_widget_start_state_transitions (GtkWidget *widget)
+{
+ GtkStateFlags state, flag;
+
+ if (!widget->priv->context)
+ return;
+
+ state = gtk_widget_get_state_flags (widget);
+ flag = GTK_STATE_FLAG_FOCUSED;
+
+ while (flag)
+ {
+ GtkAnimationDescription *animation_desc;
+
+ if ((state & flag) == 0)
+ {
+ flag >>= 1;
+ continue;
+ }
+
+ gtk_style_context_get (widget->priv->context, state,
+ "transition", &animation_desc,
+ NULL);
+
+ if (animation_desc)
+ {
+ if (gtk_animation_description_get_loop (animation_desc))
+ _gtk_widget_notify_state_change (widget, flag, TRUE);
+
+ gtk_animation_description_unref (animation_desc);
+ }
+
+ flag >>= 1;
+ }
+}
+
/**
* gtk_widget_map:
* @widget: a #GtkWidget
@@ -4042,6 +4134,8 @@ gtk_widget_map (GtkWidget *widget)
if (!gtk_widget_get_has_window (widget))
gdk_window_invalidate_rect (priv->window, &priv->allocation, FALSE);
+
+ _gtk_widget_start_state_transitions (widget);
}
}
@@ -4681,6 +4775,14 @@ gtk_widget_size_allocate (GtkWidget *widget,
cairo_region_destroy (invalidate);
}
}
+
+ if (size_changed || position_changed)
+ {
+ GtkStyleContext *context;
+
+ context = gtk_widget_get_style_context (widget);
+ _gtk_style_context_invalidate_animation_areas (context);
+ }
}
if ((size_changed || position_changed) && priv->parent &&
@@ -5414,6 +5516,8 @@ _gtk_widget_draw_internal (GtkWidget *widget,
cairo_t *cr,
gboolean clip_to_size)
{
+ GtkStyleContext *context;
+
if (!gtk_widget_is_drawable (widget))
return;
@@ -5434,6 +5538,11 @@ _gtk_widget_draw_internal (GtkWidget *widget,
0, cr,
&result);
}
+
+ context = gtk_widget_get_style_context (widget);
+ _gtk_style_context_coalesce_animation_areas (context,
+ widget->priv->allocation.x,
+ widget->priv->allocation.y);
}
/**
@@ -5817,9 +5926,6 @@ gtk_widget_event_internal (GtkWidget *widget,
case GDK_PROXIMITY_OUT:
signal_num = PROXIMITY_OUT_EVENT;
break;
- case GDK_NO_EXPOSE:
- signal_num = NO_EXPOSE_EVENT;
- break;
case GDK_CLIENT_EVENT:
signal_num = CLIENT_EVENT;
break;
@@ -6231,6 +6337,14 @@ gtk_widget_real_query_tooltip (GtkWidget *widget,
return FALSE;
}
+static void
+gtk_widget_real_style_updated (GtkWidget *widget)
+{
+ if (widget->priv->context)
+ gtk_style_context_invalidate (widget->priv->context);
+ gtk_widget_queue_resize (widget);
+}
+
static gboolean
gtk_widget_real_show_help (GtkWidget *widget,
GtkWidgetHelpType help_type)
@@ -6655,6 +6769,18 @@ gtk_widget_set_name (GtkWidget *widget,
g_free (priv->name);
priv->name = new_name;
+ if (priv->path)
+ {
+ guint pos;
+
+ pos = gtk_widget_path_length (priv->path) - 1;
+ gtk_widget_path_iter_set_name (priv->path, pos,
+ priv->name);
+ }
+
+ if (priv->context)
+ gtk_style_context_set_path (priv->context, priv->path);
+
if (priv->rc_style)
gtk_widget_reset_rc_style (widget);
@@ -6685,37 +6811,34 @@ gtk_widget_get_name (GtkWidget *widget)
return G_OBJECT_TYPE_NAME (widget);
}
-/**
- * gtk_widget_set_state:
- * @widget: a #GtkWidget
- * @state: new state for @widget
- *
- * This function is for use in widget implementations. Sets the state
- * of a widget (insensitive, prelighted, etc.) Usually you should set
- * the state using wrapper functions such as gtk_widget_set_sensitive().
- **/
-void
-gtk_widget_set_state (GtkWidget *widget,
- GtkStateType state)
+static void
+_gtk_widget_update_state_flags (GtkWidget *widget,
+ GtkStateFlags flags,
+ guint operation)
{
GtkWidgetPrivate *priv;
- g_return_if_fail (GTK_IS_WIDGET (widget));
-
priv = widget->priv;
- if (state == gtk_widget_get_state (widget))
- return;
+ /* Handle insensitive first, since it is propagated
+ * differently throughout the widget hierarchy.
+ */
+ if ((flags & GTK_STATE_FLAG_INSENSITIVE) !=
+ (priv->state_flags & GTK_STATE_FLAG_INSENSITIVE))
+ gtk_widget_set_sensitive (widget,
+ operation != STATE_CHANGE_UNSET);
- if (state == GTK_STATE_INSENSITIVE)
- gtk_widget_set_sensitive (widget, FALSE);
- else
+ flags &= ~(GTK_STATE_FLAG_INSENSITIVE);
+
+ if (flags != 0 ||
+ operation == STATE_CHANGE_REPLACE)
{
GtkStateData data;
- data.state = state;
- data.state_restoration = FALSE;
+ data.flags = flags;
+ data.operation = operation;
data.use_forall = FALSE;
+
if (priv->parent)
data.parent_sensitive = (gtk_widget_is_sensitive (priv->parent) != FALSE);
else
@@ -6729,6 +6852,141 @@ gtk_widget_set_state (GtkWidget *widget,
}
/**
+ * gtk_widget_set_state_flags:
+ * @widget: a #GtkWidget
+ * @flags: State flags to turn on
+ * @clear: Whether to clear state before turning on @flags
+ *
+ * This function is for use in widget implementations. Turns on flag
+ * values in the current widget state (insensitive, prelighted, etc.).
+ *
+ * It is worth mentioning that any other state than %GTK_STATE_FLAG_INSENSITIVE,
+ * will be propagated down to all non-internal children if @widget is a
+ * #GtkContainer, while %GTK_STATE_FLAG_INSENSITIVE itself will be propagated
+ * down to all #GtkContainer children by different means than turning on the
+ * state flag down the hierarchy, both gtk_widget_get_state_flags() and
+ * gtk_widget_is_sensitive() will make use of these.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_set_state_flags (GtkWidget *widget,
+ GtkStateFlags flags,
+ gboolean clear)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ if ((!clear && (widget->priv->state_flags & flags) == flags) ||
+ (clear && widget->priv->state_flags == flags))
+ return;
+
+ if (clear)
+ _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_REPLACE);
+ else
+ _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_SET);
+}
+
+/**
+ * gtk_widget_unset_state_flags:
+ * @widget: a #GtkWidget
+ * @flags: State flags to turn off
+ *
+ * This function is for use in widget implementations. Turns off flag
+ * values for the current widget state (insensitive, prelighted, etc.).
+ * See gtk_widget_set_state_flags().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_unset_state_flags (GtkWidget *widget,
+ GtkStateFlags flags)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ if ((widget->priv->state_flags & flags) == 0)
+ return;
+
+ _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_UNSET);
+}
+
+/**
+ * gtk_widget_get_state_flags:
+ * @widget: a #GtkWidget
+ *
+ * Returns the widget state as a flag set. It is worth mentioning
+ * that the effective %GTK_STATE_FLAG_INSENSITIVE state will be
+ * returned, that is, also based on parent insensitivity, even if
+ * @widget itself is sensitive.
+ *
+ * Returns: The state flags for widget
+ *
+ * Since: 3.0
+ **/
+GtkStateFlags
+gtk_widget_get_state_flags (GtkWidget *widget)
+{
+ GtkStateFlags flags;
+
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
+
+ flags = widget->priv->state_flags;
+
+ if (!gtk_widget_is_sensitive (widget))
+ flags |= GTK_STATE_FLAG_INSENSITIVE;
+
+ return flags;
+}
+
+/**
+ * gtk_widget_set_state:
+ * @widget: a #GtkWidget
+ * @state: new state for @widget
+ *
+ * This function is for use in widget implementations. Sets the state
+ * of a widget (insensitive, prelighted, etc.) Usually you should set
+ * the state using wrapper functions such as gtk_widget_set_sensitive().
+ *
+ * Deprecated: 3.0. Use gtk_widget_set_state_flags() instead.
+ **/
+void
+gtk_widget_set_state (GtkWidget *widget,
+ GtkStateType state)
+{
+ GtkStateFlags flags;
+
+ if (state == gtk_widget_get_state (widget))
+ return;
+
+ switch (state)
+ {
+ case GTK_STATE_ACTIVE:
+ flags = GTK_STATE_FLAG_ACTIVE;
+ break;
+ case GTK_STATE_PRELIGHT:
+ flags = GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags = GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags = GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ case GTK_STATE_INCONSISTENT:
+ flags = GTK_STATE_FLAG_INCONSISTENT;
+ break;
+ case GTK_STATE_FOCUSED:
+ flags = GTK_STATE_FLAG_FOCUSED;
+ break;
+ case GTK_STATE_NORMAL:
+ default:
+ flags = 0;
+ break;
+ }
+
+ _gtk_widget_update_state_flags (widget, flags, STATE_CHANGE_REPLACE);
+}
+
+/**
* gtk_widget_get_state:
* @widget: a #GtkWidget
*
@@ -6737,13 +6995,32 @@ gtk_widget_set_state (GtkWidget *widget,
* Returns: the state of @widget.
*
* Since: 2.18
+ *
+ * Deprecated: 3.0. Use gtk_widget_get_state_flags() instead.
*/
GtkStateType
gtk_widget_get_state (GtkWidget *widget)
{
+ GtkStateFlags flags;
+
g_return_val_if_fail (GTK_IS_WIDGET (widget), GTK_STATE_NORMAL);
- return widget->priv->state;
+ flags = gtk_widget_get_state_flags (widget);
+
+ if (flags & GTK_STATE_FLAG_INSENSITIVE)
+ return GTK_STATE_INSENSITIVE;
+ else if (flags & GTK_STATE_FLAG_INCONSISTENT)
+ return GTK_STATE_INCONSISTENT;
+ else if (flags & GTK_STATE_FLAG_ACTIVE)
+ return GTK_STATE_ACTIVE;
+ else if (flags & GTK_STATE_FLAG_SELECTED)
+ return GTK_STATE_SELECTED;
+ else if (flags & GTK_STATE_FLAG_FOCUSED)
+ return GTK_STATE_FOCUSED;
+ else if (flags & GTK_STATE_FLAG_PRELIGHT)
+ return GTK_STATE_PRELIGHT;
+ else
+ return GTK_STATE_NORMAL;
}
/**
@@ -7150,17 +7427,19 @@ gtk_widget_set_sensitive (GtkWidget *widget,
if (widget->priv->sensitive == sensitive)
return;
+ data.flags = GTK_STATE_FLAG_INSENSITIVE;
+
if (sensitive)
{
widget->priv->sensitive = TRUE;
- data.state = priv->saved_state;
+ data.operation = STATE_CHANGE_UNSET;
}
else
{
widget->priv->sensitive = FALSE;
- data.state = gtk_widget_get_state (widget);
+ data.operation = STATE_CHANGE_SET;
}
- data.state_restoration = TRUE;
+
data.use_forall = TRUE;
if (priv->parent)
@@ -7169,6 +7448,7 @@ gtk_widget_set_sensitive (GtkWidget *widget,
data.parent_sensitive = TRUE;
gtk_widget_propagate_state (widget, &data);
+
if (gtk_widget_is_drawable (widget))
gtk_widget_queue_draw (widget);
@@ -7216,6 +7496,18 @@ gtk_widget_is_sensitive (GtkWidget *widget)
return widget->priv->sensitive && widget->priv->parent_sensitive;
}
+static void
+_gtk_widget_update_path (GtkWidget *widget)
+{
+ if (widget->priv->path)
+ {
+ gtk_widget_path_free (widget->priv->path);
+ widget->priv->path = NULL;
+ }
+
+ gtk_widget_get_path (widget);
+}
+
/**
* gtk_widget_set_parent:
* @widget: a #GtkWidget
@@ -7232,6 +7524,7 @@ void
gtk_widget_set_parent (GtkWidget *widget,
GtkWidget *parent)
{
+ GtkStateFlags parent_flags;
GtkWidgetPrivate *priv;
GtkStateData data;
@@ -7258,14 +7551,17 @@ gtk_widget_set_parent (GtkWidget *widget,
g_object_ref_sink (widget);
priv->parent = parent;
- if (gtk_widget_get_state (parent) != GTK_STATE_NORMAL)
- data.state = gtk_widget_get_state (parent);
- else
- data.state = gtk_widget_get_state (widget);
- data.state_restoration = FALSE;
+ parent_flags = gtk_widget_get_state_flags (parent);
+
+ /* Merge both old state and current parent state,
+ * We don't want the insensitive flag to propagate
+ * to the new child though */
+ data.flags = parent_flags & ~GTK_STATE_FLAG_INSENSITIVE;
+ data.flags |= priv->state_flags;
+
+ data.operation = STATE_CHANGE_REPLACE;
data.parent_sensitive = (gtk_widget_is_sensitive (parent) != FALSE);
data.use_forall = gtk_widget_is_sensitive (parent) != gtk_widget_is_sensitive (widget);
-
gtk_widget_propagate_state (widget, &data);
gtk_widget_reset_rc_styles (widget);
@@ -7307,6 +7603,15 @@ gtk_widget_set_parent (GtkWidget *widget,
{
gtk_widget_queue_compute_expand (parent);
}
+
+ if (widget->priv->context)
+ {
+ _gtk_widget_update_path (widget);
+ gtk_style_context_set_path (widget->priv->context, widget->priv->path);
+
+ gtk_style_context_set_screen (widget->priv->context,
+ gtk_widget_get_screen (widget));
+ }
}
/**
@@ -7371,6 +7676,8 @@ gtk_widget_style_attach (GtkWidget *widget)
* mechanism, %FALSE otherwise.
*
* Since: 2.20
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
**/
gboolean
gtk_widget_has_rc_style (GtkWidget *widget)
@@ -7390,6 +7697,8 @@ gtk_widget_has_rc_style (GtkWidget *widget)
* want to use this function; it interacts badly with themes, because
* themes work by replacing the #GtkStyle. Instead, use
* gtk_widget_modify_style().
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
**/
void
gtk_widget_set_style (GtkWidget *widget,
@@ -7423,14 +7732,31 @@ gtk_widget_set_style (GtkWidget *widget,
* function; most of the time, if you want the style, the widget is
* realized, and realized widgets are guaranteed to have a style
* already.
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
**/
void
gtk_widget_ensure_style (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
+ if (!widget->priv->style ||
+ !gtk_style_has_context (widget->priv->style))
+ {
+ GtkStyle *style;
+
+ style = g_object_new (GTK_TYPE_STYLE,
+ "context", gtk_widget_get_style_context (widget),
+ NULL);
+
+ gtk_widget_set_style_internal (widget, style, TRUE);
+ g_object_unref (style);
+ }
+
+#if 0
if (!widget->priv->rc_style && !widget->priv->user_style)
gtk_widget_reset_rc_style (widget);
+#endif
}
/* Look up the RC style for this widget, unsetting any user style that
@@ -7464,6 +7790,8 @@ gtk_widget_reset_rc_style (GtkWidget *widget)
* Simply an accessor function that returns @widget->style.
*
* Return value: (transfer none): the widget's #GtkStyle
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
**/
GtkStyle*
gtk_widget_get_style (GtkWidget *widget)
@@ -7495,6 +7823,8 @@ gtk_widget_get_style (GtkWidget *widget)
* if you first call gtk_widget_modify_style(), subsequent calls
* to such functions gtk_widget_modify_fg() will have a cumulative
* effect with the initial modifications.
+ *
+ * Deprecated:3.0: Use #GtkStyleContext with a custom #GtkStyleProvider instead
**/
void
gtk_widget_modify_style (GtkWidget *widget,
@@ -7533,12 +7863,15 @@ gtk_widget_modify_style (GtkWidget *widget,
* thus dropping any reference to the old modifier style. Add a reference
* to the modifier style if you want to keep it alive.
*
- * Return value: (transfer none): the modifier style for the widget. This rc style is
- * owned by the widget. If you want to keep a pointer to value this
- * around, you must add a refcount using g_object_ref().
+ * Return value: (transfer none): the modifier style for the widget.
+ * This rc style is owned by the widget. If you want to keep a
+ * pointer to value this around, you must add a refcount using
+ * g_object_ref().
+ *
+ * Deprecated:3.0: Use #GtkStyleContext with a custom #GtkStyleProvider instead
**/
GtkRcStyle *
-gtk_widget_get_modifier_style (GtkWidget *widget)
+gtk_widget_get_modifier_style (GtkWidget *widget)
{
GtkRcStyle *rc_style;
@@ -7594,30 +7927,207 @@ gtk_widget_modify_color_component (GtkWidget *widget,
gtk_widget_modify_style (widget, rc_style);
}
+static void
+modifier_style_changed (GtkModifierStyle *style,
+ GtkWidget *widget)
+{
+ GtkStyleContext *context;
+
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_invalidate (context);
+}
+
+static GtkModifierStyle *
+_gtk_widget_get_modifier_properties (GtkWidget *widget)
+{
+ GtkModifierStyle *style;
+
+ style = g_object_get_qdata (G_OBJECT (widget), quark_modifier_style);
+
+ if (G_UNLIKELY (!style))
+ {
+ GtkStyleContext *context;
+
+ style = gtk_modifier_style_new ();
+ g_object_set_qdata_full (G_OBJECT (widget),
+ quark_modifier_style,
+ style,
+ (GDestroyNotify) g_object_unref);
+
+ g_signal_connect (style, "changed",
+ G_CALLBACK (modifier_style_changed), widget);
+
+ context = gtk_widget_get_style_context (widget);
+
+ gtk_style_context_add_provider (context,
+ GTK_STYLE_PROVIDER (style),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+ }
+
+ return style;
+}
+
+/**
+ * gtk_widget_override_color:
+ * @widget: a #GtkWidget
+ * @state: the state for which to set the color
+ * @color: the color to assign, or %NULL to undo the effect
+ * of previous calls to gtk_widget_override_color()
+ *
+ * Sets the color to use for a widget. All other style values are left
+ * untouched.
+ *
+ * <note>
+ * <para>
+ * This API is mostly meant as a quick way for applications to change a
+ * widget appearance. If you are developing a widgets library and intend
+ * this change to be themeable, it is better done by setting meaningful
+ * CSS classes and regions in your widget/container implementation through
+ * gtk_style_context_add_class() and gtk_style_context_add_region().
+ * </para>
+ * <para>
+ * This way, your widget library can install a #GtkCssProvider with the
+ * %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK priority in order to provide a
+ * default styling for those widgets that need so, and this theming may
+ * fully overridden by the user's theme.
+ * </para>
+ * </note>
+ *
+ * <note>
+ * <para>
+ * Note that for complex widgets this may bring in
+ * undesired results (such as uniform background color everywhere),
+ * in these cases it is better to fully style such widgets through a
+ * #GtkCssProvider with the %GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
+ * priority.
+ * </para>
+ * </note>
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_override_color (GtkWidget *widget,
+ GtkStateFlags state,
+ const GdkRGBA *color)
+{
+ GtkModifierStyle *style;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ style = _gtk_widget_get_modifier_properties (widget);
+ gtk_modifier_style_set_color (style, state, color);
+}
+
+/**
+ * gtk_widget_override_background_color:
+ * @widget: a #GtkWidget
+ * @state: the state for which to set the background color
+ * @color: the color to assign, or %NULL to undo the effect
+ * of previous calls to gtk_widget_override_background_color()
+ *
+ * Sets the background color to use for a widget. All other style values
+ * are left untouched. See gtk_widget_override_color().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_override_background_color (GtkWidget *widget,
+ GtkStateFlags state,
+ const GdkRGBA *color)
+{
+ GtkModifierStyle *style;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ style = _gtk_widget_get_modifier_properties (widget);
+ gtk_modifier_style_set_background_color (style, state, color);
+}
+
/**
- * gtk_widget_modify_symbolic_color:
+ * gtk_widget_override_font:
+ * @widget: a #GtkWidget
+ * @font_desc: the font descriptiong to use, or %NULL to undo
+ * the effect of previous calls to
+ * gtk_widget_override_font().
+ *
+ * Sets the font to use for a widget. All other style values are
+ * left untouched. See gtk_widget_override_color().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_override_font (GtkWidget *widget,
+ const PangoFontDescription *font_desc)
+{
+ GtkModifierStyle *style;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ style = _gtk_widget_get_modifier_properties (widget);
+ gtk_modifier_style_set_font (style, font_desc);
+}
+
+/**
+ * gtk_widget_override_symbolic_color:
* @widget: a #GtkWidget
* @name: the name of the symbolic color to modify
* @color: (allow-none): the color to assign (does not need to be allocated),
* or %NULL to undo the effect of previous calls to
- * of gtk_widget_modify_symbolic_color().
+ * gtk_widget_override_symbolic_color().
*
- * Sets a symbolic color for a widget.
- * All other style values are left untouched. See also
- * gtk_widget_modify_style().
+ * Sets a symbolic color for a widget, All other style values are left
+ * untouched. See gtk_widget_override_color() for overriding the foreground
+ * or background color.
*
* Since: 3.0
**/
void
-gtk_widget_modify_symbolic_color (GtkWidget *widget,
- const gchar *name,
- const GdkColor *color)
+gtk_widget_override_symbolic_color (GtkWidget *widget,
+ const gchar *name,
+ const GdkRGBA *color)
{
- GtkRcStyle *rc_style = gtk_widget_get_modifier_style (widget);
+ GtkModifierStyle *style;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
- _gtk_rc_style_set_symbolic_color (rc_style, name, color);
+ style = _gtk_widget_get_modifier_properties (widget);
+ gtk_modifier_style_map_color (style, name, color);
+}
- gtk_widget_modify_style (widget, rc_style);
+/**
+ * gtk_widget_override_cursor:
+ * @widget: a #GtkWidget
+ * @primary: the color to use for primary cursor (does not need to be
+ * allocated), or %NULL to undo the effect of previous calls to
+ * of gtk_widget_override_cursor().
+ * @secondary: the color to use for secondary cursor (does not need to be
+ * allocated), or %NULL to undo the effect of previous calls to
+ * of gtk_widget_override_cursor().
+ *
+ * Sets the cursor color to use in a widget, overriding the
+ * #GtkWidget:cursor-color and #GtkWidget:secondary-cursor-color
+ * style properties. All other style values are left untouched.
+ * See also gtk_widget_modify_style().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_override_cursor (GtkWidget *widget,
+ const GdkColor *cursor,
+ const GdkColor *secondary_cursor)
+{
+ GtkModifierStyle *style;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ style = _gtk_widget_get_modifier_properties (widget);
+ gtk_modifier_style_set_color_property (style,
+ GTK_TYPE_WIDGET,
+ "cursor-color", cursor);
+ gtk_modifier_style_set_color_property (style,
+ GTK_TYPE_WIDGET,
+ "secondary-cursor-color",
+ secondary_cursor);
}
/**
@@ -7631,16 +8141,55 @@ gtk_widget_modify_symbolic_color (GtkWidget *widget,
* Sets the foreground color for a widget in a particular state.
* All other style values are left untouched. See also
* gtk_widget_modify_style().
+ *
+ * Deprecated:3.0: Use gtk_widget_override_color() instead
**/
void
gtk_widget_modify_fg (GtkWidget *widget,
GtkStateType state,
const GdkColor *color)
{
+ GtkStateFlags flags;
+ GdkRGBA rgba;
+
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (state >= GTK_STATE_NORMAL && state <= GTK_STATE_INSENSITIVE);
- gtk_widget_modify_color_component (widget, GTK_RC_FG, state, color);
+ switch (state)
+ {
+ case GTK_STATE_ACTIVE:
+ flags = GTK_STATE_FLAG_ACTIVE;
+ break;
+ case GTK_STATE_PRELIGHT:
+ flags = GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags = GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags = GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ case GTK_STATE_NORMAL:
+ default:
+ flags = 0;
+ }
+
+ if (color)
+ {
+ rgba.red = color->red / 65535.;
+ rgba.green = color->green / 65535.;
+ rgba.blue = color->blue / 65535.;
+ rgba.alpha = 1;
+
+ gtk_widget_override_color (widget, state, &rgba);
+ }
+ else
+ gtk_widget_override_color (widget, state, NULL);
+
+ g_signal_emit (widget,
+ widget_signals[STYLE_SET],
+ 0,
+ widget->priv->style);
}
/**
@@ -7662,16 +8211,55 @@ gtk_widget_modify_fg (GtkWidget *widget,
* on their parent; if you want to set the background of a rectangular
* area around a label, try placing the label in a #GtkEventBox widget
* and setting the background color on that.
+ *
+ * Deprecated:3.0: Use gtk_widget_override_background_color() instead
**/
void
gtk_widget_modify_bg (GtkWidget *widget,
GtkStateType state,
const GdkColor *color)
{
+ GtkStateFlags flags;
+ GdkRGBA rgba;
+
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (state >= GTK_STATE_NORMAL && state <= GTK_STATE_INSENSITIVE);
- gtk_widget_modify_color_component (widget, GTK_RC_BG, state, color);
+ switch (state)
+ {
+ case GTK_STATE_ACTIVE:
+ flags = GTK_STATE_FLAG_ACTIVE;
+ break;
+ case GTK_STATE_PRELIGHT:
+ flags = GTK_STATE_FLAG_PRELIGHT;
+ break;
+ case GTK_STATE_SELECTED:
+ flags = GTK_STATE_FLAG_SELECTED;
+ break;
+ case GTK_STATE_INSENSITIVE:
+ flags = GTK_STATE_FLAG_INSENSITIVE;
+ break;
+ case GTK_STATE_NORMAL:
+ default:
+ flags = 0;
+ }
+
+ if (color)
+ {
+ rgba.red = color->red / 65535.;
+ rgba.green = color->green / 65535.;
+ rgba.blue = color->blue / 65535.;
+ rgba.alpha = 1;
+
+ gtk_widget_override_background_color (widget, state, &rgba);
+ }
+ else
+ gtk_widget_override_background_color (widget, state, NULL);
+
+ g_signal_emit (widget,
+ widget_signals[STYLE_SET],
+ 0,
+ widget->priv->style);
}
/**
@@ -7687,6 +8275,8 @@ gtk_widget_modify_bg (GtkWidget *widget,
* color used along with the base color (see gtk_widget_modify_base())
* for widgets such as #GtkEntry and #GtkTextView. See also
* gtk_widget_modify_style().
+ *
+ * Deprecated:3.0: Use gtk_widget_override_color() instead
**/
void
gtk_widget_modify_text (GtkWidget *widget,
@@ -7720,6 +8310,8 @@ gtk_widget_modify_text (GtkWidget *widget,
* parent; if you want to set the background of a rectangular area around
* a label, try placing the label in a #GtkEventBox widget and setting
* the base color on that.
+ *
+ * Deprecated:3.0: Use gtk_widget_override_background_color() instead
**/
void
gtk_widget_modify_base (GtkWidget *widget,
@@ -7778,22 +8370,22 @@ modify_color_property (GtkWidget *widget,
* See also gtk_widget_modify_style().
*
* Since: 2.12
+ *
+ * Deprecated: 3.0. Use gtk_widget_override_cursor() instead.
**/
void
gtk_widget_modify_cursor (GtkWidget *widget,
const GdkColor *primary,
const GdkColor *secondary)
{
- GtkRcStyle *rc_style;
-
g_return_if_fail (GTK_IS_WIDGET (widget));
- rc_style = gtk_widget_get_modifier_style (widget);
-
- modify_color_property (widget, rc_style, "cursor-color", primary);
- modify_color_property (widget, rc_style, "secondary-cursor-color", secondary);
+ gtk_widget_override_cursor (widget, primary, secondary);
- gtk_widget_modify_style (widget, rc_style);
+ g_signal_emit (widget,
+ widget_signals[STYLE_SET],
+ 0,
+ widget->priv->style);
}
/**
@@ -7804,26 +8396,21 @@ gtk_widget_modify_cursor (GtkWidget *widget,
*
* Sets the font to use for a widget. All other style values are left
* untouched. See also gtk_widget_modify_style().
+ *
+ * Deprecated:3.0: Use gtk_widget_override_font() instead
**/
void
gtk_widget_modify_font (GtkWidget *widget,
PangoFontDescription *font_desc)
{
- GtkRcStyle *rc_style;
-
g_return_if_fail (GTK_IS_WIDGET (widget));
- rc_style = gtk_widget_get_modifier_style (widget);
-
- if (rc_style->font_desc)
- pango_font_description_free (rc_style->font_desc);
-
- if (font_desc)
- rc_style->font_desc = pango_font_description_copy (font_desc);
- else
- rc_style->font_desc = NULL;
+ gtk_widget_override_font (widget, font_desc);
- gtk_widget_modify_style (widget, rc_style);
+ g_signal_emit (widget,
+ widget_signals[STYLE_SET],
+ 0,
+ widget->priv->style);
}
static void
@@ -7841,7 +8428,8 @@ gtk_widget_real_style_set (GtkWidget *widget,
if (gtk_widget_get_realized (widget) &&
gtk_widget_get_has_window (widget))
- gtk_style_set_background (priv->style, priv->window, priv->state);
+ gtk_style_set_background (priv->style, priv->window,
+ gtk_widget_get_state (widget));
}
static void
@@ -7907,6 +8495,8 @@ do_screen_change (GtkWidget *widget,
{
if (old_screen != new_screen)
{
+ GtkStyleContext *context;
+
if (old_screen)
{
PangoContext *context = g_object_get_qdata (G_OBJECT (widget), quark_pango_context);
@@ -7915,6 +8505,10 @@ do_screen_change (GtkWidget *widget,
}
_gtk_tooltip_hide (widget);
+
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_set_screen (context, gtk_widget_get_screen (widget));
+
g_signal_emit (widget, widget_signals[SCREEN_CHANGED], 0, old_screen);
}
}
@@ -8078,17 +8672,33 @@ _gtk_widget_propagate_screen_changed (GtkWidget *widget,
}
static void
-reset_rc_styles_recurse (GtkWidget *widget, gpointer data)
+reset_style_recurse (GtkWidget *widget, gpointer data)
{
+#if 0
if (widget->priv->rc_style)
gtk_widget_reset_rc_style (widget);
+#endif
+
+ if (widget->priv->context)
+ {
+ _gtk_widget_update_path (widget);
+ gtk_style_context_set_path (widget->priv->context,
+ widget->priv->path);
+ }
if (GTK_IS_CONTAINER (widget))
gtk_container_forall (GTK_CONTAINER (widget),
- reset_rc_styles_recurse,
+ reset_style_recurse,
NULL);
}
+void
+gtk_widget_reset_style (GtkWidget *widget)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ reset_style_recurse (widget, NULL);
+}
/**
* gtk_widget_reset_rc_styles:
@@ -8099,13 +8709,15 @@ reset_rc_styles_recurse (GtkWidget *widget, gpointer data)
* for the currently loaded RC file settings.
*
* This function is not useful for applications.
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
*/
void
gtk_widget_reset_rc_styles (GtkWidget *widget)
{
g_return_if_fail (GTK_IS_WIDGET (widget));
- reset_rc_styles_recurse (widget, NULL);
+ reset_style_recurse (widget, NULL);
}
/**
@@ -8113,8 +8725,10 @@ gtk_widget_reset_rc_styles (GtkWidget *widget)
*
* Returns the default style used by all widgets initially.
*
- * Returns: (transfer none): the default style. This #GtkStyle object is owned
- * by GTK+ and should not be modified or freed.
+ * Returns: (transfer none): the default style. This #GtkStyle
+ * object is owned by GTK+ and should not be modified or freed.
+ *
+ * Deprecated:3.0: Use #GtkStyleContext instead
*/
GtkStyle*
gtk_widget_get_default_style (void)
@@ -8176,12 +8790,22 @@ static void
update_pango_context (GtkWidget *widget,
PangoContext *context)
{
- GtkWidgetPrivate *priv = widget->priv;
+ PangoFontDescription *font_desc;
+ GtkStyleContext *style_context;
+
+ style_context = gtk_widget_get_style_context (widget);
- pango_context_set_font_description (context, priv->style->font_desc);
+ gtk_style_context_get (style_context,
+ gtk_widget_get_state_flags (widget),
+ "font", &font_desc,
+ NULL);
+
+ pango_context_set_font_description (context, font_desc);
pango_context_set_base_dir (context,
gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL);
+
+ pango_font_description_free (font_desc);
}
static void
@@ -8277,21 +8901,18 @@ gtk_widget_create_pango_layout (GtkWidget *widget,
}
/**
- * gtk_widget_render_icon:
+ * gtk_widget_render_icon_pixbuf:
* @widget: a #GtkWidget
* @stock_id: a stock ID
* @size: (type int): a stock size. A size of (GtkIconSize)-1 means
* render at the size of the source and don't scale (if there are
* multiple source sizes, GTK+ picks one of the available sizes).
- * @detail: (allow-none): render detail to pass to theme engine
*
- * A convenience function that uses the theme engine and RC file
+ * A convenience function that uses the theme engine and style
* settings for @widget to look up @stock_id and render it to
* a pixbuf. @stock_id should be a stock icon ID such as
* #GTK_STOCK_OPEN or #GTK_STOCK_OK. @size should be a size
- * such as #GTK_ICON_SIZE_MENU. @detail should be a string that
- * identifies the widget or code doing the rendering, so that
- * theme engines can special-case rendering for that widget or code.
+ * such as #GTK_ICON_SIZE_MENU.
*
* The pixels in the returned #GdkPixbuf are shared with the rest of
* the application and should not be modified. The pixbuf should be freed
@@ -8299,16 +8920,16 @@ gtk_widget_create_pango_layout (GtkWidget *widget,
*
* Return value: (transfer full): a new pixbuf, or %NULL if the
* stock ID wasn't known
+ *
+ * Since: 3.0
**/
GdkPixbuf*
-gtk_widget_render_icon (GtkWidget *widget,
- const gchar *stock_id,
- GtkIconSize size,
- const gchar *detail)
+gtk_widget_render_icon_pixbuf (GtkWidget *widget,
+ const gchar *stock_id,
+ GtkIconSize size)
{
GtkWidgetPrivate *priv;
GtkIconSet *icon_set;
- GdkPixbuf *retval;
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
g_return_val_if_fail (stock_id != NULL, NULL);
@@ -8318,20 +8939,47 @@ gtk_widget_render_icon (GtkWidget *widget,
gtk_widget_ensure_style (widget);
- icon_set = gtk_style_lookup_icon_set (priv->style, stock_id);
+ icon_set = gtk_style_context_lookup_icon_set (priv->context, stock_id);
if (icon_set == NULL)
return NULL;
- retval = gtk_icon_set_render_icon (icon_set,
- priv->style,
- gtk_widget_get_direction (widget),
- gtk_widget_get_state (widget),
- size,
- widget,
- detail);
+ return gtk_icon_set_render_icon_pixbuf (icon_set, priv->context, size);
+}
- return retval;
+/**
+ * gtk_widget_render_icon:
+ * @widget: a #GtkWidget
+ * @stock_id: a stock ID
+ * @size: (type int): a stock size. A size of (GtkIconSize)-1 means
+ * render at the size of the source and don't scale (if there are
+ * multiple source sizes, GTK+ picks one of the available sizes).
+ * @detail: (allow-none): render detail to pass to theme engine
+ *
+ * A convenience function that uses the theme engine and RC file
+ * settings for @widget to look up @stock_id and render it to
+ * a pixbuf. @stock_id should be a stock icon ID such as
+ * #GTK_STOCK_OPEN or #GTK_STOCK_OK. @size should be a size
+ * such as #GTK_ICON_SIZE_MENU. @detail should be a string that
+ * identifies the widget or code doing the rendering, so that
+ * theme engines can special-case rendering for that widget or code.
+ *
+ * The pixels in the returned #GdkPixbuf are shared with the rest of
+ * the application and should not be modified. The pixbuf should be freed
+ * after use with g_object_unref().
+ *
+ * Return value: (transfer full): a new pixbuf, or %NULL if the
+ * stock ID wasn't known
+ *
+ * Deprecated: 3.0: Use gtk_widget_render_icon_pixbuf() instead.
+ **/
+GdkPixbuf*
+gtk_widget_render_icon (GtkWidget *widget,
+ const gchar *stock_id,
+ GtkIconSize size,
+ const gchar *detail)
+{
+ return gtk_widget_render_icon_pixbuf (widget, stock_id, size);
}
/**
@@ -9567,7 +10215,13 @@ gtk_widget_set_direction (GtkWidget *widget,
widget->priv->direction = dir;
if (old_dir != gtk_widget_get_direction (widget))
- gtk_widget_emit_direction_changed (widget, old_dir);
+ {
+ if (widget->priv->context)
+ gtk_style_context_set_direction (widget->priv->context,
+ gtk_widget_get_direction (widget));
+
+ gtk_widget_emit_direction_changed (widget, old_dir);
+ }
}
/**
@@ -9726,6 +10380,12 @@ gtk_widget_finalize (GObject *object)
if (accessible)
g_object_unref (accessible);
+ if (priv->path)
+ gtk_widget_path_free (priv->path);
+
+ if (priv->context)
+ g_object_unref (priv->context);
+
if (g_object_is_floating (object))
g_warning ("A floating object was finalized. This means that someone\n"
"called g_object_unref() on an object that had only a floating\n"
@@ -10278,33 +10938,27 @@ gtk_widget_propagate_state (GtkWidget *widget,
GtkStateData *data)
{
GtkWidgetPrivate *priv = widget->priv;
- guint8 old_state = gtk_widget_get_state (widget);
- guint8 old_saved_state = priv->saved_state;
+ GtkStateFlags new_flags, old_flags = priv->state_flags;
+ GtkStateType old_state;
- /* don't call this function with state==GTK_STATE_INSENSITIVE,
- * parent_sensitive==TRUE on a sensitive widget
- */
+ old_state = gtk_widget_get_state (widget);
+ if (!priv->parent_sensitive)
+ old_flags |= GTK_STATE_FLAG_INSENSITIVE;
priv->parent_sensitive = data->parent_sensitive;
- if (gtk_widget_is_sensitive (widget))
+ switch (data->operation)
{
- if (data->state_restoration)
- priv->state = priv->saved_state;
- else
- priv->state = data->state;
- }
- else
- {
- if (!data->state_restoration)
- {
- if (data->state != GTK_STATE_INSENSITIVE)
- priv->saved_state = data->state;
- }
- else if (gtk_widget_get_state (widget) != GTK_STATE_INSENSITIVE)
- priv->saved_state = gtk_widget_get_state (widget);
- priv->state = GTK_STATE_INSENSITIVE;
+ case STATE_CHANGE_REPLACE:
+ priv->state_flags = data->flags;
+ break;
+ case STATE_CHANGE_SET:
+ priv->state_flags |= data->flags;
+ break;
+ case STATE_CHANGE_UNSET:
+ priv->state_flags &= ~(data->flags);
+ break;
}
if (gtk_widget_is_focus (widget) && !gtk_widget_is_sensitive (widget))
@@ -10312,12 +10966,14 @@ gtk_widget_propagate_state (GtkWidget *widget,
GtkWidget *window;
window = gtk_widget_get_toplevel (widget);
+
if (window && gtk_widget_is_toplevel (window))
gtk_window_set_focus (GTK_WINDOW (window), NULL);
}
- if (old_state != gtk_widget_get_state (widget) ||
- old_saved_state != priv->saved_state)
+ new_flags = gtk_widget_get_state_flags (widget);
+
+ if (old_flags != new_flags)
{
g_object_ref (widget);
@@ -10325,6 +10981,7 @@ gtk_widget_propagate_state (GtkWidget *widget,
gtk_grab_remove (widget);
g_signal_emit (widget, widget_signals[STATE_CHANGED], 0, old_state);
+ g_signal_emit (widget, widget_signals[STATE_FLAGS_CHANGED], 0, old_flags);
if (!priv->shadowed)
{
@@ -10351,7 +11008,7 @@ gtk_widget_propagate_state (GtkWidget *widget,
if (!gtk_widget_is_sensitive (widget))
_gtk_widget_synthesize_crossing (widget, NULL, d->data,
GDK_CROSSING_STATE_CHANGED);
- else if (old_state == GTK_STATE_INSENSITIVE)
+ else if (old_flags & GTK_STATE_FLAG_INSENSITIVE)
_gtk_widget_synthesize_crossing (NULL, widget, d->data,
GDK_CROSSING_STATE_CHANGED);
@@ -10363,17 +11020,48 @@ gtk_widget_propagate_state (GtkWidget *widget,
}
if (GTK_IS_CONTAINER (widget))
- {
- data->parent_sensitive = (gtk_widget_is_sensitive (widget) != FALSE);
- if (data->use_forall)
- gtk_container_forall (GTK_CONTAINER (widget),
- (GtkCallback) gtk_widget_propagate_state,
- data);
- else
- gtk_container_foreach (GTK_CONTAINER (widget),
- (GtkCallback) gtk_widget_propagate_state,
- data);
- }
+ {
+ data->parent_sensitive = gtk_widget_is_sensitive (widget);
+
+ /* Do not propagate insensitive state further */
+ data->flags &= ~(GTK_STATE_FLAG_INSENSITIVE);
+
+ if (data->use_forall)
+ gtk_container_forall (GTK_CONTAINER (widget),
+ (GtkCallback) gtk_widget_propagate_state,
+ data);
+ else
+ gtk_container_foreach (GTK_CONTAINER (widget),
+ (GtkCallback) gtk_widget_propagate_state,
+ data);
+ }
+
+ /* Trigger state change transitions for the widget */
+ if (priv->context &&
+ gtk_widget_get_mapped (widget))
+ {
+ gint diff, flag = 1;
+ GdkWindow *window;
+
+ diff = old_flags ^ new_flags;
+ window = gtk_widget_get_window (widget);
+
+ while (diff != 0)
+ {
+ if ((diff & flag) != 0)
+ {
+ gboolean target;
+
+ target = ((new_flags & flag) != 0);
+ _gtk_widget_notify_state_change (widget, flag, target);
+
+ diff &= ~flag;
+ }
+
+ flag <<= 1;
+ }
+ }
+
g_object_unref (widget);
}
}
@@ -10692,15 +11380,12 @@ gtk_widget_style_get_property (GtkWidget *widget,
const gchar *property_name,
GValue *value)
{
- GtkWidgetPrivate *priv;
GParamSpec *pspec;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (property_name != NULL);
g_return_if_fail (G_IS_VALUE (value));
- priv = widget->priv;
-
g_object_ref (widget);
pspec = g_param_spec_pool_lookup (style_property_spec_pool,
property_name,
@@ -10713,12 +11398,16 @@ gtk_widget_style_get_property (GtkWidget *widget,
property_name);
else
{
+ GtkStyleContext *context;
const GValue *peek_value;
+ GtkStateFlags state;
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
- peek_value = _gtk_style_peek_property_value (priv->style,
- G_OBJECT_TYPE (widget),
- pspec,
- (GtkRcPropertyParser) g_param_spec_get_qdata (pspec, quark_property_parser));
+ peek_value = _gtk_style_context_peek_style_property (context,
+ G_OBJECT_TYPE (widget),
+ state, pspec);
/* auto-conversion of the caller's value type
*/
@@ -10751,14 +11440,15 @@ gtk_widget_style_get_valist (GtkWidget *widget,
const gchar *first_property_name,
va_list var_args)
{
- GtkWidgetPrivate *priv;
+ GtkStyleContext *context;
+ GtkStateFlags state;
const gchar *name;
g_return_if_fail (GTK_IS_WIDGET (widget));
- priv = widget->priv;
-
g_object_ref (widget);
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
name = first_property_name;
while (name)
@@ -10781,10 +11471,10 @@ gtk_widget_style_get_valist (GtkWidget *widget,
}
/* style pspecs are always readable so we can spare that check here */
- peek_value = _gtk_style_peek_property_value (priv->style,
- G_OBJECT_TYPE (widget),
- pspec,
- (GtkRcPropertyParser) g_param_spec_get_qdata (pspec, quark_property_parser));
+ peek_value = _gtk_style_context_peek_style_property (context,
+ G_OBJECT_TYPE (widget),
+ state, pspec);
+
G_VALUE_LCOPY (peek_value, var_args, 0, &error);
if (error)
{
@@ -10826,9 +11516,12 @@ gtk_widget_style_get (GtkWidget *widget,
/**
* gtk_widget_path:
* @widget: a #GtkWidget
- * @path_length: (out) (allow-none): location to store length of the path, or %NULL
- * @path: (out) (allow-none): location to store allocated path string, or %NULL
- * @path_reversed: (out) (allow-none): location to store allocated reverse path string, or %NULL
+ * @path_length: (out) (allow-none): location to store length of the path,
+ * or %NULL
+ * @path: (out) (allow-none): location to store allocated path string,
+ * or %NULL
+ * @path_reversed: (out) (allow-none): location to store allocated reverse
+ * path string, or %NULL
*
* Obtains the full path to @widget. The path is simply the name of a
* widget and all its parents in the container hierarchy, separated by
@@ -10842,6 +11535,8 @@ gtk_widget_style_get (GtkWidget *widget,
* file. @path_reversed_p fills in the path in reverse order,
* i.e. starting with @widget's name instead of starting with the name
* of @widget's outermost ancestor.
+ *
+ * Deprecated:3.0: Use gtk_widget_get_path() instead
**/
void
gtk_widget_path (GtkWidget *widget,
@@ -10899,14 +11594,17 @@ gtk_widget_path (GtkWidget *widget,
/**
* gtk_widget_class_path:
* @widget: a #GtkWidget
- * @path_length: (out) (allow-none): location to store the length of the class path, or %NULL
- * @path: (out) (allow-none): location to store the class path as an allocated string, or %NULL
- * @path_reversed: (out) (allow-none): location to store the reverse class path as an allocated
- * string, or %NULL
+ * @path_length: (out) (allow-none): location to store the length of the
+ * class path, or %NULL
+ * @path: (out) (allow-none): location to store the class path as an
+ * allocated string, or %NULL
+ * @path_reversed: (out) (allow-none): location to store the reverse
+ * class path as an allocated string, or %NULL
*
* Same as gtk_widget_path(), but always uses the name of a widget's type,
* never uses a custom name set with gtk_widget_set_name().
*
+ * Deprecated:3.0: Use gtk_widget_get_path() instead
**/
void
gtk_widget_class_path (GtkWidget *widget,
@@ -13191,3 +13889,112 @@ _gtk_widget_set_height_request_needed (GtkWidget *widget,
{
widget->priv->height_request_needed = height_request_needed;
}
+
+/**
+ * gtk_widget_get_path:
+ * @widget: a #GtkWidget
+ *
+ * Returns the #GtkWidgetPath representing @widget, if the widget
+ * is not connected to a toplevel widget, a partial path will be
+ * created.
+ *
+ * Returns: (transfer none): The #GtkWidgetPath representing @widget
+ **/
+GtkWidgetPath *
+gtk_widget_get_path (GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ /* As strange as it may seem, this may happen on object construction.
+ * init() implementations of parent types may eventually call this function,
+ * each with its corresponding GType, which could leave a child
+ * implementation with a wrong widget type in the widget path
+ */
+ if (widget->priv->path &&
+ G_OBJECT_TYPE (widget) != gtk_widget_path_get_widget_type (widget->priv->path))
+ {
+ gtk_widget_path_free (widget->priv->path);
+ widget->priv->path = NULL;
+ }
+
+ if (!widget->priv->path)
+ {
+ GtkWidget *parent;
+ guint pos;
+
+ parent = widget->priv->parent;
+
+ if (parent)
+ widget->priv->path = gtk_container_get_path_for_child (GTK_CONTAINER (parent), widget);
+ else
+ {
+ /* Widget is either toplevel or unparented, treat both
+ * as toplevels style wise, since there are situations
+ * where style properties might be retrieved on that
+ * situation.
+ */
+ widget->priv->path = gtk_widget_path_new ();
+ }
+
+ pos = gtk_widget_path_append_type (widget->priv->path, G_OBJECT_TYPE (widget));
+
+ if (widget->priv->name)
+ gtk_widget_path_iter_set_name (widget->priv->path, pos, widget->priv->name);
+
+ if (widget->priv->context)
+ gtk_style_context_set_path (widget->priv->context,
+ widget->priv->path);
+ }
+
+ return widget->priv->path;
+}
+
+static void
+style_context_changed (GtkStyleContext *context,
+ gpointer user_data)
+{
+ GtkWidget *widget = user_data;
+
+ g_signal_emit (widget, widget_signals[STYLE_UPDATED], 0);
+}
+
+/**
+ * gtk_widget_get_style_context:
+ * @widget: a #GtkWidget
+ *
+ * Returns the style context associated to @widget.
+ *
+ * Returns: (transfer none): a #GtkStyleContext. This memory is owned by @widget and
+ * must not be freed.
+ **/
+GtkStyleContext *
+gtk_widget_get_style_context (GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ if (G_UNLIKELY (!widget->priv->context))
+ {
+ widget->priv->context = g_object_new (GTK_TYPE_STYLE_CONTEXT,
+ "direction", gtk_widget_get_direction (widget),
+ NULL);
+
+ g_signal_connect (widget->priv->context, "changed",
+ G_CALLBACK (style_context_changed), widget);
+
+ gtk_style_context_set_screen (widget->priv->context,
+ gtk_widget_get_screen (widget));
+
+ _gtk_widget_update_path (widget);
+ gtk_style_context_set_path (widget->priv->context,
+ widget->priv->path);
+ }
+ else
+ {
+ /* Force widget path regeneration if needed, the
+ * context will be updated within this function.
+ */
+ gtk_widget_get_path (widget);
+ }
+
+ return widget->priv->context;
+}
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 8a566df12b..83cdb66270 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -35,7 +35,10 @@
#include <gtk/gtkaccelgroup.h>
#include <gtk/gtkadjustment.h>
#include <gtk/gtkstyle.h>
+#include <gtk/gtkborder.h>
#include <gtk/gtksettings.h>
+#include <gtk/gtkstylecontext.h>
+#include <gtk/gtkwidgetpath.h>
#include <atk/atk.h>
G_BEGIN_DECLS
@@ -223,6 +226,8 @@ struct _GtkWidgetClass
GtkAllocation *allocation);
void (* state_changed) (GtkWidget *widget,
GtkStateType previous_state);
+ void (* state_flags_changed) (GtkWidget *widget,
+ GtkStateFlags previous_state_flags);
void (* parent_set) (GtkWidget *widget,
GtkWidget *previous_parent);
void (* hierarchy_changed) (GtkWidget *widget,
@@ -320,8 +325,6 @@ struct _GtkWidgetClass
GdkEventVisibility *event);
gboolean (* client_event) (GtkWidget *widget,
GdkEventClient *event);
- gboolean (* no_expose_event) (GtkWidget *widget,
- GdkEventAny *event);
gboolean (* window_state_event) (GtkWidget *widget,
GdkEventWindowState *event);
gboolean (* damage_event) (GtkWidget *widget,
@@ -416,6 +419,8 @@ struct _GtkWidgetClass
gint *allocated_pos,
gint *allocated_size);
+ void (* style_updated) (GtkWidget *widget);
+
/*< private >*/
/* Padding for future expansion */
@@ -576,6 +581,13 @@ void gtk_widget_set_state (GtkWidget *widget,
GtkStateType state);
GtkStateType gtk_widget_get_state (GtkWidget *widget);
+void gtk_widget_set_state_flags (GtkWidget *widget,
+ GtkStateFlags flags,
+ gboolean clear);
+void gtk_widget_unset_state_flags (GtkWidget *widget,
+ GtkStateFlags flags);
+GtkStateFlags gtk_widget_get_state_flags (GtkWidget *widget);
+
void gtk_widget_set_sensitive (GtkWidget *widget,
gboolean sensitive);
gboolean gtk_widget_get_sensitive (GtkWidget *widget);
@@ -747,10 +759,31 @@ gboolean gtk_widget_translate_coordinates (GtkWidget *src_widget,
*/
gboolean gtk_widget_hide_on_delete (GtkWidget *widget);
+/* Functions to override widget styling */
+void gtk_widget_override_color (GtkWidget *widget,
+ GtkStateFlags state,
+ const GdkRGBA *color);
+void gtk_widget_override_background_color (GtkWidget *widget,
+ GtkStateFlags state,
+ const GdkRGBA *color);
+
+void gtk_widget_override_font (GtkWidget *widget,
+ const PangoFontDescription *font_desc);
+
+void gtk_widget_override_symbolic_color (GtkWidget *widget,
+ const gchar *name,
+ const GdkRGBA *color);
+void gtk_widget_override_cursor (GtkWidget *widget,
+ const GdkColor *cursor,
+ const GdkColor *secondary_cursor);
+
+
+void gtk_widget_style_attach (GtkWidget *widget);
+
+#if !defined(GTK_DISABLE_DEPRECATED) || defined(GTK_COMPILATION)
+
/* Widget styles.
*/
-void gtk_widget_style_attach (GtkWidget *widget);
-
gboolean gtk_widget_has_rc_style (GtkWidget *widget);
void gtk_widget_set_style (GtkWidget *widget,
GtkStyle *style);
@@ -777,9 +810,27 @@ void gtk_widget_modify_cursor (GtkWidget *widget,
const GdkColor *secondary);
void gtk_widget_modify_font (GtkWidget *widget,
PangoFontDescription *font_desc);
-void gtk_widget_modify_symbolic_color (GtkWidget *widget,
- const gchar *name,
- const GdkColor *color);
+
+/* Descend recursively and set rc-style on all widgets without user styles */
+void gtk_widget_reset_rc_styles (GtkWidget *widget);
+void gtk_widget_reset_style (GtkWidget *widget);
+
+/* Set certain default values to be used at widget creation time */
+GtkStyle* gtk_widget_get_default_style (void);
+
+/* Compute a widget's path in the form "GtkWindow.MyLabel", and
+ * return newly alocated strings.
+ */
+void gtk_widget_path (GtkWidget *widget,
+ guint *path_length,
+ gchar **path,
+ gchar **path_reversed);
+void gtk_widget_class_path (GtkWidget *widget,
+ guint *path_length,
+ gchar **path,
+ gchar **path_reversed);
+
+#endif /* GTK_DISABLE_DEPRECATED */
PangoContext *gtk_widget_create_pango_context (GtkWidget *widget);
PangoContext *gtk_widget_get_pango_context (GtkWidget *widget);
@@ -798,9 +849,6 @@ void gtk_widget_set_composite_name (GtkWidget *widget,
const gchar *name);
gchar* gtk_widget_get_composite_name (GtkWidget *widget);
-/* Descend recursively and set rc-style on all widgets without user styles */
-void gtk_widget_reset_rc_styles (GtkWidget *widget);
-
/* Push/pop pairs, to change default values upon a widget's creation.
* This will override the values that got set by the
* gtk_widget_set_default_* () functions.
@@ -829,13 +877,7 @@ void gtk_widget_style_get (GtkWidget *widget,
const gchar *first_property_name,
...) G_GNUC_NULL_TERMINATED;
-
-/* Set certain default values to be used at widget creation time.
- */
-GtkStyle* gtk_widget_get_default_style (void);
-
-/* Functions for setting directionality for widgets
- */
+/* Functions for setting directionality for widgets */
void gtk_widget_set_direction (GtkWidget *widget,
GtkTextDirection dir);
@@ -857,18 +899,6 @@ void gtk_widget_input_shape_combine_region (GtkWidget *widget,
/* internal function */
void gtk_widget_reset_shapes (GtkWidget *widget);
-/* Compute a widget's path in the form "GtkWindow.MyLabel", and
- * return newly alocated strings.
- */
-void gtk_widget_path (GtkWidget *widget,
- guint *path_length,
- gchar **path,
- gchar **path_reversed);
-void gtk_widget_class_path (GtkWidget *widget,
- guint *path_length,
- gchar **path,
- gchar **path_reversed);
-
GList* gtk_widget_list_mnemonic_labels (GtkWidget *widget);
void gtk_widget_add_mnemonic_label (GtkWidget *widget,
GtkWidget *label);
@@ -943,6 +973,11 @@ void _gtk_widget_buildable_finish_accelerator (GtkWidget *widget,
gboolean gtk_widget_in_destruction (GtkWidget *widget);
+GtkStyleContext * gtk_widget_get_style_context (GtkWidget *widget);
+
+GtkWidgetPath * gtk_widget_get_path (GtkWidget *widget);
+
+
G_END_DECLS
#endif /* __GTK_WIDGET_H__ */
diff --git a/gtk/gtkwidgetpath.c b/gtk/gtkwidgetpath.c
new file mode 100644
index 0000000000..d25f550975
--- /dev/null
+++ b/gtk/gtkwidgetpath.c
@@ -0,0 +1,1048 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gtkwidget.h"
+#include "gtkwidgetpath.h"
+
+/**
+ * SECTION:gtkwidgetpath
+ * @Short_description: Widget path abstraction
+ * @Title: GtkWidgetPath
+ * @See_also: #GtkStyleContext
+ *
+ * GtkWidgetPath is a boxed type that represents a widget hierarchy from
+ * the topmost widget, typically a toplevel, to any child. This widget
+ * path abstraction is used in #GtkStyleContext on behalf of the real
+ * widget in order to query style information.
+ *
+ * If you are using GTK+ widgets, you probably will not need to use
+ * this API directly, as there is gtk_widget_get_path(), and the style
+ * context returned by gtk_widget_get_style_context() will be automatically
+ * updated on widget hierarchy changes.
+ *
+ * The widget path generation is generally simple:
+ * <example>
+ * <title>Defining a button within a window</title>
+ * <programlisting>
+ * {
+ * GtkWidgetPath *path;
+ *
+ * path = gtk_widget_path_new ();
+ * gtk_widget_path_append_type (path, GTK_TYPE_WINDOW);
+ * gtk_widget_path_append_type (path, GTK_TYPE_BUTTON);
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * Although more complex information, such as widget names, or
+ * different classes (property that may be used by other widget
+ * types) and intermediate regions may be included:
+ *
+ * <example>
+ * <title>Defining the first tab widget in a notebook</title>
+ * <programlisting>
+ * {
+ * GtkWidgetPath *path;
+ * guint pos;
+ *
+ * path = gtk_widget_path_new ();
+ *
+ * pos = gtk_widget_path_append_type (path, GTK_TYPE_NOTEBOOK);
+ * gtk_widget_path_iter_add_region (path, pos, "tab", GTK_REGION_EVEN | GTK_REGION_FIRST);
+ *
+ * pos = gtk_widget_path_append_type (path, GTK_TYPE_LABEL);
+ * gtk_widget_path_iter_set_name (path, pos, "first tab label");
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * All this information will be used to match the style information
+ * that applies to the described widget.
+ **/
+
+G_DEFINE_BOXED_TYPE (GtkWidgetPath, gtk_widget_path,
+ gtk_widget_path_copy, gtk_widget_path_free)
+
+
+typedef struct GtkPathElement GtkPathElement;
+
+struct GtkPathElement
+{
+ GType type;
+ GQuark name;
+ GHashTable *regions;
+ GArray *classes;
+};
+
+struct _GtkWidgetPath
+{
+ GArray *elems; /* First element contains the described widget */
+};
+
+/**
+ * gtk_widget_path_new:
+ *
+ * Returns an empty widget path.
+ *
+ * Returns: (transfer full): A newly created, empty, #GtkWidgetPath
+ *
+ * Since: 3.0
+ **/
+GtkWidgetPath *
+gtk_widget_path_new (void)
+{
+ GtkWidgetPath *path;
+
+ path = g_slice_new0 (GtkWidgetPath);
+ path->elems = g_array_new (FALSE, TRUE, sizeof (GtkPathElement));
+
+ return path;
+}
+
+/**
+ * gtk_widget_path_copy:
+ * @path: a #GtkWidgetPath
+ *
+ * Returns a copy of @path
+ *
+ * Returns: (transfer full): a copy of @path
+ *
+ * Since: 3.0
+ **/
+GtkWidgetPath *
+gtk_widget_path_copy (const GtkWidgetPath *path)
+{
+ GtkWidgetPath *new_path;
+ guint i;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ new_path = gtk_widget_path_new ();
+
+ for (i = 0; i < path->elems->len; i++)
+ {
+ GtkPathElement *elem, new = { 0 };
+
+ elem = &g_array_index (path->elems, GtkPathElement, i);
+
+ new.type = elem->type;
+ new.name = elem->name;
+
+ if (elem->regions)
+ {
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, elem->regions);
+ new.regions = g_hash_table_new (NULL, NULL);
+
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ g_hash_table_insert (new.regions, key, value);
+ }
+
+ if (elem->classes)
+ {
+ new.classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
+ g_array_append_vals (new.classes, elem->classes->data, elem->classes->len);
+ }
+
+ g_array_append_val (new_path->elems, new);
+ }
+
+ return new_path;
+}
+
+/**
+ * gtk_widget_path_free:
+ * @path: a #GtkWidgetPath
+ *
+ * Frees a #GtkWidgetPath.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_path_free (GtkWidgetPath *path)
+{
+ guint i;
+
+ g_return_if_fail (path != NULL);
+
+ for (i = 0; i < path->elems->len; i++)
+ {
+ GtkPathElement *elem;
+
+ elem = &g_array_index (path->elems, GtkPathElement, i);
+
+ if (elem->regions)
+ g_hash_table_destroy (elem->regions);
+
+ if (elem->classes)
+ g_array_free (elem->classes, TRUE);
+ }
+
+ g_array_free (path->elems, TRUE);
+ g_slice_free (GtkWidgetPath, path);
+}
+
+/**
+ * gtk_widget_path_length:
+ * @path: a #GtkWidgetPath
+ *
+ * Returns the number of #GtkWidget #GTypes between the represented
+ * widget and its topmost container.
+ *
+ * Returns: the number of elements in the path
+ *
+ * Since: 3.0
+ **/
+gint
+gtk_widget_path_length (const GtkWidgetPath *path)
+{
+ g_return_val_if_fail (path != NULL, 0);
+
+ return path->elems->len;
+}
+
+/**
+ * gtk_widget_path_prepend_type:
+ * @path: a #GtkWidgetPath
+ * @type: widget type to prepend
+ *
+ * Prepends a widget type to the widget hierachy represented by @path.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_path_prepend_type (GtkWidgetPath *path,
+ GType type)
+{
+ GtkPathElement new = { 0 };
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET));
+
+ new.type = type;
+ g_array_prepend_val (path->elems, new);
+}
+
+/**
+ * gtk_widget_path_append_type:
+ * @path: a #GtkWidgetPath
+ * @type: widget type to append
+ *
+ * Appends a widget type to the widget hierachy represented by @path.
+ *
+ * Returns: the position where the element was inserted
+ *
+ * Since: 3.0
+ **/
+gint
+gtk_widget_path_append_type (GtkWidgetPath *path,
+ GType type)
+{
+ GtkPathElement new = { 0 };
+
+ g_return_val_if_fail (path != NULL, 0);
+ g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), 0);
+
+ new.type = type;
+ g_array_append_val (path->elems, new);
+
+ return path->elems->len - 1;
+}
+
+/**
+ * gtk_widget_path_iter_get_widget_type:
+ * @path: a #GtkWidgetPath
+ * @pos: position to get the widget type for, -1 for the path head
+ *
+ * Returns the widget #GType that is at position @pos in the widget
+ * hierarchy defined in @path.
+ *
+ * Returns: a widget type
+ *
+ * Since: 3.0
+ **/
+GType
+gtk_widget_path_iter_get_widget_type (const GtkWidgetPath *path,
+ gint pos)
+{
+ GtkPathElement *elem;
+
+ g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
+ g_return_val_if_fail (path->elems->len != 0, G_TYPE_INVALID);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+ return elem->type;
+}
+
+/**
+ * gtk_widget_path_iter_set_widget_type:
+ * @path: a #GtkWidgetPath
+ * @pos: position to modify, -1 for the path head
+ * @type: widget type to set
+ *
+ * Sets the widget type for a given position in the widget hierarchy
+ * defined by @path. @type must be a #GtkWidget derived #GType.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_path_iter_set_widget_type (GtkWidgetPath *path,
+ gint pos,
+ GType type)
+{
+ GtkPathElement *elem;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (path->elems->len != 0);
+ g_return_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET));
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+ elem->type = type;
+}
+
+/**
+ * gtk_widget_path_iter_get_name:
+ * @path: a #GtkWidgetPath
+ * @pos: position to get the widget name for, -1 for the path head
+ *
+ * Returns the name corresponding to the widget found at
+ * the position @pos in the widget hierarchy defined by
+ * @path
+ *
+ * Returns: The widget name, or %NULL if none was set.
+ **/
+G_CONST_RETURN gchar *
+gtk_widget_path_iter_get_name (const GtkWidgetPath *path,
+ gint pos)
+{
+ GtkPathElement *elem;
+
+ g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (path->elems->len != 0, NULL);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+ return g_quark_to_string (elem->name);
+}
+
+/**
+ * gtk_widget_path_iter_set_name:
+ * @path: a #GtkWidgetPath
+ * @pos: position to modify, -1 for the path head
+ * @name: widget name
+ *
+ * Sets the widget name for the widget found at position @pos
+ * in the widget hierarchy defined by @path.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_path_iter_set_name (GtkWidgetPath *path,
+ gint pos,
+ const gchar *name)
+{
+ GtkPathElement *elem;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (path->elems->len != 0);
+ g_return_if_fail (name != NULL);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+
+ elem->name = g_quark_from_string (name);
+}
+
+/**
+ * gtk_widget_path_iter_has_qname:
+ * @path: a #GtkWidgetPath
+ * @pos: position to query, -1 for the path head
+ * @qname: widget name as a #GQuark
+ *
+ * See gtk_widget_path_iter_has_name(). This is a version
+ * that operates on #GQuark<!-- -->s.
+ *
+ * Returns: %TRUE if the widget at @pos has this name
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_widget_path_iter_has_qname (const GtkWidgetPath *path,
+ gint pos,
+ GQuark qname)
+{
+ GtkPathElement *elem;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+ g_return_val_if_fail (path->elems->len != 0, FALSE);
+ g_return_val_if_fail (qname != 0, FALSE);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+
+ return (elem->name == qname);
+}
+
+/**
+ * gtk_widget_path_iter_has_name:
+ * @path: a #GtkWidgetPath
+ * @pos: position to query, -1 for the path head
+ * @name: a widget name
+ *
+ * Returns %TRUE if the widget at position @pos has the name @name,
+ * %FALSE otherwise.
+ *
+ * Returns: %TRUE if the widget at @pos has this name
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_widget_path_iter_has_name (const GtkWidgetPath *path,
+ gint pos,
+ const gchar *name)
+{
+ GQuark qname;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+ g_return_val_if_fail (path->elems->len != 0, FALSE);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ qname = g_quark_try_string (name);
+
+ if (qname == 0)
+ return FALSE;
+
+ return gtk_widget_path_iter_has_qname (path, pos, qname);
+}
+
+/**
+ * gtk_widget_path_iter_add_class:
+ * @path: a #GtkWidget
+ * @pos: position to modify, -1 for the path head
+ * @name: a class name
+ *
+ * Adds the class @name to the widget at position @pos in
+ * the hierarchy defined in @path. See
+ * gtk_style_context_add_class().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_path_iter_add_class (GtkWidgetPath *path,
+ gint pos,
+ const gchar *name)
+{
+ GtkPathElement *elem;
+ gboolean added = FALSE;
+ GQuark qname;
+ guint i;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (path->elems->len != 0);
+ g_return_if_fail (name != NULL);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+ qname = g_quark_from_string (name);
+
+ if (!elem->classes)
+ elem->classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
+
+ for (i = 0; i < elem->classes->len; i++)
+ {
+ GQuark quark;
+
+ quark = g_array_index (elem->classes, GQuark, i);
+
+ if (qname == quark)
+ {
+ /* Already there */
+ added = TRUE;
+ break;
+ }
+ if (qname < quark)
+ {
+ g_array_insert_val (elem->classes, i, qname);
+ added = TRUE;
+ break;
+ }
+ }
+
+ if (!added)
+ g_array_append_val (elem->classes, qname);
+}
+
+/**
+ * gtk_widget_path_iter_remove_class:
+ * @path: a #GtkWidgetPath
+ * @pos: position to modify, -1 for the path head
+ * @name: class name
+ *
+ * Removes the class @name from the widget at position @pos in
+ * the hierarchy defined in @path.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_path_iter_remove_class (GtkWidgetPath *path,
+ gint pos,
+ const gchar *name)
+{
+ GtkPathElement *elem;
+ GQuark qname;
+ guint i;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (path->elems->len != 0);
+ g_return_if_fail (name != NULL);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ qname = g_quark_try_string (name);
+
+ if (qname == 0)
+ return;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+
+ if (!elem->classes)
+ return;
+
+ for (i = 0; i < elem->classes->len; i++)
+ {
+ GQuark quark;
+
+ quark = g_array_index (elem->classes, GQuark, i);
+
+ if (quark > qname)
+ break;
+ else if (quark == qname)
+ {
+ g_array_remove_index (elem->classes, i);
+ break;
+ }
+ }
+}
+
+/**
+ * gtk_widget_path_iter_clear_classes:
+ * @path: a #GtkWidget
+ * @pos: position to modify, -1 for the path head
+ *
+ * Removes all classes from the widget at position @pos in the
+ * hierarchy defined in @path.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_path_iter_clear_classes (GtkWidgetPath *path,
+ gint pos)
+{
+ GtkPathElement *elem;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (path->elems->len != 0);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+
+ if (!elem->classes)
+ return;
+
+ if (elem->classes->len > 0)
+ g_array_remove_range (elem->classes, 0, elem->classes->len);
+}
+
+/**
+ * gtk_widget_path_iter_list_classes:
+ * @path: a #GtkWidgetPath
+ * @pos: position to query, -1 for the path head
+ *
+ * Returns a list with all the class names defined for the widget
+ * at position @pos in the hierarchy defined in @path.
+ *
+ * Returns: (transfer container) (type utf8): The list of classes,
+ * This is a list of strings, the #GSList contents are
+ * owned by GTK+, but you should use g_slist_free() to
+ * free the list itself.
+ *
+ * Since: 3.0
+ **/
+GSList *
+gtk_widget_path_iter_list_classes (const GtkWidgetPath *path,
+ gint pos)
+{
+ GtkPathElement *elem;
+ GSList *list = NULL;
+ guint i;
+
+ g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (path->elems->len != 0, NULL);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+
+ if (!elem->classes)
+ return NULL;
+
+ for (i = 0; i < elem->classes->len; i++)
+ {
+ GQuark quark;
+
+ quark = g_array_index (elem->classes, GQuark, i);
+ list = g_slist_prepend (list, (gchar *) g_quark_to_string (quark));
+ }
+
+ return g_slist_reverse (list);
+}
+
+/**
+ * gtk_widget_path_iter_has_qclass:
+ * @path: a #GtkWidgetPath
+ * @pos: position to query, -1 for the path head
+ * @qname: class name as a #GQuark
+ *
+ * See gtk_widget_path_iter_has_class(). This is a version that operates
+ * with GQuark<!-- -->s.
+ *
+ * Returns: %TRUE if the widget at @pos has the class defined.
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_widget_path_iter_has_qclass (const GtkWidgetPath *path,
+ gint pos,
+ GQuark qname)
+{
+ GtkPathElement *elem;
+ guint i;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+ g_return_val_if_fail (path->elems->len != 0, FALSE);
+ g_return_val_if_fail (qname != 0, FALSE);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+
+ if (!elem->classes)
+ return FALSE;
+
+ for (i = 0; i < elem->classes->len; i++)
+ {
+ GQuark quark;
+
+ quark = g_array_index (elem->classes, GQuark, i);
+
+ if (quark == qname)
+ return TRUE;
+ else if (quark > qname)
+ break;
+ }
+
+ return FALSE;
+}
+
+/**
+ * gtk_widget_path_iter_has_class:
+ * @path: a #GtkWidgetPath
+ * @pos: position to query, -1 for the path head
+ * @name: class name
+ *
+ * Returns %TRUE if the widget at position @pos has the class @name
+ * defined, %FALSE otherwise.
+ *
+ * Returns: %TRUE if the class @name is defined for the widget at @pos
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_widget_path_iter_has_class (const GtkWidgetPath *path,
+ gint pos,
+ const gchar *name)
+{
+ GQuark qname;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+ g_return_val_if_fail (path->elems->len != 0, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ qname = g_quark_try_string (name);
+
+ if (qname == 0)
+ return FALSE;
+
+ return gtk_widget_path_iter_has_qclass (path, pos, qname);
+}
+
+/**
+ * gtk_widget_path_iter_add_region:
+ * @path: a #GtkWidgetPath
+ * @pos: position to modify, -1 for the path head
+ * @name: region name
+ * @flags: flags affecting the region
+ *
+ * Adds the region @name to the widget at position @pos in
+ * the hierarchy defined in @path. See
+ * gtk_style_context_add_region().
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_path_iter_add_region (GtkWidgetPath *path,
+ gint pos,
+ const gchar *name,
+ GtkRegionFlags flags)
+{
+ GtkPathElement *elem;
+ GQuark qname;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (path->elems->len != 0);
+ g_return_if_fail (name != NULL);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+ qname = g_quark_from_string (name);
+
+ if (!elem->regions)
+ elem->regions = g_hash_table_new (NULL, NULL);
+
+ g_hash_table_insert (elem->regions,
+ GUINT_TO_POINTER (qname),
+ GUINT_TO_POINTER (flags));
+}
+
+/**
+ * gtk_widget_path_iter_remove_region:
+ * @path: a #GtkWidgetPath
+ * @pos: position to modify, -1 for the path head
+ * @name: region name
+ *
+ * Removes the region @name from the widget at position @pos in
+ * the hierarchy defined in @path.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_path_iter_remove_region (GtkWidgetPath *path,
+ gint pos,
+ const gchar *name)
+{
+ GtkPathElement *elem;
+ GQuark qname;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (path->elems->len != 0);
+ g_return_if_fail (name != NULL);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ qname = g_quark_try_string (name);
+
+ if (qname == 0)
+ return;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+
+ if (elem->regions)
+ g_hash_table_remove (elem->regions, GUINT_TO_POINTER (qname));
+}
+
+/**
+ * gtk_widget_path_iter_clear_regions:
+ * @path: a #GtkWidgetPath
+ * @pos: position to modify, -1 for the path head
+ *
+ * Removes all regions from the widget at position @pos in the
+ * hierarchy defined in @path.
+ *
+ * Since: 3.0
+ **/
+void
+gtk_widget_path_iter_clear_regions (GtkWidgetPath *path,
+ gint pos)
+{
+ GtkPathElement *elem;
+
+ g_return_if_fail (path != NULL);
+ g_return_if_fail (path->elems->len != 0);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+
+ if (elem->regions)
+ g_hash_table_remove_all (elem->regions);
+}
+
+/**
+ * gtk_widget_path_iter_list_regions:
+ * @path: a #GtkWidgetPath
+ * @pos: position to query, -1 for the path head
+ *
+ * Returns a list with all the region names defined for the widget
+ * at position @pos in the hierarchy defined in @path.
+ *
+ * Returns: (transfer container) (type utf8): The list of regions,
+ * This is a list of strings, the #GSList contents are
+ * owned by GTK+, but you should use g_slist_free() to
+ * free the list itself.
+ *
+ * Since: 3.0
+ **/
+GSList *
+gtk_widget_path_iter_list_regions (const GtkWidgetPath *path,
+ gint pos)
+{
+ GtkPathElement *elem;
+ GHashTableIter iter;
+ GSList *list = NULL;
+ gpointer key;
+
+ g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (path->elems->len != 0, NULL);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+
+ if (!elem->regions)
+ return NULL;
+
+ g_hash_table_iter_init (&iter, elem->regions);
+
+ while (g_hash_table_iter_next (&iter, &key, NULL))
+ {
+ GQuark qname;
+
+ qname = GPOINTER_TO_UINT (key);
+ list = g_slist_prepend (list, (gchar *) g_quark_to_string (qname));
+ }
+
+ return list;
+}
+
+/**
+ * gtk_widget_path_iter_has_qregion:
+ * @path: a #GtkWidgetPath
+ * @pos: position to query, -1 for the path head
+ * @qname: region name as a #GQuark
+ * @flags: (out): return location for the region flags
+ *
+ * See gtk_widget_path_iter_has_region(). This is a version that operates
+ * with GQuark<!-- -->s.
+ *
+ * Returns: %TRUE if the widget at @pos has the region defined.
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_widget_path_iter_has_qregion (const GtkWidgetPath *path,
+ gint pos,
+ GQuark qname,
+ GtkRegionFlags *flags)
+{
+ GtkPathElement *elem;
+ gpointer value;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+ g_return_val_if_fail (path->elems->len != 0, FALSE);
+ g_return_val_if_fail (qname != 0, FALSE);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ elem = &g_array_index (path->elems, GtkPathElement, pos);
+
+ if (!elem->regions)
+ return FALSE;
+
+ if (!g_hash_table_lookup_extended (elem->regions,
+ GUINT_TO_POINTER (qname),
+ NULL, &value))
+ return FALSE;
+
+ if (flags)
+ *flags = GPOINTER_TO_UINT (value);
+
+ return TRUE;
+}
+
+/**
+ * gtk_widget_path_iter_has_region:
+ * @path: a #GtkWidgetPath
+ * @pos: position to query, -1 for the path head
+ * @name: region name
+ * @flags: (out): return location for the region flags
+ *
+ * Returns %TRUE if the widget at position @pos has the class @name
+ * defined, %FALSE otherwise.
+ *
+ * Returns: %TRUE if the class @name is defined for the widget at @pos
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_widget_path_iter_has_region (const GtkWidgetPath *path,
+ gint pos,
+ const gchar *name,
+ GtkRegionFlags *flags)
+{
+ GQuark qname;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+ g_return_val_if_fail (path->elems->len != 0, FALSE);
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ if (pos < 0 || pos > path->elems->len)
+ pos = path->elems->len - 1;
+
+ qname = g_quark_try_string (name);
+
+ if (qname == 0)
+ return FALSE;
+
+ return gtk_widget_path_iter_has_qregion (path, pos, qname, flags);
+}
+
+/**
+ * gtk_widget_path_get_widget_type:
+ * @path: a #GtkWidget
+ *
+ * Returns the topmost widget type, that is, the widget type this path
+ * is representing.
+ *
+ * Returns: The widget type
+ *
+ * Since: 3.0
+ **/
+GType
+gtk_widget_path_get_widget_type (const GtkWidgetPath *path)
+{
+ GtkPathElement *elem;
+
+ g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
+
+ elem = &g_array_index (path->elems, GtkPathElement,
+ path->elems->len - 1);
+ return elem->type;
+}
+
+/**
+ * gtk_widget_path_is_type:
+ * @path: a #GtkWidgetPath
+ * @type: widget type to match
+ *
+ * Returns %TRUE if the widget type represented by this path
+ * is @type, or a subtype of it.
+ *
+ * Returns: %TRUE if the widget represented by @path is of type @type
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_widget_path_is_type (const GtkWidgetPath *path,
+ GType type)
+{
+ GtkPathElement *elem;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+ g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), FALSE);
+
+ elem = &g_array_index (path->elems, GtkPathElement,
+ path->elems->len - 1);
+
+ if (elem->type == type ||
+ g_type_is_a (elem->type, type))
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
+ * gtk_widget_path_has_parent:
+ * @path: a #GtkWidgetPath
+ * @type: widget type to check in parents
+ *
+ * Returns %TRUE if any of the parents of the widget represented
+ * in @path is of type @type, or any subtype of it.
+ *
+ * Returns: %TRUE if any parent is of type @type
+ *
+ * Since: 3.0
+ **/
+gboolean
+gtk_widget_path_has_parent (const GtkWidgetPath *path,
+ GType type)
+{
+ guint i;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+ g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), FALSE);
+
+ for (i = 0; i < path->elems->len - 1; i++)
+ {
+ GtkPathElement *elem;
+
+ elem = &g_array_index (path->elems, GtkPathElement, i);
+
+ if (elem->type == type ||
+ g_type_is_a (elem->type, type))
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/gtk/gtkwidgetpath.h b/gtk/gtkwidgetpath.h
new file mode 100644
index 0000000000..801906c68f
--- /dev/null
+++ b/gtk/gtkwidgetpath.h
@@ -0,0 +1,113 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_WIDGET_PATH_H__
+#define __GTK_WIDGET_PATH_H__
+
+#include <glib-object.h>
+#include "gtkenums.h"
+
+G_BEGIN_DECLS
+
+typedef struct _GtkWidgetPath GtkWidgetPath;
+
+
+GtkWidgetPath * gtk_widget_path_new (void);
+
+GtkWidgetPath * gtk_widget_path_copy (const GtkWidgetPath *path);
+void gtk_widget_path_free (GtkWidgetPath *path);
+
+gint gtk_widget_path_length (const GtkWidgetPath *path);
+
+gint gtk_widget_path_append_type (GtkWidgetPath *path,
+ GType type);
+void gtk_widget_path_prepend_type (GtkWidgetPath *path,
+ GType type);
+
+GType gtk_widget_path_iter_get_widget_type (const GtkWidgetPath *path,
+ gint pos);
+void gtk_widget_path_iter_set_widget_type (GtkWidgetPath *path,
+ gint pos,
+ GType type);
+
+G_CONST_RETURN gchar * gtk_widget_path_iter_get_name (const GtkWidgetPath *path,
+ gint pos);
+void gtk_widget_path_iter_set_name (GtkWidgetPath *path,
+ gint pos,
+ const gchar *name);
+gboolean gtk_widget_path_iter_has_name (const GtkWidgetPath *path,
+ gint pos,
+ const gchar *name);
+gboolean gtk_widget_path_iter_has_qname (const GtkWidgetPath *path,
+ gint pos,
+ GQuark qname);
+
+void gtk_widget_path_iter_add_class (GtkWidgetPath *path,
+ gint pos,
+ const gchar *name);
+void gtk_widget_path_iter_remove_class (GtkWidgetPath *path,
+ gint pos,
+ const gchar *name);
+void gtk_widget_path_iter_clear_classes (GtkWidgetPath *path,
+ gint pos);
+GSList * gtk_widget_path_iter_list_classes (const GtkWidgetPath *path,
+ gint pos);
+gboolean gtk_widget_path_iter_has_class (const GtkWidgetPath *path,
+ gint pos,
+ const gchar *name);
+gboolean gtk_widget_path_iter_has_qclass (const GtkWidgetPath *path,
+ gint pos,
+ GQuark qname);
+
+void gtk_widget_path_iter_add_region (GtkWidgetPath *path,
+ gint pos,
+ const gchar *name,
+ GtkRegionFlags flags);
+void gtk_widget_path_iter_remove_region (GtkWidgetPath *path,
+ gint pos,
+ const gchar *name);
+void gtk_widget_path_iter_clear_regions (GtkWidgetPath *path,
+ gint pos);
+
+GSList * gtk_widget_path_iter_list_regions (const GtkWidgetPath *path,
+ gint pos);
+
+gboolean gtk_widget_path_iter_has_region (const GtkWidgetPath *path,
+ gint pos,
+ const gchar *name,
+ GtkRegionFlags *flags);
+gboolean gtk_widget_path_iter_has_qregion (const GtkWidgetPath *path,
+ gint pos,
+ GQuark qname,
+ GtkRegionFlags *flags);
+
+GType gtk_widget_path_get_widget_type (const GtkWidgetPath *path);
+
+gboolean gtk_widget_path_is_type (const GtkWidgetPath *path,
+ GType type);
+gboolean gtk_widget_path_has_parent (const GtkWidgetPath *path,
+ GType type);
+
+G_END_DECLS
+
+#endif /* __GTK_WIDGET_PATH_H__ */
diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c
index 1f7134e646..31f46a56cd 100644
--- a/gtk/gtkwindow.c
+++ b/gtk/gtkwindow.c
@@ -345,8 +345,7 @@ static gint gtk_window_focus_in_event (GtkWidget *widget,
GdkEventFocus *event);
static gint gtk_window_focus_out_event (GtkWidget *widget,
GdkEventFocus *event);
-static void gtk_window_style_set (GtkWidget *widget,
- GtkStyle *style);
+static void gtk_window_style_updated (GtkWidget *widget);
static gint gtk_window_client_event (GtkWidget *widget,
GdkEventClient *event);
static gboolean gtk_window_state_event (GtkWidget *widget,
@@ -595,7 +594,7 @@ gtk_window_class_init (GtkWindowClass *klass)
widget_class->window_state_event = gtk_window_state_event;
widget_class->direction_changed = gtk_window_direction_changed;
widget_class->state_changed = gtk_window_state_changed;
- widget_class->style_set = gtk_window_style_set;
+ widget_class->style_updated = gtk_window_style_updated;
container_class->check_resize = gtk_window_check_resize;
@@ -4879,12 +4878,12 @@ gtk_window_realize (GtkWidget *widget)
{
GtkAllocation allocation;
GtkWindow *window;
- GtkStyle *style;
GdkWindow *parent_window;
GdkWindow *gdk_window;
GdkWindowAttr attributes;
gint attributes_mask;
GtkWindowPrivate *priv;
+ GtkStyleContext *context;
window = GTK_WINDOW (widget);
priv = window->priv;
@@ -5012,10 +5011,11 @@ gtk_window_realize (GtkWidget *widget)
gdk_window_set_user_data (gdk_window, window);
gtk_widget_style_attach (widget);
- style = gtk_widget_get_style (widget);
- gtk_style_set_background (style, gdk_window, GTK_STATE_NORMAL);
+ context = gtk_widget_get_style_context (widget);
+
+ gtk_style_context_set_background (context, gdk_window);
if (priv->frame)
- gtk_style_set_background (style, priv->frame, GTK_STATE_NORMAL);
+ gtk_style_context_set_background (context, priv->frame);
if (priv->transient_parent &&
gtk_widget_get_realized (GTK_WIDGET (priv->transient_parent)))
@@ -5113,12 +5113,13 @@ gtk_window_unrealize (GtkWidget *widget)
GTK_WIDGET_CLASS (gtk_window_parent_class)->unrealize (widget);
}
-static GdkWindowEdge
-get_grip_edge (GtkWidget *widget)
+static GtkJunctionSides
+get_grip_junction (GtkWidget *widget)
{
- return gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR
- ? GDK_WINDOW_EDGE_SOUTH_EAST
- : GDK_WINDOW_EDGE_SOUTH_WEST;
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ return GTK_JUNCTION_CORNER_BOTTOMRIGHT;
+ else
+ return GTK_JUNCTION_CORNER_BOTTOMLEFT;
}
static gboolean
@@ -5231,7 +5232,7 @@ set_grip_shape (GtkWindow *window)
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
cairo_paint (cr);
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 1.0);
- if (get_grip_edge (GTK_WIDGET (window)) == GDK_WINDOW_EDGE_SOUTH_EAST)
+ if (get_grip_junction (GTK_WIDGET (window)) & GTK_JUNCTION_CORNER_BOTTOMRIGHT)
{
cairo_move_to (cr, width, 0.0);
cairo_line_to (cr, width, height);
@@ -5461,8 +5462,7 @@ gtk_window_state_changed (GtkWidget *widget,
}
static void
-gtk_window_style_set (GtkWidget *widget,
- GtkStyle *style)
+gtk_window_style_updated (GtkWidget *widget)
{
GtkWindow *window = GTK_WINDOW (widget);
GtkWindowPrivate *priv = window->priv;
@@ -5681,7 +5681,6 @@ gtk_window_get_resize_grip_area (GtkWindow *window,
{
GtkWidget *widget = GTK_WIDGET (window);
GtkAllocation allocation;
- GtkStyle *style;
gint grip_width;
gint grip_height;
@@ -5691,7 +5690,6 @@ gtk_window_get_resize_grip_area (GtkWindow *window,
return FALSE;
gtk_widget_get_allocation (widget, &allocation);
- style = gtk_widget_get_style (widget);
gtk_widget_style_get (widget,
"resize-grip-width", &grip_width,
@@ -7432,16 +7430,30 @@ gtk_window_draw (GtkWidget *widget,
cairo_t *cr)
{
GtkWindowPrivate *priv = GTK_WINDOW (widget)->priv;
+ GtkStyleContext *context;
gboolean ret = FALSE;
+ context = gtk_widget_get_style_context (widget);
+
+ gtk_style_context_save (context);
+
if (!gtk_widget_get_app_paintable (widget))
- gtk_paint_flat_box (gtk_widget_get_style (widget),
- cr,
- GTK_STATE_NORMAL,
- GTK_SHADOW_NONE, widget, "base",
- 0, 0,
- gtk_widget_get_allocated_width (widget),
- gtk_widget_get_allocated_height (widget));
+ {
+ GtkStateFlags state;
+
+ state = gtk_widget_get_state_flags (widget);
+
+ if (gtk_window_has_toplevel_focus (GTK_WINDOW (widget)))
+ state |= GTK_STATE_FLAG_FOCUSED;
+
+ gtk_style_context_set_state (context, state);
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_BACKGROUND);
+ gtk_render_background (context, cr, 0, 0,
+ gtk_widget_get_allocated_width (widget),
+ gtk_widget_get_allocated_height (widget));
+ }
+
+ gtk_style_context_restore (context);
if (GTK_WIDGET_CLASS (gtk_window_parent_class)->draw)
ret = GTK_WIDGET_CLASS (gtk_window_parent_class)->draw (widget, cr);
@@ -7451,18 +7463,18 @@ gtk_window_draw (GtkWidget *widget,
{
GdkRectangle rect;
+ gtk_style_context_save (context);
cairo_save (cr);
+
gtk_cairo_transform_to_window (cr, widget, priv->grip_window);
gtk_window_get_resize_grip_area (GTK_WINDOW (widget), &rect);
- gtk_paint_resize_grip (gtk_widget_get_style (widget),
- cr,
- gtk_widget_get_state (widget),
- widget,
- "statusbar",
- get_grip_edge (widget),
- 0, 0,
- rect.width, rect.height);
+
+ gtk_style_context_add_class (context, GTK_STYLE_CLASS_GRIP);
+ gtk_style_context_set_junction_sides (context, get_grip_junction (widget));
+ gtk_render_handle (context, cr, 0, 0, rect.width, rect.height);
+
cairo_restore (cr);
+ gtk_style_context_restore (context);
}
return ret;
diff --git a/gtk/gtkxembed.c b/gtk/gtkxembed.c
index 7353e30cd3..5c900fc479 100644
--- a/gtk/gtkxembed.c
+++ b/gtk/gtkxembed.c
@@ -153,7 +153,7 @@ _gtk_xembed_send_message (GdkWindow *recipient,
g_message ("Sending %s", _gtk_xembed_message_name (message)));
memset (&xclient, 0, sizeof (xclient));
- xclient.window = GDK_WINDOW_XWINDOW (recipient);
+ xclient.window = GDK_WINDOW_XID (recipient);
xclient.type = ClientMessage;
xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED");
xclient.format = 32;
@@ -165,7 +165,7 @@ _gtk_xembed_send_message (GdkWindow *recipient,
gdk_error_trap_push ();
XSendEvent (GDK_WINDOW_XDISPLAY(recipient),
- GDK_WINDOW_XWINDOW (recipient),
+ GDK_WINDOW_XID (recipient),
False, NoEventMask, (XEvent *)&xclient);
gdk_error_trap_pop_ignored ();
}
diff --git a/gtk/tests/Makefile.am b/gtk/tests/Makefile.am
index e97a30f9cb..dda9727d49 100644
--- a/gtk/tests/Makefile.am
+++ b/gtk/tests/Makefile.am
@@ -50,8 +50,8 @@ floating_SOURCES = floating.c
floating_LDADD = $(progs_ldadd)
#TEST_PROGS += object
-object_SOURCES = object.c pixbuf-init.c
-object_LDADD = $(progs_ldadd)
+#object_SOURCES = object.c pixbuf-init.c
+#object_LDADD = $(progs_ldadd)
# this doesn't work in make distcheck, since running
# on a naked X server creates slightly different event
@@ -100,6 +100,11 @@ SAMPLE_PROGS = gtk-example-application
gtk_example_application_SOURCES = gtk-example-application.c
gtk_example_application_LDADD = $(progs_ldadd)
+TEST_PROGS += stylecontext
+stylecontext_SOURCES = stylecontext.c
+stylecontext_LDADD = $(progs_ldadd)
+EXTRA_DIST += test.css test.png
+
EXTRA_DIST += \
file-chooser-test-dir/empty \
diff --git a/gtk/tests/stylecontext.c b/gtk/tests/stylecontext.c
new file mode 100644
index 0000000000..20b7dadfc9
--- /dev/null
+++ b/gtk/tests/stylecontext.c
@@ -0,0 +1,534 @@
+#include <gtk/gtk.h>
+
+static void
+test_parse_empty (void)
+{
+ GtkCssProvider *provider;
+ GError *error;
+ gboolean res;
+
+ provider = gtk_css_provider_new ();
+ error = NULL;
+ res = gtk_css_provider_load_from_data (provider, "", -1, &error);
+
+ g_assert (res);
+ g_assert_no_error (error);
+ g_clear_error (&error);
+
+ g_object_unref (provider);
+}
+
+static void
+test_parse_at (void)
+{
+ GtkCssProvider *provider;
+ GError *error;
+ gboolean res;
+ gint i;
+ const gchar *valid[] = {
+ "@import \"test.css\";",
+ "@import 'test.css';",
+ "@import url(\"test.css\");",
+ "@import url('test.css');",
+ "@import\nurl (\t\"test.css\" ) ;",
+ "@define-color bg_color #f9a039;",
+ "@define-color color @bg_color;",
+ "@define-color color rgb(100, 99, 88);",
+ "@define-color color rgba(50%, 50%, 50%, 0.5);",
+ "@define-color color lighter(#f9a039);",
+ "@define-color color darker ( @blue ) ;",
+ "@define-color color shade(@blue, 1.3);",
+ "@define-color color alpha(@blue, 1.3);",
+ "@define-color color mix(@blue, @red, 0.2);",
+ "@define-color color red;",
+ "@define-color color mix(shade (#121212, 0.5), mix (rgb(10%,20%,100%), @blue,0.5), 0.2);",
+ "@define-color blue @blue;",
+ "@define-color blue123_a-b #123;",
+ NULL
+ };
+
+ const gchar *invalid[] = {
+ "@import test.css ;",
+ "@import url ( \"test.css\" xyz );",
+ "@import url(\");",
+ "@import url(');",
+ "@import url(\"abc');",
+ "@ import ;",
+ "@define_color blue red;",
+ "@define-color blue #12234;",
+ "@define-color blue #12g234;",
+ "@define-color blue @@;",
+ "@define-color blue 5!#%4@DG$##x;",
+ "@define-color color mix(@red, @blue, @green);",
+ "@define-color color mix(@blue, 0.2, @red);",
+ "@define-color color mix(0.2, @blue, @red);",
+ "@define-color color mix(@blue, @red);",
+ "@define-color color mix(@blue);",
+ "@define-color color mix();",
+ "@define-color color rgba(50%, 50%, 50%);",
+ "@define-color color rgb(50%, a);",
+ "@define-color 1col rgb(50%, a);",
+ "@three-dee { some other crap };",
+ NULL
+ };
+
+ error = NULL;
+ for (i = 0; valid[i]; i++)
+ {
+ provider = gtk_css_provider_new ();
+ res = gtk_css_provider_load_from_data (provider, valid[i], -1, &error);
+ g_assert_no_error (error);
+ g_assert (res);
+
+ g_object_unref (provider);
+ }
+
+ for (i = 0; invalid[i]; i++)
+ {
+ provider = gtk_css_provider_new ();
+ res = gtk_css_provider_load_from_data (provider, invalid[i], -1, &error);
+ g_assert_error (error, GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_FAILED);
+ g_assert (!res);
+ g_object_unref (provider);
+ g_clear_error (&error);
+ }
+}
+
+static void
+test_parse_selectors (void)
+{
+ GtkCssProvider *provider;
+ GError *error;
+ gboolean res;
+ gint i;
+ const gchar *valid[] = {
+ "* {}",
+ "E {}",
+ "E F {}",
+ "E > F {}",
+ "E#id {}",
+ "#id {}",
+ "tab:first-child {}",
+ "tab:last-child {}",
+ "tab:nth-child(first) {}",
+ "tab:nth-child(last) {}",
+ "tab:nth-child(even) {}",
+ "tab:nth-child(odd) {}",
+ "tab:sorted {}",
+ ".some-class {}",
+ ".some-class.another-class {}",
+ ".some-class .another-class {}",
+ "E * {}",
+ "E .class {}",
+ "E > .foo {}",
+ "E > #id {}",
+ "E:active {}",
+ "E:prelight {}",
+ "E:hover {}",
+ "E:selected {}",
+ "E:insensitive {}",
+ "E:inconsistent {}",
+ "E:focused {}",
+ "E:active:prelight {}",
+ "* > .notebook tab:first-child .label:focused {}",
+ "E, F {}",
+ "E, F /* comment here */ {}",
+ "E,/* comment here */ F {}",
+ "E1.e1_2 #T3_4 {}",
+ NULL
+ };
+
+ const gchar *invalid[] = {
+ /* nth-child and similar pseudo classes can only
+ * be used with regions, not with types
+ */
+ "E:first-child {}",
+ "E:last-child {}",
+ "E:nth-child(first) {}",
+ "E:nth-child(last) {}",
+ "E:nth-child(even) {}",
+ "E:nth-child(odd) {}",
+ "E:sorted {}",
+ /* widget state pseudo-classes can only be used for
+ * the last element
+ */
+ "E:focused tab {}",
+ NULL
+ };
+
+ error = NULL;
+ for (i = 0; valid[i]; i++)
+ {
+ provider = gtk_css_provider_new ();
+ res = gtk_css_provider_load_from_data (provider, valid[i], -1, &error);
+ g_assert_no_error (error);
+ g_assert (res);
+
+ g_object_unref (provider);
+ }
+
+ for (i = 0; invalid[i]; i++)
+ {
+ provider = gtk_css_provider_new ();
+ res = gtk_css_provider_load_from_data (provider, invalid[i], -1, &error);
+ g_assert_error (error, GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_FAILED);
+ g_assert (!res);
+ g_object_unref (provider);
+ g_clear_error (&error);
+ }
+}
+
+static void
+test_parse_declarations (void)
+{
+ GtkCssProvider *provider;
+ GError *error;
+ gboolean res;
+ gint i;
+ const gchar *valid[] = {
+ "* {}",
+ "* { font: Sans 15 }",
+ "* { font: Sans 15; }",
+ "* { font: bold }",
+ "* { color: red }",
+ "* { /* just a comment */ }",
+ "* { /* multi\nline\ncomment */ }",
+ "* { font: /* comment here */ Sans 15 }",
+ "* { color: red; background-color: shade (@bg_color, 0.5) }",
+ "* { margin: 5 }",
+ "* { margin: 5 10 }",
+ "* { margin: 5 10 3 }",
+ "* { margin: 5 10 3 5 }",
+ "* { padding: 5 }",
+ "* { padding: 5 10 }",
+ "* { border-width: 5; border-radius: 10 }",
+ "* { border-color: #ff00ff }",
+ "* { engine: clearlooks }",
+ "* { background-image: -gtk-gradient (linear, \n"
+ " left top, right top, \n"
+ " from (#fff), to (#000)) }",
+ "* { background-image: -gtk-gradient (linear, \n"
+ " 0.0 0.5, 0.5 1.0, \n"
+ " from (#fff), \n"
+ " color-stop (0.5, #f00),\n"
+ " to (#000)) }",
+ "* { background-image: -gtk-gradient (radial, \n"
+ " center center, 0.2, \n"
+ " center center, 0.8, \n"
+ " color-stop (0.0,#fff),\n"
+ " color-stop (1.0,#000))}\n",
+ "* { border-image: url (\"test.png\") 3 4 3 4 stretch }",
+ "* { border-image: url (\"test.png\") 3 4 3 4 repeat stretch}",
+ "* { transition: 150ms ease-in-out }",
+ "* { transition: 1s linear loop }",
+ NULL
+ };
+
+ const gchar *invalid[] = {
+ "* { color }",
+ "* { color:green; color }",
+ "* { color:red; color; color:green }",
+ "* { color:green; color: }",
+ "* { color:red; color:; color:green }",
+ "* { color:green; color{;color:maroon} }",
+ "* { color:red; color{;color:maroon}; color:green }",
+ "* { content: 'Hello",
+ NULL
+ };
+
+ error = NULL;
+ for (i = 0; valid[i]; i++)
+ {
+ provider = gtk_css_provider_new ();
+ res = gtk_css_provider_load_from_data (provider, valid[i], -1, &error);
+ g_assert_no_error (error);
+ g_assert (res);
+
+ g_object_unref (provider);
+ }
+
+ for (i = 0; invalid[i]; i++)
+ {
+ provider = gtk_css_provider_new ();
+ res = gtk_css_provider_load_from_data (provider, invalid[i], -1, &error);
+ g_assert_error (error, GTK_CSS_PROVIDER_ERROR, GTK_CSS_PROVIDER_ERROR_FAILED);
+ g_assert (!res);
+ g_object_unref (provider);
+ g_clear_error (&error);
+ }
+}
+
+static void
+test_path (void)
+{
+ GtkWidgetPath *path;
+ GtkWidgetPath *path2;
+ gint pos;
+ GtkRegionFlags flags;
+
+ path = gtk_widget_path_new ();
+ g_assert_cmpint (gtk_widget_path_length (path), ==, 0);
+
+ pos = gtk_widget_path_append_type (path, GTK_TYPE_WINDOW);
+ g_assert_cmpint (pos, ==, 0);
+ g_assert_cmpint (gtk_widget_path_length (path), ==, 1);
+ g_assert (gtk_widget_path_iter_get_widget_type (path, 0) == GTK_TYPE_WINDOW);
+ g_assert (gtk_widget_path_is_type (path, GTK_TYPE_WIDGET));
+ g_assert (gtk_widget_path_iter_get_name (path, 0) == NULL);
+
+ pos = gtk_widget_path_append_type (path, GTK_TYPE_WIDGET);
+ g_assert_cmpint (pos, ==, 1);
+ g_assert_cmpint (gtk_widget_path_length (path), ==, 2);
+ gtk_widget_path_iter_set_widget_type (path, pos, GTK_TYPE_BUTTON);
+ g_assert (gtk_widget_path_is_type (path, GTK_TYPE_BUTTON));
+ g_assert (gtk_widget_path_has_parent (path, GTK_TYPE_WIDGET));
+ g_assert (gtk_widget_path_has_parent (path, GTK_TYPE_WINDOW));
+ g_assert (!gtk_widget_path_has_parent (path, GTK_TYPE_DIALOG));
+ g_assert (gtk_widget_path_iter_get_name (path, 1) == NULL);
+
+ gtk_widget_path_iter_set_name (path, 1, "name");
+ g_assert (gtk_widget_path_iter_has_name (path, 1, "name"));
+
+ gtk_widget_path_iter_add_class (path, 1, "class1");
+ gtk_widget_path_iter_add_class (path, 1, "class2");
+ g_assert (gtk_widget_path_iter_has_class (path, 1, "class1"));
+ g_assert (gtk_widget_path_iter_has_class (path, 1, "class2"));
+ g_assert (!gtk_widget_path_iter_has_class (path, 1, "class3"));
+
+ path2 = gtk_widget_path_copy (path);
+ g_assert (gtk_widget_path_iter_has_class (path2, 1, "class1"));
+ g_assert (gtk_widget_path_iter_has_class (path2, 1, "class2"));
+ g_assert (!gtk_widget_path_iter_has_class (path2, 1, "class3"));
+ gtk_widget_path_free (path2);
+
+ gtk_widget_path_iter_remove_class (path, 1, "class2");
+ g_assert (gtk_widget_path_iter_has_class (path, 1, "class1"));
+ g_assert (!gtk_widget_path_iter_has_class (path, 1, "class2"));
+ gtk_widget_path_iter_clear_classes (path, 1);
+ g_assert (!gtk_widget_path_iter_has_class (path, 1, "class1"));
+
+ gtk_widget_path_iter_add_region (path, 1, "tab", 0);
+ gtk_widget_path_iter_add_region (path, 1, "title", GTK_REGION_EVEN | GTK_REGION_FIRST);
+
+ g_assert (gtk_widget_path_iter_has_region (path, 1, "tab", &flags) &&
+ flags == 0);
+ g_assert (gtk_widget_path_iter_has_region (path, 1, "title", &flags) &&
+ flags == (GTK_REGION_EVEN | GTK_REGION_FIRST));
+ g_assert (!gtk_widget_path_iter_has_region (path, 1, "extension", NULL));
+
+ path2 = gtk_widget_path_copy (path);
+ g_assert (gtk_widget_path_iter_has_region (path2, 1, "tab", &flags) &&
+ flags == 0);
+ g_assert (gtk_widget_path_iter_has_region (path2, 1, "title", &flags) &&
+ flags == (GTK_REGION_EVEN | GTK_REGION_FIRST));
+ g_assert (!gtk_widget_path_iter_has_region (path2, 1, "extension", NULL));
+ gtk_widget_path_free (path2);
+
+ gtk_widget_path_free (path);
+}
+
+static void
+test_match (void)
+{
+ GtkStyleContext *context;
+ GtkWidgetPath *path;
+ GtkCssProvider *provider;
+ GError *error;
+ const gchar *data;
+ GdkRGBA *color;
+ GdkRGBA expected;
+
+ error = NULL;
+ provider = gtk_css_provider_new ();
+
+ gdk_rgba_parse (&expected, "#fff");
+
+ context = gtk_style_context_new ();
+
+ path = gtk_widget_path_new ();
+ gtk_widget_path_append_type (path, GTK_TYPE_WINDOW);
+ gtk_widget_path_append_type (path, GTK_TYPE_BOX);
+ gtk_widget_path_append_type (path, GTK_TYPE_BUTTON);
+ gtk_widget_path_iter_set_name (path, 0, "mywindow");
+ gtk_widget_path_iter_add_class (path, 2, "button");
+ gtk_style_context_set_path (context, path);
+ gtk_widget_path_free (path);
+
+ gtk_style_context_add_provider (context,
+ GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+
+ data = "* { color: #fff }";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_invalidate (context);
+ gtk_style_context_get (context, 0, "color", &color, NULL);
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ data = "* { color: #f00 }\n"
+ "GtkButton { color: #fff }";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_invalidate (context);
+ gtk_style_context_get (context, 0, "color", &color, NULL);
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ data = "* { color: #f00 }\n"
+ "GtkButton { color: #fff }\n"
+ "GtkWindow > GtkButton { color: #000 }";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_invalidate (context);
+ gtk_style_context_get (context, 0, "color", &color, NULL);
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ data = "* { color: #f00 }\n"
+ ".button { color: #fff }";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_invalidate (context);
+ gtk_style_context_get (context, 0, "color", &color, NULL);
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ data = "* { color: #f00 }\n"
+ "GtkButton { color: #000 }\n"
+ ".button { color: #fff }";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_invalidate (context);
+ gtk_style_context_get (context, 0, "color", &color, NULL);
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ data = "* { color: #f00 }\n"
+ "GtkButton { color: #000 }\n"
+ "GtkWindow GtkButton { color: #fff }";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_invalidate (context);
+ gtk_style_context_get (context, 0, "color", &color, NULL);
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ data = "* { color: #f00 }\n"
+ ".button { color: #000 }\n"
+ "GtkWindow .button { color: #fff }";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_invalidate (context);
+ gtk_style_context_get (context, 0, "color", &color, NULL);
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ data = "* { color: #f00 }\n"
+ "* .button { color: #000 }\n"
+ "#mywindow .button { color: #fff }";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_invalidate (context);
+ gtk_style_context_get (context, 0, "color", &color, NULL);
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ data = "* { color: #f00 }\n"
+ "GtkWindow .button { color: #000 }\n"
+ "GtkWindow#mywindow .button { color: #fff }";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_invalidate (context);
+ gtk_style_context_get (context, 0, "color", &color, NULL);
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ data = "* { color: #f00 }\n"
+ "GtkWindow .button { color: #fff }\n"
+ "GObject .button { color: #000 }";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_invalidate (context);
+ gtk_style_context_get (context, 0, "color", &color, NULL);
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ g_object_unref (provider);
+ g_object_unref (context);
+}
+
+static void
+test_style_property (void)
+{
+ GtkStyleContext *context;
+ GtkWidgetPath *path;
+ GtkCssProvider *provider;
+ GError *error;
+ const gchar *data;
+ gint x;
+ GdkRGBA *color;
+ GdkRGBA expected;
+
+ error = NULL;
+ provider = gtk_css_provider_new ();
+
+ context = gtk_style_context_new ();
+
+ path = gtk_widget_path_new ();
+ gtk_widget_path_append_type (path, GTK_TYPE_WINDOW);
+ gtk_widget_path_append_type (path, GTK_TYPE_BOX);
+ gtk_widget_path_append_type (path, GTK_TYPE_BUTTON);
+ gtk_style_context_set_path (context, path);
+ gtk_widget_path_free (path);
+ gtk_style_context_set_state (context, GTK_STATE_FLAG_PRELIGHT);
+
+ /* Since we set the prelight state on the context, we expect
+ * only the third selector to match, even though the second one
+ * has higher specificity, and the fourth one comes later.
+ *
+ * In particular, we want to verify that widget style properties and
+ * CSS properties follow the same matching rules, ie we expect
+ * color to be #003 and child-displacement-x to be 3.
+ */
+ data = "GtkButton:insensitive { color: #001; -GtkButton-child-displacement-x: 1 }\n"
+ "GtkBox GtkButton:selected { color: #002; -GtkButton-child-displacement-x: 2 }\n"
+ "GtkButton:prelight { color: #003; -GtkButton-child-displacement-x: 3 }\n"
+ "GtkButton:focused { color: #004; -GtkButton-child-displacement-x: 4 }\n";
+ gtk_css_provider_load_from_data (provider, data, -1, &error);
+ g_assert_no_error (error);
+ gtk_style_context_add_provider (context,
+ GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_USER);
+
+ gtk_style_context_invalidate (context);
+
+ gtk_style_context_get (context, GTK_STATE_FLAG_PRELIGHT, "color", &color, NULL);
+ gdk_rgba_parse (&expected, "#003");
+ g_assert (gdk_rgba_equal (color, &expected));
+ gdk_rgba_free (color);
+
+ gtk_style_context_get_style (context, "child-displacement-x", &x, NULL);
+
+ g_assert_cmpint (x, ==, 3);
+
+ g_object_unref (provider);
+ g_object_unref (context);
+}
+
+int
+main (int argc, char *argv[])
+{
+ gtk_init (NULL, NULL);
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/style/parse/empty", test_parse_empty);
+ g_test_add_func ("/style/parse/at", test_parse_at);
+ g_test_add_func ("/style/parse/selectors", test_parse_selectors);
+ g_test_add_func ("/style/parse/declarations", test_parse_declarations);
+ g_test_add_func ("/style/path", test_path);
+ g_test_add_func ("/style/match", test_match);
+ g_test_add_func ("/style/style-property", test_style_property);
+
+ return g_test_run ();
+}
diff --git a/gtk/tests/test.css b/gtk/tests/test.css
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/gtk/tests/test.css
diff --git a/gtk/tests/test.png b/gtk/tests/test.png
new file mode 100644
index 0000000000..8d0f458491
--- /dev/null
+++ b/gtk/tests/test.png
Binary files differ