diff options
author | Tristan Van Berkom <tristan.van.berkom@gmail.com> | 2010-12-05 00:36:37 +0900 |
---|---|---|
committer | Tristan Van Berkom <tristan.van.berkom@gmail.com> | 2010-12-05 00:36:37 +0900 |
commit | 0d786985a368d88e8ab4e45fc3607efc5e773732 (patch) | |
tree | 5dfc8ec0e27ac3c23b9dc2eb097018971c28fd6b /gtk | |
parent | 1d3961b34258a7a8c97ca12993220554369fccb1 (diff) | |
parent | cd76b057e9693b6919f4ccbe40ce205efc8d3caf (diff) | |
download | gtk+-0d786985a368d88e8ab4e45fc3607efc5e773732.tar.gz |
Merge branch 'master' into treeview-refactor
Conflicts:
gtk/gtkmarshalers.list
tests/Makefile.am
Diffstat (limited to 'gtk')
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) @@ -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), - >k_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), - >k_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: #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 + * '#' 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#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"> + * /* Theme labels that are descendants of a window */ + * GtkWindow GtkLabel { + * background-color: #898989 + * } + * + * /* Theme notebooks, and anything that's within these */ + * GtkNotebook { + * background-color: #a939f0 + * } + * + * /* Theme combo boxes, and entries that + * are direct children of a notebook */ + * GtkComboBox, + * GtkNotebook > GtkEntry { + * color: @fg_color; + * background-color: #1209a2 + * } + * + * /* Theme any widget within a GtkBin */ + * GtkBin * { + * font-name: Sans 20 + * } + * + * /* Theme a label named title-label */ + * GtkLabel#title-label { + * font-name: Sans 15 + * } + * + * /* Theme any widget named main-entry */ + * #main-entry { + * background-color: #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"> + * /* Theme all widgets defining the class entry */ + * .entry { + * color: #39f1f9; + * } + * + * /* Theme spinbuttons' entry */ + * GtkSpinButton.entry { + * color: #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"> + * /* Theme any label within a notebook */ + * GtkNotebook GtkLabel { + * color: #f90192; + * } + * + * /* Theme labels within notebook tabs */ + * GtkNotebook tab GtkLabel { + * color: #703910; + * } + * + * /* Theme labels in the any first notebook + * tab, both selectors are equivalent */ + * GtkNotebook tab:nth-child(first) GtkLabel, + * GtkNotebook tab:first-child GtkLabel { + * color: #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"> + * /* Theme active (pressed) buttons */ + * GtkButton:active { + * background-color: #0274d9; + * } + * + * /* Theme buttons with the mouse pointer on it, + * both are equivalent */ + * GtkButton:hover, + * GtkButton:prelight { + * background-color: #3085a9; + * } + * + * /* Theme insensitive widgets, both are equivalent */ + * :insensitive, + * *:insensitive { + * background-color: #320a91; + * } + * + * /* Theme selection colors in entries */ + * GtkEntry:selected { + * background-color: #56f9a0; + * } + * + * /* Theme focused labels */ + * GtkLabel:focused { + * background-color: #b4940f; + * } + * + * /* Theme inconsistent checkbuttons */ + * GtkCheckButton:inconsistent { + * background-color: #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>@ Rules</title> + * <para> + * GTK+'s CSS supports the @import rule, in order to load another + * CSS style sheet in addition to the currently parsed one. + * </para> + * <example> + * <title>Using the @import rule</title> + * <programlisting language="text"> + * @import url ("path/to/common.css"); + * </programlisting> + * </example> + * <para> + * GTK+ also supports an additional @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"> + * @define-color bg_color #f9a039; + * + * * { + * background-color: @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"> + * @define-color entry-color shade (@bg_color, 0.7); + * + * GtkEntry { + * background-color: @entry-color; + * } + * + * GtkEntry:focused { + * background-color: mix (@entry-color, + * shade (#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>#@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>#ff12ab + * #f0c</literallayout></entry> + * </row> + * <row> + * <entry>@name</entry> + * <entry>Reference to a color that has been defined with + * @define-color + * </entry> + * <entry>@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(#ff1e0a, @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(@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(@yellow), to(@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, @yellow), + * color-stop(0.2, @blue), + * color-stop(1, #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(@yellow), to(@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, #f00), + * color-stop (0.1, #a0f), + * color-stop (0.2, @yellow), + * color-stop (1, @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: #fff; + * color: &color1; + * background-color: shade (&color1, 0.5); + * color: mix (&color1, #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 (#fff), to (#000)); + * -gtk-gradient (linear, 0.0 0.5, 0.5 1.0, + * from (#fff), + * color-stop (0.5, #f00), + * to (#000)); + * -gtk-gradient (radial, + * center center, 0.2, + * center center, 0.8, + * color-stop (0.0, #fff), + * color-stop (1.0, #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: #f00 + * } + * + * GtkButton:hover { + * background-color: #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 π, π and 3&solidus;2 π</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> + * * { + * 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 Binary files differnew file mode 100644 index 0000000000..8d0f458491 --- /dev/null +++ b/gtk/tests/test.png |