gtk2/gtk/gtkstylecontext.c
Alexander Larsson af2c40bd54 css: Add a scale argument to css-value compute vfunc
We need to be able to compute different GtkCssImage values
depending on the scale, and we need this at compute time so that
we don't need to read any images other than the scale in used (to
e.g. calculate the image size). GtkStyleProviderPrivate is shared
for all style contexts, so its not right.
2013-07-03 14:39:25 +02:00

4810 lines
134 KiB
C

/* 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, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gdk/gdk.h>
#include <math.h>
#include <stdlib.h>
#include <gobject/gvaluecollector.h>
#include "gtkstylecontextprivate.h"
#include "gtkcontainerprivate.h"
#include "gtkcsscolorvalueprivate.h"
#include "gtkcsscornervalueprivate.h"
#include "gtkcssenginevalueprivate.h"
#include "gtkcssnumbervalueprivate.h"
#include "gtkcssrgbavalueprivate.h"
#include "gtkdebug.h"
#include "gtkstylepropertiesprivate.h"
#include "gtktypebuiltins.h"
#include "gtkthemingengineprivate.h"
#include "gtkintl.h"
#include "gtkwidget.h"
#include "gtkwindow.h"
#include "gtkprivate.h"
#include "gtkiconfactory.h"
#include "gtkwidgetpath.h"
#include "gtkwidgetprivate.h"
#include "gtkstylecascadeprivate.h"
#include "gtkstyleproviderprivate.h"
#include "gtksettings.h"
#include "gtksettingsprivate.h"
#include "deprecated/gtkgradientprivate.h"
#include "deprecated/gtksymboliccolorprivate.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 providers' 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 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:
* #GTK_STYLE_CLASS_CELL,
* #GTK_STYLE_CLASS_ENTRY,
* #GTK_STYLE_CLASS_BUTTON,
* #GTK_STYLE_CLASS_COMBOBOX_ENTRY,
* #GTK_STYLE_CLASS_CALENDAR,
* #GTK_STYLE_CLASS_SLIDER,
* #GTK_STYLE_CLASS_BACKGROUND,
* #GTK_STYLE_CLASS_RUBBERBAND,
* #GTK_STYLE_CLASS_TOOLTIP,
* #GTK_STYLE_CLASS_MENU,
* #GTK_STYLE_CLASS_MENUBAR,
* #GTK_STYLE_CLASS_MENUITEM,
* #GTK_STYLE_CLASS_TOOLBAR,
* #GTK_STYLE_CLASS_PRIMARY_TOOLBAR,
* #GTK_STYLE_CLASS_INLINE_TOOLBAR,
* #GTK_STYLE_CLASS_RADIO,
* #GTK_STYLE_CLASS_CHECK,
* #GTK_STYLE_CLASS_TROUGH,
* #GTK_STYLE_CLASS_SCROLLBAR,
* #GTK_STYLE_CLASS_SCALE,
* #GTK_STYLE_CLASS_SCALE_HAS_MARKS_ABOVE,
* #GTK_STYLE_CLASS_SCALE_HAS_MARKS_BELOW,
* #GTK_STYLE_CLASS_HEADER,
* #GTK_STYLE_CLASS_ACCELERATOR,
* #GTK_STYLE_CLASS_GRIP,
* #GTK_STYLE_CLASS_DOCK,
* #GTK_STYLE_CLASS_PROGRESSBAR,
* #GTK_STYLE_CLASS_SPINNER,
* #GTK_STYLE_CLASS_EXPANDER,
* #GTK_STYLE_CLASS_SPINBUTTON,
* #GTK_STYLE_CLASS_NOTEBOOK,
* #GTK_STYLE_CLASS_VIEW,
* #GTK_STYLE_CLASS_SIDEBAR,
* #GTK_STYLE_CLASS_IMAGE,
* #GTK_STYLE_CLASS_HIGHLIGHT,
* #GTK_STYLE_CLASS_FRAME,
* #GTK_STYLE_CLASS_DND,
* #GTK_STYLE_CLASS_PANE_SEPARATOR,
* #GTK_STYLE_CLASS_SEPARATOR,
* #GTK_STYLE_CLASS_INFO,
* #GTK_STYLE_CLASS_WARNING,
* #GTK_STYLE_CLASS_QUESTION,
* #GTK_STYLE_CLASS_ERROR,
* #GTK_STYLE_CLASS_HORIZONTAL,
* #GTK_STYLE_CLASS_VERTICAL,
* #GTK_STYLE_CLASS_TOP,
* #GTK_STYLE_CLASS_BOTTOM,
* #GTK_STYLE_CLASS_LEFT,
* #GTK_STYLE_CLASS_RIGHT,
* </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>
*/
/* When these change we do a full restyling. Otherwise we try to figure out
* if we need to change things. */
#define GTK_STYLE_CONTEXT_RADICAL_CHANGE (GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_SOURCE)
/* When these change we don't clear the cache. This takes more memory but makes
* things go faster. */
#define GTK_STYLE_CONTEXT_CACHED_CHANGE (GTK_CSS_CHANGE_STATE)
typedef struct GtkStyleInfo GtkStyleInfo;
typedef struct GtkRegion GtkRegion;
typedef struct PropertyValue PropertyValue;
typedef struct StyleData StyleData;
struct GtkRegion
{
GQuark class_quark;
GtkRegionFlags flags;
};
struct PropertyValue
{
GType widget_type;
GParamSpec *pspec;
GtkStateFlags state;
GValue value;
};
struct GtkStyleInfo
{
GtkStyleInfo *next;
GArray *style_classes;
GArray *regions;
GtkJunctionSides junction_sides;
GtkStateFlags state_flags;
StyleData *data;
};
struct StyleData
{
GtkCssComputedValues *store;
GArray *property_cache;
guint ref_count;
};
struct _GtkStyleContextPrivate
{
GdkScreen *screen;
GtkStyleCascade *cascade;
GtkStyleContext *parent;
GSList *children;
GtkWidget *widget;
GtkWidgetPath *widget_path;
GHashTable *style_data;
GtkStyleInfo *info;
gint scale;
GdkFrameClock *frame_clock;
guint frame_clock_update_id;
GtkCssChange relevant_changes;
GtkCssChange pending_changes;
const GtkBitmask *invalidating_context;
guint animating : 1;
guint invalid : 1;
};
enum {
PROP_0,
PROP_SCREEN,
PROP_DIRECTION,
PROP_FRAME_CLOCK,
PROP_PARENT
};
enum {
CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 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);
static StyleData *style_data_lookup (GtkStyleContext *context);
static void gtk_style_context_disconnect_update (GtkStyleContext *context);
static void gtk_style_context_connect_update (GtkStyleContext *context);
G_DEFINE_TYPE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT)
static void
gtk_style_context_real_changed (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv = context->priv;
if (priv->widget)
_gtk_widget_style_context_invalidated (priv->widget);
}
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;
klass->changed = gtk_style_context_real_changed;
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_FRAME_CLOCK,
g_param_spec_object ("paint-clock",
P_("FrameClock"),
P_("The associated GdkFrameClock"),
GDK_TYPE_FRAME_CLOCK,
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));
/**
* GtkStyleContext:parent:
*
* Sets or gets the style context's parent. See gtk_style_context_set_parent()
* for details.
*
* Since: 3.4
*/
g_object_class_install_property (object_class,
PROP_PARENT,
g_param_spec_object ("parent",
P_("Parent"),
P_("The parent style context"),
GTK_TYPE_STYLE_CONTEXT,
GTK_PARAM_READWRITE));
g_type_class_add_private (object_class, sizeof (GtkStyleContextPrivate));
}
static StyleData *
style_data_new (void)
{
StyleData *data;
data = g_slice_new0 (StyleData);
data->ref_count = 1;
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 StyleData *
style_data_ref (StyleData *style_data)
{
style_data->ref_count++;
return style_data;
}
static void
style_data_unref (StyleData *data)
{
data->ref_count--;
if (data->ref_count > 0)
return;
g_object_unref (data->store);
clear_property_cache (data);
g_slice_free (StyleData, data);
}
static gboolean
style_data_is_animating (StyleData *style_data)
{
return !_gtk_css_computed_values_is_static (style_data->store);
}
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_set_data (GtkStyleInfo *info,
StyleData *data)
{
if (info->data == data)
return;
if (data)
style_data_ref (data);
if (info->data)
style_data_unref (info->data);
info->data = data;
}
static void
style_info_free (GtkStyleInfo *info)
{
style_info_set_data (info, NULL);
g_array_free (info->style_classes, TRUE);
g_array_free (info->regions, TRUE);
g_slice_free (GtkStyleInfo, info);
}
static GtkStyleInfo *
style_info_pop (GtkStyleInfo *info)
{
GtkStyleInfo *next = info->next;
style_info_free (info);
return next;
}
static GtkStyleInfo *
style_info_copy (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->next = info;
copy->junction_sides = info->junction_sides;
copy->state_flags = info->state_flags;
style_info_set_data (copy, info->data);
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 ^ info->state_flags;
}
static gboolean
style_info_equal (gconstpointer elem1,
gconstpointer elem2)
{
const GtkStyleInfo *info1, *info2;
info1 = elem1;
info2 = elem2;
if (info1->state_flags != info2->state_flags)
return FALSE;
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 void
gtk_style_context_cascade_changed (GtkStyleCascade *cascade,
GtkStyleContext *context)
{
_gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_SOURCE);
}
static void
gtk_style_context_set_cascade (GtkStyleContext *context,
GtkStyleCascade *cascade)
{
GtkStyleContextPrivate *priv;
priv = context->priv;
if (priv->cascade == cascade)
return;
if (cascade)
{
g_object_ref (cascade);
g_signal_connect (cascade,
"-gtk-private-changed",
G_CALLBACK (gtk_style_context_cascade_changed),
context);
}
if (priv->cascade)
{
g_signal_handlers_disconnect_by_func (priv->cascade,
gtk_style_context_cascade_changed,
context);
g_object_unref (priv->cascade);
}
priv->cascade = cascade;
if (cascade)
gtk_style_context_cascade_changed (cascade, context);
}
static void
gtk_style_context_init (GtkStyleContext *style_context)
{
GtkStyleContextPrivate *priv;
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_unref);
priv->screen = gdk_screen_get_default ();
priv->relevant_changes = GTK_CSS_CHANGE_ANY;
/* Create default info store */
priv->info = style_info_new ();
priv->info->state_flags = GTK_STATE_FLAG_DIR_LTR;
gtk_style_context_set_cascade (style_context,
_gtk_style_cascade_get_for_screen (priv->screen));
}
static void
gtk_style_context_update (GdkFrameClock *clock,
GtkStyleContext *context)
{
_gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANIMATE);
}
static gboolean
gtk_style_context_is_animating (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv = context->priv;
return priv->animating;
}
static void
gtk_style_context_disconnect_update (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv = context->priv;
if (priv->frame_clock && priv->frame_clock_update_id)
{
g_signal_handler_disconnect (priv->frame_clock,
priv->frame_clock_update_id);
priv->frame_clock_update_id = 0;
gdk_frame_clock_end_updating (priv->frame_clock);
}
}
static void
gtk_style_context_connect_update (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv = context->priv;
if (priv->frame_clock && priv->frame_clock_update_id == 0)
{
priv->frame_clock_update_id = g_signal_connect (priv->frame_clock,
"update",
G_CALLBACK (gtk_style_context_update),
context);
gdk_frame_clock_begin_updating (priv->frame_clock);
}
}
static void
gtk_style_context_stop_animating (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv = context->priv;
if (!gtk_style_context_is_animating (context))
return;
priv->animating = FALSE;
gtk_style_context_disconnect_update (context);
}
static void
gtk_style_context_start_animating (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv = context->priv;
if (gtk_style_context_is_animating (context))
return;
priv->animating = TRUE;
gtk_style_context_connect_update (context);
}
static gboolean
gtk_style_context_should_animate (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
StyleData *data;
gboolean animate;
priv = context->priv;
if (priv->widget == NULL)
return FALSE;
if (!gtk_widget_get_mapped (priv->widget))
return FALSE;
data = style_data_lookup (context);
if (!style_data_is_animating (data))
return FALSE;
g_object_get (gtk_widget_get_settings (context->priv->widget),
"gtk-enable-animations", &animate,
NULL);
return animate;
}
void
_gtk_style_context_update_animating (GtkStyleContext *context)
{
if (gtk_style_context_should_animate (context))
gtk_style_context_start_animating (context);
else
gtk_style_context_stop_animating (context);
}
static void
gtk_style_context_finalize (GObject *object)
{
GtkStyleContextPrivate *priv;
GtkStyleContext *style_context;
style_context = GTK_STYLE_CONTEXT (object);
priv = style_context->priv;
gtk_style_context_stop_animating (style_context);
/* children hold a reference to us */
g_assert (priv->children == NULL);
gtk_style_context_set_parent (style_context, NULL);
gtk_style_context_set_cascade (style_context, NULL);
if (priv->widget_path)
gtk_widget_path_free (priv->widget_path);
g_hash_table_destroy (priv->style_data);
while (priv->info)
priv->info = style_info_pop (priv->info);
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)
{
GtkStyleContext *style_context;
style_context = GTK_STYLE_CONTEXT (object);
switch (prop_id)
{
case PROP_SCREEN:
gtk_style_context_set_screen (style_context,
g_value_get_object (value));
break;
case PROP_DIRECTION:
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
gtk_style_context_set_direction (style_context,
g_value_get_enum (value));
G_GNUC_END_IGNORE_DEPRECATIONS;
break;
case PROP_FRAME_CLOCK:
gtk_style_context_set_frame_clock (style_context,
g_value_get_object (value));
break;
case PROP_PARENT:
gtk_style_context_set_parent (style_context,
g_value_get_object (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)
{
GtkStyleContext *style_context;
GtkStyleContextPrivate *priv;
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_GNUC_BEGIN_IGNORE_DEPRECATIONS;
g_value_set_enum (value, gtk_style_context_get_direction (style_context));
G_GNUC_END_IGNORE_DEPRECATIONS;
break;
case PROP_FRAME_CLOCK:
g_value_set_object (value, priv->frame_clock);
break;
case PROP_PARENT:
g_value_set_object (value, priv->parent);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GtkWidgetPath *
create_query_path (GtkStyleContext *context,
GtkStyleInfo *info)
{
GtkStyleContextPrivate *priv;
GtkWidgetPath *path;
guint i, pos;
priv = context->priv;
path = priv->widget ? _gtk_widget_create_path (priv->widget) : gtk_widget_path_copy (priv->widget_path);
pos = gtk_widget_path_length (path) - 1;
/* 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 void
build_properties (GtkStyleContext *context,
GtkCssComputedValues *values,
GtkStyleInfo *info,
const GtkBitmask *relevant_changes)
{
GtkStyleContextPrivate *priv;
GtkCssMatcher matcher;
GtkWidgetPath *path;
GtkCssLookup *lookup;
priv = context->priv;
path = create_query_path (context, info);
lookup = _gtk_css_lookup_new (relevant_changes);
if (_gtk_css_matcher_init (&matcher, path, info->state_flags))
_gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
&matcher,
lookup);
_gtk_css_lookup_resolve (lookup,
GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
priv->scale,
values,
priv->parent ? style_data_lookup (priv->parent)->store : NULL);
_gtk_css_lookup_free (lookup);
gtk_widget_path_free (path);
}
static StyleData *
style_data_lookup (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
StyleData *data;
priv = context->priv;
info = priv->info;
/* Current data in use is cached, just return it */
if (info->data)
return info->data;
g_assert (priv->widget != NULL || priv->widget_path != NULL);
data = g_hash_table_lookup (priv->style_data, info);
if (data)
{
style_info_set_data (info, data);
return data;
}
data = style_data_new ();
data->store = _gtk_css_computed_values_new ();
style_info_set_data (info, data);
g_hash_table_insert (priv->style_data,
style_info_copy (info),
data);
build_properties (context, data->store, info, NULL);
return data;
}
static StyleData *
style_data_lookup_for_state (GtkStyleContext *context,
GtkStateFlags state)
{
StyleData *data;
if (context->priv->info->state_flags == state)
return style_data_lookup (context);
gtk_style_context_save (context);
gtk_style_context_set_state (context, state);
data = style_data_lookup (context);
gtk_style_context_restore (context);
return data;
}
static void
gtk_style_context_set_invalid (GtkStyleContext *context,
gboolean invalid)
{
GtkStyleContextPrivate *priv;
priv = context->priv;
if (priv->invalid == invalid)
return;
priv->invalid = invalid;
if (invalid)
{
if (GTK_IS_RESIZE_CONTAINER (priv->widget))
_gtk_container_queue_restyle (GTK_CONTAINER (priv->widget));
else if (priv->parent)
gtk_style_context_set_invalid (priv->parent, TRUE);
}
}
/* returns TRUE if someone called gtk_style_context_save() but hasn't
* called gtk_style_context_restore() yet.
* In those situations we don't invalidate the context when somebody
* changes state/regions/classes.
*/
static gboolean
gtk_style_context_is_saved (GtkStyleContext *context)
{
return context->priv->info->next != NULL;
}
static void
gtk_style_context_queue_invalidate_internal (GtkStyleContext *context,
GtkCssChange change)
{
GtkStyleContextPrivate *priv = context->priv;
GtkStyleInfo *info = priv->info;
if (gtk_style_context_is_saved (context))
{
style_info_set_data (info, NULL);
}
else
{
_gtk_style_context_queue_invalidate (context, change);
/* XXX: We need to invalidate siblings here somehow */
}
}
/**
* 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);
}
void
_gtk_style_context_set_widget (GtkStyleContext *context,
GtkWidget *widget)
{
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
context->priv->widget = widget;
_gtk_style_context_update_animating (context);
_gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_SELF);
}
/**
* 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.
* Note that a style provider added by this function only affects
* the style of the widget to which @context belongs. If you want
* to affect the style of all widgets, use
* gtk_style_context_add_provider_for_screen().
*
* <note><para>If both priorities are the same, A #GtkStyleProvider
* added through this function takes precedence over another added
* through gtk_style_context_add_provider_for_screen().</para></note>
*
* 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;
if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
{
GtkStyleCascade *new_cascade;
new_cascade = _gtk_style_cascade_new ();
_gtk_style_cascade_set_parent (new_cascade, priv->cascade);
_gtk_style_cascade_add_provider (new_cascade, provider, priority);
gtk_style_context_set_cascade (context, new_cascade);
g_object_unref (new_cascade);
}
else
{
_gtk_style_cascade_add_provider (priv->cascade, provider, priority);
}
}
/**
* 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 (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
return;
_gtk_style_cascade_remove_provider (priv->cascade, provider);
}
/**
* 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 #GtkStyleContexts under @screen.
*
* GTK+ uses this to make styling information from #GtkSettings
* available.
*
* <note><para>If both priorities are the same, A #GtkStyleProvider
* added through gtk_style_context_add_provider() takes precedence
* over another added through this function.</para></note>
*
* Since: 3.0
**/
void
gtk_style_context_add_provider_for_screen (GdkScreen *screen,
GtkStyleProvider *provider,
guint priority)
{
GtkStyleCascade *cascade;
g_return_if_fail (GDK_IS_SCREEN (screen));
g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
g_return_if_fail (!GTK_IS_SETTINGS (provider) || _gtk_settings_get_screen (GTK_SETTINGS (provider)) == screen);
cascade = _gtk_style_cascade_get_for_screen (screen);
_gtk_style_cascade_add_provider (cascade, provider, priority);
}
/**
* 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)
{
GtkStyleCascade *cascade;
g_return_if_fail (GDK_IS_SCREEN (screen));
g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
g_return_if_fail (!GTK_IS_SETTINGS (provider));
cascade = _gtk_style_cascade_get_for_screen (screen);
_gtk_style_cascade_remove_provider (cascade, provider);
}
/**
* gtk_style_context_get_section:
* @context: a #GtkStyleContext
* @property: style property name
*
* Queries the location in the CSS where @property was defined for the
* current @context. Note that the state to be queried is taken from
* gtk_style_context_get_state().
*
* If the location is not available, %NULL will be returned. The
* location might not be available for various reasons, such as the
* property being overridden, @property not naming a supported CSS
* property or tracking of definitions being disabled for performance
* reasons.
*
* Shorthand CSS properties cannot be queried for a location and will
* always return %NULL.
*
* Returns: %NULL or the section where value was defined
**/
GtkCssSection *
gtk_style_context_get_section (GtkStyleContext *context,
const gchar *property)
{
GtkStyleContextPrivate *priv;
GtkStyleProperty *prop;
StyleData *data;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
g_return_val_if_fail (property != NULL, NULL);
priv = context->priv;
g_return_val_if_fail (priv->widget != NULL || priv->widget_path != NULL, NULL);
prop = _gtk_style_property_lookup (property);
if (!GTK_IS_CSS_STYLE_PROPERTY (prop))
return NULL;
data = style_data_lookup (context);
return _gtk_css_computed_values_get_section (data->store, _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop)));
}
static GtkCssValue *
gtk_style_context_query_func (guint id,
gpointer values)
{
return _gtk_css_computed_values_get_value (values, id);
}
/**
* 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;
GtkStyleProperty *prop;
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 != NULL || priv->widget_path != NULL);
prop = _gtk_style_property_lookup (property);
if (prop == NULL)
{
g_warning ("Style property \"%s\" is not registered", property);
return;
}
if (_gtk_style_property_get_value_type (prop) == G_TYPE_NONE)
{
g_warning ("Style property \"%s\" is not gettable", property);
return;
}
data = style_data_lookup_for_state (context, state);
_gtk_style_property_query (prop, value, gtk_style_context_query_func, data->store);
}
/**
* 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)
{
const gchar *property_name;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
property_name = va_arg (args, const gchar *);
while (property_name)
{
gchar *error = NULL;
GValue value = G_VALUE_INIT;
gtk_style_context_get_property (context,
property_name,
state,
&value);
G_VALUE_LCOPY (&value, args, 0, &error);
g_value_unset (&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_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,
...)
{
va_list args;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
va_start (args, state);
gtk_style_context_get_valist (context, 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)
{
GtkStateFlags old_flags;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
old_flags = context->priv->info->state_flags;
if (old_flags == flags)
return;
context->priv->info->state_flags = flags;
if (((old_flags ^ flags) & (GTK_STATE_FLAG_DIR_LTR | GTK_STATE_FLAG_DIR_RTL)) &&
!gtk_style_context_is_saved (context))
g_object_notify (G_OBJECT (context), "direction");
gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_STATE);
}
/**
* 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)
{
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
return context->priv->info->state_flags;
}
/**
* gtk_style_context_set_scale:
* @context: a #GtkStyleContext
* @scale: scale
*
* Sets the scale to use when getting image assets for the style .
*
* Since: 3.10
**/
void
gtk_style_context_set_scale (GtkStyleContext *context,
gint scale)
{
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
if (context->priv->scale == scale)
return;
context->priv->scale = scale;
gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_SOURCE);
}
/**
* gtk_style_context_get_scale:
* @context: a #GtkStyleContext
*
* Returns the scale used for assets.
*
* Returns: the scale
*
* Since: 3.10
**/
gint
gtk_style_context_get_scale (GtkStyleContext *context)
{
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
return context->priv->scale;
}
/**
* 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
*
* Deprecated: 3.6: This function always returns %FALSE
**/
gboolean
gtk_style_context_state_is_running (GtkStyleContext *context,
GtkStateType state,
gdouble *progress)
{
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
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;
g_return_if_fail (priv->widget == NULL);
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_queue_invalidate (context, GTK_CSS_CHANGE_ANY);
}
/**
* gtk_style_context_get_path:
* @context: a #GtkStyleContext
*
* Returns the widget path used for style matching.
*
* Returns: (transfer none): A #GtkWidgetPath
*
* Since: 3.0
**/
const GtkWidgetPath *
gtk_style_context_get_path (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
priv = context->priv;
if (priv->widget)
return gtk_widget_get_path (priv->widget);
else
return priv->widget_path;
}
/**
* gtk_style_context_set_parent:
* @context: a #GtkStyleContext
* @parent: (allow-none): the new parent or %NULL
*
* Sets the parent style context for @context. The parent style
* context is used to implement
* <ulink url="http://www.w3.org/TR/css3-cascade/#inheritance">inheritance</ulink>
* of properties.
*
* If you are using a #GtkStyleContext returned from
* gtk_widget_get_style_context(), the parent will be set for you.
*
* Since: 3.4
**/
void
gtk_style_context_set_parent (GtkStyleContext *context,
GtkStyleContext *parent)
{
GtkStyleContextPrivate *priv;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (parent == NULL || GTK_IS_STYLE_CONTEXT (parent));
priv = context->priv;
if (priv->parent == parent)
return;
if (parent)
{
parent->priv->children = g_slist_prepend (parent->priv->children, context);
g_object_ref (parent);
if (priv->invalid)
gtk_style_context_set_invalid (parent, TRUE);
}
if (priv->parent)
{
priv->parent->priv->children = g_slist_remove (priv->parent->priv->children, context);
g_object_unref (priv->parent);
}
priv->parent = parent;
g_object_notify (G_OBJECT (context), "parent");
_gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_PARENT | GTK_CSS_CHANGE_ANY_SIBLING);
}
/**
* gtk_style_context_get_parent:
* @context: a #GtkStyleContext
*
* Gets the parent context set via gtk_style_context_set_parent().
* See that function for details.
*
* Returns: (transfer none): the parent context or %NULL
*
* Since: 3.4
**/
GtkStyleContext *
gtk_style_context_get_parent (GtkStyleContext *context)
{
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
return context->priv->parent;
}
/**
* 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;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
priv->info = style_info_copy (priv->info);
/* Need to unset animations here because we can not know what style
* class potential transitions came from once we save().
*/
if (priv->info->data && style_data_is_animating (priv->info->data))
style_info_set_data (priv->info, NULL);
}
/**
* 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;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
priv->info = style_info_pop (priv->info);
if (!priv->info)
{
g_warning ("Unpaired gtk_style_context_restore() call");
/* Create default region */
priv->info = style_info_new ();
}
}
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);
info = priv->info;
if (!style_class_find (info->style_classes, class_quark, &position))
{
g_array_insert_val (info->style_classes, position, class_quark);
gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_CLASS);
}
}
/**
* 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;
info = priv->info;
if (style_class_find (info->style_classes, class_quark, &position))
{
g_array_remove_index (info->style_classes, position);
gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_CLASS);
}
}
/**
* 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;
info = priv->info;
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;
info = priv->info;
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;
info = priv->info;
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;
}
gboolean
_gtk_style_context_check_region_name (const gchar *str)
{
g_return_val_if_fail (str != NULL, FALSE);
if (!g_ascii_islower (str[0]))
return FALSE;
while (*str)
{
if (*str != '-' &&
!g_ascii_islower (*str))
return FALSE;
str++;
}
return TRUE;
}
/**
* 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.
*
* <note><para>Region names must only contain lowercase letters
* and '-', starting always with a lowercase letter.</para></note>
*
* 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);
g_return_if_fail (_gtk_style_context_check_region_name (region_name));
priv = context->priv;
region_quark = g_quark_from_string (region_name);
info = priv->info;
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);
gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_REGION);
}
}
/**
* 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;
info = priv->info;
if (region_find (info->regions, region_quark, &position))
{
g_array_remove_index (info->regions, position);
gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_REGION);
}
}
/**
* 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;
info = priv->info;
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;
}
GtkCssValue *
_gtk_style_context_peek_property (GtkStyleContext *context,
guint property_id)
{
StyleData *data = style_data_lookup (context);
return _gtk_css_computed_values_get_value (data->store, property_id);
}
const GValue *
_gtk_style_context_peek_style_property (GtkStyleContext *context,
GType widget_type,
GtkStateFlags state,
GParamSpec *pspec)
{
GtkStyleContextPrivate *priv;
PropertyValue *pcache, key = { 0 };
StyleData *data;
guint i;
priv = context->priv;
data = style_data_lookup_for_state (context, state);
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->widget || priv->widget_path)
{
GtkWidgetPath *widget_path = priv->widget ? _gtk_widget_create_path (priv->widget) : priv->widget_path;
if (gtk_style_provider_get_style_property (GTK_STYLE_PROVIDER (priv->cascade),
widget_path,
state, pspec, &pcache->value))
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
/* Resolve symbolic colors to GdkColor/GdkRGBA */
if (G_VALUE_TYPE (&pcache->value) == GTK_TYPE_SYMBOLIC_COLOR)
{
GtkSymbolicColor *color;
GdkRGBA rgba;
color = g_value_dup_boxed (&pcache->value);
g_value_unset (&pcache->value);
if (G_PARAM_SPEC_VALUE_TYPE (pspec) == GDK_TYPE_RGBA)
g_value_init (&pcache->value, GDK_TYPE_RGBA);
else
g_value_init (&pcache->value, GDK_TYPE_COLOR);
if (_gtk_style_context_resolve_color (context, _gtk_symbolic_color_get_css_value (color), &rgba, NULL))
{
if (G_PARAM_SPEC_VALUE_TYPE (pspec) == 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_set_boxed (&pcache->value, &rgb);
}
}
else
g_param_value_set_default (pspec, &pcache->value);
gtk_symbolic_color_unref (color);
}
G_GNUC_END_IGNORE_DEPRECATIONS;
if (priv->widget)
gtk_widget_path_free (widget_path);
return &pcache->value;
}
if (priv->widget)
gtk_widget_path_free (widget_path);
}
/* 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: 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)
{
widget_type = G_OBJECT_TYPE (priv->widget);
}
else
{
if (!priv->widget_path)
return;
widget_type = gtk_widget_path_get_object_type (priv->widget_path);
if (!g_type_is_a (widget_type, GTK_TYPE_WIDGET))
{
g_warning ("%s: can't get style properties for non-widget class `%s'",
G_STRLOC,
g_type_name (widget_type));
return;
}
}
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;
GType widget_type;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
prop_name = va_arg (args, const gchar *);
priv = context->priv;
if (priv->widget)
{
widget_type = G_OBJECT_TYPE (priv->widget);
}
else
{
if (!priv->widget_path)
return;
widget_type = gtk_widget_path_get_object_type (priv->widget_path);
if (!g_type_is_a (widget_type, GTK_TYPE_WIDGET))
{
g_warning ("%s: can't get style properties for non-widget class `%s'",
G_STRLOC,
g_type_name (widget_type));
return;
}
}
state = gtk_style_context_get_state (context);
while (prop_name)
{
GtkWidgetClass *widget_class;
GParamSpec *pspec;
const GValue *peek_value;
gchar *error;
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);
break;
}
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);
break;
}
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
*
* Deprecated: 3.10: Use gtk_icon_theme_lookup_icon() instead.
**/
GtkIconSet *
gtk_style_context_lookup_icon_set (GtkStyleContext *context,
const gchar *stock_id)
{
GtkStyleContextPrivate *priv;
GtkIconSet *icon_set;
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 != NULL || priv->widget_path != NULL, NULL);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
icon_set = gtk_icon_factory_lookup_default (stock_id);
G_GNUC_END_IGNORE_DEPRECATIONS;
return icon_set;
}
/**
* 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;
if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
{
gtk_style_context_set_cascade (context, _gtk_style_cascade_get_for_screen (screen));
}
else
{
_gtk_style_cascade_set_parent (priv->cascade, _gtk_style_cascade_get_for_screen (screen));
}
priv->screen = screen;
g_object_notify (G_OBJECT (context), "screen");
}
/**
* gtk_style_context_get_screen:
* @context: a #GtkStyleContext
*
* Returns the #GdkScreen to which @context is attached.
*
* Returns: (transfer none): 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_frame_clock:
* @context: a #GdkFrameClock
* @frame_clock: a #GdkFrameClock
*
* Attaches @context to the given frame clock.
*
* The frame clock is used for the timing of animations.
*
* If you are using a #GtkStyleContext returned from
* gtk_widget_get_style_context(), you do not need to
* call this yourself.
*
* Since: 3.8
**/
void
gtk_style_context_set_frame_clock (GtkStyleContext *context,
GdkFrameClock *frame_clock)
{
GtkStyleContextPrivate *priv;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (frame_clock == NULL || GDK_IS_FRAME_CLOCK (frame_clock));
priv = context->priv;
if (priv->frame_clock == frame_clock)
return;
if (priv->animating)
gtk_style_context_disconnect_update (context);
if (priv->frame_clock)
g_object_unref (priv->frame_clock);
priv->frame_clock = frame_clock;
if (priv->frame_clock)
g_object_ref (priv->frame_clock);
if (priv->animating)
gtk_style_context_connect_update (context);
g_object_notify (G_OBJECT (context), "paint-clock");
}
/**
* gtk_style_context_get_frame_clock:
* @context: a #GtkStyleContext
*
* Returns the #GdkFrameClock to which @context is attached.
*
* Returns: (transfer none): a #GdkFrameClock, or %NULL
* if @context does not have an attached frame clock.
*
* Since: 3.8
**/
GdkFrameClock *
gtk_style_context_get_frame_clock (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
priv = context->priv;
return priv->frame_clock;
}
/**
* 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
*
* Deprecated: 3.8: Use gtk_style_context_set_state() with
* #GTK_STATE_FLAG_DIR_LTR and #GTK_STATE_FLAG_DIR_RTL
* instead.
**/
void
gtk_style_context_set_direction (GtkStyleContext *context,
GtkTextDirection direction)
{
GtkStateFlags state;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
state = gtk_style_context_get_state (context);
state &= ~(GTK_STATE_FLAG_DIR_LTR | GTK_STATE_FLAG_DIR_RTL);
switch (direction)
{
case GTK_TEXT_DIR_LTR:
state |= GTK_STATE_FLAG_DIR_LTR;
break;
case GTK_TEXT_DIR_RTL:
state |= GTK_STATE_FLAG_DIR_RTL;
break;
case GTK_TEXT_DIR_NONE:
default:
break;
}
gtk_style_context_set_state (context, state);
}
/**
* gtk_style_context_get_direction:
* @context: a #GtkStyleContext
*
* Returns the widget direction used for rendering.
*
* Returns: the widget direction
*
* Since: 3.0
*
* Deprecated: 3.8: Use gtk_style_context_get_state() and
* check for #GTK_STATE_FLAG_DIR_LTR and
* #GTK_STATE_FLAG_DIR_RTL instead.
**/
GtkTextDirection
gtk_style_context_get_direction (GtkStyleContext *context)
{
GtkStateFlags state;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), GTK_TEXT_DIR_LTR);
state = gtk_style_context_get_state (context);
if (state & GTK_STATE_FLAG_DIR_LTR)
return GTK_TEXT_DIR_LTR;
else if (state & GTK_STATE_FLAG_DIR_RTL)
return GTK_TEXT_DIR_RTL;
else
return GTK_TEXT_DIR_NONE;
}
/**
* 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)
{
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
context->priv->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)
{
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
return context->priv->info->junction_sides;
}
gboolean
_gtk_style_context_resolve_color (GtkStyleContext *context,
GtkCssValue *color,
GdkRGBA *result,
GtkCssDependencies *dependencies)
{
GtkCssValue *val;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
g_return_val_if_fail (color != NULL, FALSE);
g_return_val_if_fail (result != NULL, FALSE);
val = _gtk_css_color_value_resolve (color,
GTK_STYLE_PROVIDER_PRIVATE (context->priv->cascade),
_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_COLOR),
GTK_CSS_DEPENDS_ON_COLOR,
dependencies,
NULL);
if (val == NULL)
return FALSE;
*result = *_gtk_css_rgba_value_get_rgba (val);
_gtk_css_value_unref (val);
return TRUE;
}
/**
* 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)
{
GtkCssValue *value;
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);
value = _gtk_style_provider_private_get_color (GTK_STYLE_PROVIDER_PRIVATE (context->priv->cascade), color_name);
if (value == NULL)
return FALSE;
return _gtk_style_context_resolve_color (context, value, color, NULL);
}
/**
* gtk_style_context_notify_state_change:
* @context: a #GtkStyleContext
* @window: a #GdkWindow
* @region_id: (allow-none): animatable region to notify on, or %NULL.
* See gtk_style_context_push_animatable_region()
* @state: state to trigger transition for
* @state_value: %TRUE if @state is the state we are changing to,
* %FALSE if we are changing away from it
*
* Notifies a state change on @context, so if the current style makes use
* of transition animations, one will be started so all rendered elements
* under @region_id are animated for state @state being set to value
* @state_value.
*
* The @window parameter is used in order to invalidate the rendered area
* as the animation runs, so make sure it is the same window that is being
* rendered on by the gtk_render_*() functions.
*
* If @region_id is %NULL, all rendered elements using @context will be
* affected by this state transition.
*
* As a practical example, a #GtkButton notifying a state transition on
* the prelight state:
* <programlisting>
* gtk_style_context_notify_state_change (context,
* gtk_widget_get_window (widget),
* NULL,
* GTK_STATE_PRELIGHT,
* button->in_button);
* </programlisting>
*
* Can be handled in the CSS file like this:
* <programlisting>
* GtkButton {
* background-color: &num;f00
* }
*
* GtkButton:hover {
* background-color: &num;fff;
* transition: 200ms linear
* }
* </programlisting>
*
* This combination will animate the button background from red to white
* if a pointer enters the button, and back to red if the pointer leaves
* the button.
*
* Note that @state is used when finding the transition parameters, which
* is why the style places the transition under the :hover pseudo-class.
*
* Since: 3.0
*
* Deprecated: 3.6: This function does nothing.
**/
void
gtk_style_context_notify_state_change (GtkStyleContext *context,
GdkWindow *window,
gpointer region_id,
GtkStateType state,
gboolean state_value)
{
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);
g_return_if_fail (context->priv->widget != NULL || context->priv->widget_path != NULL);
}
/**
* gtk_style_context_cancel_animations:
* @context: a #GtkStyleContext
* @region_id: (allow-none): animatable region to stop, or %NULL.
* See gtk_style_context_push_animatable_region()
*
* Stops all running animations for @region_id and all animatable
* regions underneath.
*
* A %NULL @region_id will stop all ongoing animations in @context,
* when dealing with a #GtkStyleContext obtained through
* gtk_widget_get_style_context(), this is normally done for you
* in all circumstances you would expect all widget to be stopped,
* so this should be only used in complex widgets with different
* animatable regions.
*
* Since: 3.0
*
* Deprecated: 3.6: This function does nothing.
**/
void
gtk_style_context_cancel_animations (GtkStyleContext *context,
gpointer region_id)
{
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
}
/**
* gtk_style_context_scroll_animations:
* @context: a #GtkStyleContext
* @window: a #GdkWindow used previously in
* gtk_style_context_notify_state_change()
* @dx: Amount to scroll in the X axis
* @dy: Amount to scroll in the Y axis
*
* This function is analogous to gdk_window_scroll(), and
* should be called together with it so the invalidation
* areas for any ongoing animation are scrolled together
* with it.
*
* Since: 3.0
*
* Deprecated: 3.6: This function does nothing.
**/
void
gtk_style_context_scroll_animations (GtkStyleContext *context,
GdkWindow *window,
gint dx,
gint dy)
{
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (GDK_IS_WINDOW (window));
}
/**
* 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
*
* Deprecated: 3.6: This function does nothing.
**/
void
gtk_style_context_push_animatable_region (GtkStyleContext *context,
gpointer region_id)
{
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (region_id != NULL);
}
/**
* 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
*
* Deprecated: 3.6: This function does nothing.
**/
void
gtk_style_context_pop_animatable_region (GtkStyleContext *context)
{
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
}
static void
gtk_style_context_clear_cache (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
priv = context->priv;
for (info = priv->info; info; info = info->next)
{
style_info_set_data (info, NULL);
}
g_hash_table_remove_all (priv->style_data);
}
static void
gtk_style_context_update_cache (GtkStyleContext *context,
const GtkBitmask *parent_changes)
{
GtkStyleContextPrivate *priv;
GHashTableIter iter;
gpointer key, value;
if (_gtk_bitmask_is_empty (parent_changes))
return;
priv = context->priv;
g_hash_table_iter_init (&iter, priv->style_data);
while (g_hash_table_iter_next (&iter, &key, &value))
{
GtkStyleInfo *info = key;
StyleData *data = value;
GtkBitmask *changes;
changes = _gtk_css_computed_values_compute_dependencies (data->store, parent_changes);
if (!_gtk_bitmask_is_empty (changes))
build_properties (context, data->store, info, changes);
_gtk_bitmask_free (changes);
}
}
static void
gtk_style_context_do_invalidate (GtkStyleContext *context,
const GtkBitmask *changes)
{
GtkStyleContextPrivate *priv;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
/* Avoid reentrancy */
if (priv->invalidating_context)
return;
priv->invalidating_context = changes;
g_signal_emit (context, signals[CHANGED], 0);
priv->invalidating_context = NULL;
}
static GtkBitmask *
gtk_style_context_update_animations (GtkStyleContext *context,
gint64 timestamp)
{
GtkBitmask *differences;
StyleData *style_data;
style_data = style_data_lookup (context);
differences = _gtk_css_computed_values_advance (style_data->store,
timestamp);
if (_gtk_css_computed_values_is_static (style_data->store))
_gtk_style_context_update_animating (context);
return differences;
}
static gboolean
gtk_style_context_needs_full_revalidate (GtkStyleContext *context,
GtkCssChange change)
{
GtkStyleContextPrivate *priv = context->priv;
/* Try to avoid invalidating if we can */
if (change & GTK_STYLE_CONTEXT_RADICAL_CHANGE)
{
priv->relevant_changes = GTK_CSS_CHANGE_ANY;
}
else
{
if (priv->relevant_changes == GTK_CSS_CHANGE_ANY)
{
GtkWidgetPath *path;
GtkCssMatcher matcher, superset;
path = create_query_path (context, priv->info);
if (_gtk_css_matcher_init (&matcher, path, priv->info->state_flags))
{
_gtk_css_matcher_superset_init (&superset, &matcher, GTK_STYLE_CONTEXT_RADICAL_CHANGE & ~GTK_CSS_CHANGE_SOURCE);
priv->relevant_changes = _gtk_style_provider_private_get_change (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
&superset);
}
else
priv->relevant_changes = 0;
priv->relevant_changes &= ~GTK_STYLE_CONTEXT_RADICAL_CHANGE;
gtk_widget_path_unref (path);
}
}
if (priv->relevant_changes & change)
return TRUE;
else
return FALSE;
}
static gboolean
gtk_style_context_should_create_transitions (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
gboolean animate;
priv = context->priv;
if (priv->widget == NULL)
return FALSE;
if (!gtk_widget_get_mapped (priv->widget))
return FALSE;
g_object_get (gtk_widget_get_settings (context->priv->widget),
"gtk-enable-animations", &animate,
NULL);
return animate;
}
void
_gtk_style_context_validate (GtkStyleContext *context,
gint64 timestamp,
GtkCssChange change,
const GtkBitmask *parent_changes)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
StyleData *current;
GtkBitmask *changes;
GSList *list;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
change |= priv->pending_changes;
/* If you run your application with
* GTK_DEBUG=no-css-cache
* every invalidation will purge the cache and completely query
* everything anew form the cache. This is slow (in particular
* when animating), but useful for figuring out bugs.
*
* We achieve that by pretending that everything that could have
* changed has and so we of course totally need to redo everything.
*
* Note that this also completely revalidates child widgets all
* the time.
*/
if (G_UNLIKELY (gtk_get_debug_flags () & GTK_DEBUG_NO_CSS_CACHE))
change = GTK_CSS_CHANGE_ANY;
if (!priv->invalid && change == 0 && _gtk_bitmask_is_empty (parent_changes))
return;
priv->pending_changes = 0;
gtk_style_context_set_invalid (context, FALSE);
info = priv->info;
if (info->data)
current = style_data_ref (info->data);
else
current = NULL;
/* Try to avoid invalidating if we can */
if (current == NULL ||
gtk_style_context_needs_full_revalidate (context, change))
{
StyleData *data;
if ((priv->relevant_changes & change) & ~GTK_STYLE_CONTEXT_CACHED_CHANGE)
{
gtk_style_context_clear_cache (context);
}
else
{
gtk_style_context_update_cache (context, parent_changes);
style_info_set_data (info, NULL);
}
data = style_data_lookup (context);
_gtk_css_computed_values_create_animations (data->store,
priv->parent ? style_data_lookup (priv->parent)->store : NULL,
timestamp,
GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
priv->scale,
current && gtk_style_context_should_create_transitions (context) ? current->store : NULL);
if (_gtk_css_computed_values_is_static (data->store))
change &= ~GTK_CSS_CHANGE_ANIMATE;
else
change |= GTK_CSS_CHANGE_ANIMATE;
_gtk_style_context_update_animating (context);
if (current)
{
changes = _gtk_css_computed_values_get_difference (data->store, current->store);
/* In the case where we keep the cache, we want unanimated values */
_gtk_css_computed_values_cancel_animations (current->store);
}
else
{
changes = _gtk_bitmask_new ();
changes = _gtk_bitmask_invert_range (changes, 0, _gtk_css_style_property_get_n_properties ());
}
}
else
{
changes = _gtk_css_computed_values_compute_dependencies (current->store, parent_changes);
gtk_style_context_update_cache (context, parent_changes);
}
if (current)
style_data_unref (current);
if (change & GTK_CSS_CHANGE_ANIMATE &&
gtk_style_context_is_animating (context))
{
GtkBitmask *animation_changes;
animation_changes = gtk_style_context_update_animations (context, timestamp);
changes = _gtk_bitmask_union (changes, animation_changes);
_gtk_bitmask_free (animation_changes);
}
if (change & GTK_CSS_CHANGE_FORCE_INVALIDATE)
{
GtkBitmask *full = _gtk_bitmask_new ();
full = _gtk_bitmask_invert_range (full,
0,
_gtk_css_style_property_get_n_properties ());
gtk_style_context_do_invalidate (context, full);
_gtk_bitmask_free (full);
}
else if (!_gtk_bitmask_is_empty (changes))
{
gtk_style_context_do_invalidate (context, changes);
}
change = _gtk_css_change_for_child (change);
for (list = priv->children; list; list = list->next)
{
_gtk_style_context_validate (list->data, timestamp, change, changes);
}
_gtk_bitmask_free (changes);
}
void
_gtk_style_context_queue_invalidate (GtkStyleContext *context,
GtkCssChange change)
{
GtkStyleContextPrivate *priv;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (change != 0);
priv = context->priv;
if (priv->widget != NULL)
{
priv->pending_changes |= change;
gtk_style_context_set_invalid (context, TRUE);
}
else if (priv->widget_path == NULL)
{
gtk_style_context_invalidate (context);
}
}
/**
* 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)
{
GtkBitmask *changes;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
gtk_style_context_clear_cache (context);
changes = _gtk_bitmask_new ();
changes = _gtk_bitmask_invert_range (changes,
0,
_gtk_css_style_property_get_n_properties ());
gtk_style_context_do_invalidate (context, changes);
_gtk_bitmask_free (changes);
}
static gboolean
corner_value_is_right_angle (GtkCssValue *value)
{
return _gtk_css_corner_value_get_x (value, 100) <= 0.0 &&
_gtk_css_corner_value_get_y (value, 100) <= 0.0;
}
/**
* 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)
{
const GdkRGBA *color;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (GDK_IS_WINDOW (window));
/* This is a sophisitcated optimization.
* If we know the GDK window's background will be opaque, we mark
* it as opaque. This is so GDK can do all the optimizations it does
* for opaque windows and be fast.
* This is mainly used when scrolling.
*
* We could indeed just set black instead of the color we have.
*/
color = _gtk_css_rgba_value_get_rgba (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BACKGROUND_COLOR));
if (color->alpha >= 1.0 &&
corner_value_is_right_angle (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_LEFT_RADIUS)) &&
corner_value_is_right_angle (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_TOP_RIGHT_RADIUS)) &&
corner_value_is_right_angle (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_RIGHT_RADIUS)) &&
corner_value_is_right_angle (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_BORDER_BOTTOM_LEFT_RADIUS)))
{
gdk_window_set_background_rgba (window, color);
}
else
{
GdkRGBA transparent = { 0.0, 0.0, 0.0, 0.0 };
gdk_window_set_background_rgba (window, &transparent);
}
}
/**
* 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)
{
GdkRGBA *c;
g_return_if_fail (color != NULL);
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
gtk_style_context_get (context,
state,
"color", &c,
NULL);
*color = *c;
gdk_rgba_free (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)
{
GdkRGBA *c;
g_return_if_fail (color != NULL);
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
gtk_style_context_get (context,
state,
"background-color", &c,
NULL);
*color = *c;
gdk_rgba_free (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)
{
GdkRGBA *c;
g_return_if_fail (color != NULL);
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
gtk_style_context_get (context,
state,
"border-color", &c,
NULL);
*color = *c;
gdk_rgba_free (c);
}
/**
* gtk_style_context_get_border:
* @context: a #GtkStyleContext
* @state: state to retrieve the border for
* @border: (out): return value for the border settings
*
* Gets the border for a given state as a #GtkBorder.
* See %GTK_STYLE_PROPERTY_BORDER_WIDTH.
*
* Since: 3.0
**/
void
gtk_style_context_get_border (GtkStyleContext *context,
GtkStateFlags state,
GtkBorder *border)
{
int top, left, bottom, right;
g_return_if_fail (border != NULL);
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
gtk_style_context_get (context,
state,
"border-top-width", &top,
"border-left-width", &left,
"border-bottom-width", &bottom,
"border-right-width", &right,
NULL);
border->top = top;
border->left = left;
border->bottom = bottom;
border->right = right;
}
/**
* gtk_style_context_get_padding:
* @context: a #GtkStyleContext
* @state: state to retrieve the padding for
* @padding: (out): return value for the padding settings
*
* Gets the padding for a given state as a #GtkBorder.
* See %GTK_STYLE_PROPERTY_PADDING.
*
* Since: 3.0
**/
void
gtk_style_context_get_padding (GtkStyleContext *context,
GtkStateFlags state,
GtkBorder *padding)
{
int top, left, bottom, right;
g_return_if_fail (padding != NULL);
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
gtk_style_context_get (context,
state,
"padding-top", &top,
"padding-left", &left,
"padding-bottom", &bottom,
"padding-right", &right,
NULL);
padding->top = top;
padding->left = left;
padding->bottom = bottom;
padding->right = right;
}
/**
* gtk_style_context_get_margin:
* @context: a #GtkStyleContext
* @state: state to retrieve the border for
* @margin: (out): return value for the margin settings
*
* Gets the margin for a given state as a #GtkBorder.
* See %GTK_STYLE_PROPERTY_MARGIN.
*
* Since: 3.0
**/
void
gtk_style_context_get_margin (GtkStyleContext *context,
GtkStateFlags state,
GtkBorder *margin)
{
int top, left, bottom, right;
g_return_if_fail (margin != NULL);
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
gtk_style_context_get (context,
state,
"margin-top", &top,
"margin-left", &left,
"margin-bottom", &bottom,
"margin-right", &right,
NULL);
margin->top = top;
margin->left = left;
margin->bottom = bottom;
margin->right = right;
}
/**
* gtk_style_context_get_font:
* @context: a #GtkStyleContext
* @state: state to retrieve the font for
*
* Returns the font description for a given state. The returned
* object is const and will remain valid until the
* #GtkStyleContext::changed signal happens.
*
* Returns: (transfer none): the #PangoFontDescription for the given
* state. This object is owned by GTK+ and should not be
* freed.
*
* Since: 3.0
*
* Deprecated: 3.8: Use gtk_style_context_get() for "font" or
* subproperties instead.
**/
const PangoFontDescription *
gtk_style_context_get_font (GtkStyleContext *context,
GtkStateFlags state)
{
GtkStyleContextPrivate *priv;
StyleData *data;
PangoFontDescription *description, *previous;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
priv = context->priv;
g_return_val_if_fail (priv->widget != NULL || priv->widget_path != NULL, NULL);
data = style_data_lookup_for_state (context, state);
/* Yuck, fonts are created on-demand but we don't return a ref.
* Do bad things to achieve this requirement */
gtk_style_context_get (context, state, "font", &description, NULL);
previous = g_object_get_data (G_OBJECT (data->store), "font-cache-for-get_font");
if (previous)
{
pango_font_description_merge (previous, description, TRUE);
pango_font_description_free (description);
description = previous;
}
else
{
g_object_set_data_full (G_OBJECT (data->store),
"font-cache-for-get_font",
description,
(GDestroyNotify) pango_font_description_free);
}
return description;
}
static void
get_cursor_color (GtkStyleContext *context,
gboolean primary,
GdkRGBA *color)
{
GdkColor *style_color;
gtk_style_context_get_style (context,
primary ? "cursor-color" : "secondary-cursor-color",
&style_color,
NULL);
if (style_color)
{
color->red = style_color->red / 65535.0;
color->green = style_color->green / 65535.0;
color->blue = style_color->blue / 65535.0;
color->alpha = 1;
gdk_color_free (style_color);
}
else
{
gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, color);
if (!primary)
{
GdkRGBA bg;
gtk_style_context_get_background_color (context, GTK_STATE_FLAG_NORMAL, &bg);
color->red = (color->red + bg.red) * 0.5;
color->green = (color->green + bg.green) * 0.5;
color->blue = (color->blue + bg.blue) * 0.5;
}
}
}
void
_gtk_style_context_get_cursor_color (GtkStyleContext *context,
GdkRGBA *primary_color,
GdkRGBA *secondary_color)
{
if (primary_color)
get_cursor_color (context, TRUE, primary_color);
if (secondary_color)
get_cursor_color (context, FALSE, secondary_color);
}
/* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (width <= 0 || height <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_check (engine, cr,
x, y, width, height);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (width <= 0 || height <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_option (engine, cr,
x, y, width, height);
cairo_restore (cr);
}
/**
* 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: X origin of the render area
* @y: Y origin of the render area
* @size: square side for render area
*
* Renders an arrow pointing to @angle.
*
* <example>
* <title>Typical arrow rendering at 0, 1&solidus;2 &pi;, &pi; and 3&solidus;2 &pi;</title>
* <inlinegraphic fileref="arrows.png" format="PNG"/>
* </example>
*
* Since: 3.0
**/
void
gtk_render_arrow (GtkStyleContext *context,
cairo_t *cr,
gdouble angle,
gdouble x,
gdouble y,
gdouble size)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (size <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
gtk_style_context_save (context);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_ARROW);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_arrow (engine, cr,
angle, x, y, size);
gtk_style_context_restore (context);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (width <= 0 || height <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_background (engine, cr, x, y, width, height);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (width <= 0 || height <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_frame (engine, cr, x, y, width, height);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (width <= 0 || height <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_expander (engine, cr, x, y, width, height);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (width <= 0 || height <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_focus (engine, cr, x, y, width, height);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
PangoRectangle extents;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (PANGO_IS_LAYOUT (layout));
g_return_if_fail (cr != NULL);
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
pango_layout_get_extents (layout, &extents, NULL);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_layout (engine, cr, x, y, layout);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_line (engine, cr, x0, y0, x1, y1);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (width <= 0 || height <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_slider (engine, cr, x, y, width, height, orientation);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
g_return_if_fail (xy0_gap <= xy1_gap);
g_return_if_fail (xy0_gap >= 0);
if (width <= 0 || height <= 0)
return;
if (gap_side == GTK_POS_LEFT ||
gap_side == GTK_POS_RIGHT)
g_return_if_fail (xy1_gap <= height);
else
g_return_if_fail (xy1_gap <= width);
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_frame_gap (engine, cr,
x, y, width, height, gap_side,
xy0_gap, xy1_gap);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (width <= 0 || height <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_extension (engine, cr, x, y, width, height, gap_side);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (width <= 0 || height <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_handle (engine, cr, x, y, width, height);
cairo_restore (cr);
}
/**
* 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)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
if (width <= 0 || height <= 0)
return;
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_activity (engine, cr, x, y, width, height);
cairo_restore (cr);
}
/**
* 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
*
* Deprecated: 3.10: Use gtk_icon_theme_load_icon() instead.
**/
GdkPixbuf *
gtk_render_icon_pixbuf (GtkStyleContext *context,
const GtkIconSource *source,
GtkIconSize size)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
g_return_val_if_fail (size > GTK_ICON_SIZE_INVALID || size == -1, NULL);
g_return_val_if_fail (source != NULL, NULL);
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
_gtk_theming_engine_set_context (engine, context);
return engine_class->render_icon_pixbuf (engine, source, size);
}
/**
* gtk_render_icon:
* @context: a #GtkStyleContext
* @cr: a #cairo_t
* @pixbuf: a #GdkPixbuf containing the icon to draw
* @x: X position for the @pixbuf
* @y: Y position for the @pixbuf
*
* Renders the icon in @pixbuf at the specified @x and @y coordinates.
*
* Since: 3.2
**/
void
gtk_render_icon (GtkStyleContext *context,
cairo_t *cr,
GdkPixbuf *pixbuf,
gdouble x,
gdouble y)
{
GtkThemingEngineClass *engine_class;
GtkThemingEngine *engine;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
engine = _gtk_css_engine_value_get_engine (_gtk_style_context_peek_property (context, GTK_CSS_PROPERTY_ENGINE));
engine_class = GTK_THEMING_ENGINE_GET_CLASS (engine);
cairo_save (cr);
_gtk_theming_engine_set_context (engine, context);
engine_class->render_icon (engine, cr, pixbuf, x, y);
cairo_restore (cr);
}
static void
draw_insertion_cursor (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble height,
gboolean is_primary,
PangoDirection direction,
gboolean draw_arrow)
{
GdkRGBA primary_color;
GdkRGBA secondary_color;
gfloat cursor_aspect_ratio;
gint stem_width;
gint offset;
cairo_save (cr);
_gtk_style_context_get_cursor_color (context, &primary_color, &secondary_color);
gdk_cairo_set_source_rgba (cr, is_primary ? &primary_color : &secondary_color);
/* When changing the shape or size of the cursor here,
* propagate the changes to gtktextview.c:text_window_invalidate_cursors().
*/
gtk_style_context_get_style (context,
"cursor-aspect-ratio", &cursor_aspect_ratio,
NULL);
stem_width = height * cursor_aspect_ratio + 1;
/* put (stem_width % 2) on the proper side of the cursor */
if (direction == PANGO_DIRECTION_LTR)
offset = stem_width / 2;
else
offset = stem_width - stem_width / 2;
cairo_rectangle (cr, x - offset, y, stem_width, height);
cairo_fill (cr);
if (draw_arrow)
{
gint arrow_width;
gint ax, ay;
arrow_width = stem_width + 1;
if (direction == PANGO_DIRECTION_RTL)
{
ax = x - offset - 1;
ay = y + height - arrow_width * 2 - arrow_width + 1;
cairo_move_to (cr, ax, ay + 1);
cairo_line_to (cr, ax - arrow_width, ay + arrow_width);
cairo_line_to (cr, ax, ay + 2 * arrow_width);
cairo_fill (cr);
}
else if (direction == PANGO_DIRECTION_LTR)
{
ax = x + stem_width - offset;
ay = y + height - arrow_width * 2 - arrow_width + 1;
cairo_move_to (cr, ax, ay + 1);
cairo_line_to (cr, ax + arrow_width, ay + arrow_width);
cairo_line_to (cr, ax, ay + 2 * arrow_width);
cairo_fill (cr);
}
else
g_assert_not_reached();
}
cairo_restore (cr);
}
/**
* gtk_render_insertion_cursor:
* @context: a #GtkStyleContext
* @cr: a #cairo_t
* @x: X origin
* @y: Y origin
* @layout: the #PangoLayout of the text
* @index: the index in the #PangoLayout
* @direction: the #PangoDirection of the text
*
* Draws a text caret on @cr at the specified index of @layout.
*
* Since: 3.4
**/
void
gtk_render_insertion_cursor (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
PangoLayout *layout,
int index,
PangoDirection direction)
{
GtkStyleContextPrivate *priv;
gboolean split_cursor;
PangoRectangle strong_pos, weak_pos;
PangoRectangle *cursor1, *cursor2;
PangoDirection keymap_direction;
PangoDirection direction2;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
g_return_if_fail (PANGO_IS_LAYOUT (layout));
g_return_if_fail (index >= 0);
priv = context->priv;
g_object_get (gtk_settings_get_for_screen (priv->screen),
"gtk-split-cursor", &split_cursor,
NULL);
keymap_direction = gdk_keymap_get_direction (gdk_keymap_get_for_display (gdk_screen_get_display (priv->screen)));
pango_layout_get_cursor_pos (layout, index, &strong_pos, &weak_pos);
direction2 = PANGO_DIRECTION_NEUTRAL;
if (split_cursor)
{
cursor1 = &strong_pos;
if (strong_pos.x != weak_pos.x || strong_pos.y != weak_pos.y)
{
direction2 = (direction == PANGO_DIRECTION_LTR) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
cursor2 = &weak_pos;
}
}
else
{
if (keymap_direction == direction)
cursor1 = &strong_pos;
else
cursor1 = &weak_pos;
}
draw_insertion_cursor (context,
cr,
x + PANGO_PIXELS (cursor1->x),
y + PANGO_PIXELS (cursor1->y),
PANGO_PIXELS (cursor1->height),
TRUE,
direction,
direction2 != PANGO_DIRECTION_NEUTRAL);
if (direction2 != PANGO_DIRECTION_NEUTRAL)
{
draw_insertion_cursor (context,
cr,
x + PANGO_PIXELS (cursor2->x),
y + PANGO_PIXELS (cursor2->y),
PANGO_PIXELS (cursor2->height),
FALSE,
direction2,
TRUE);
}
}
/**
* gtk_draw_insertion_cursor:
* @widget: a #GtkWidget
* @cr: cairo context to draw to
* @location: location where to draw the cursor (@location->width is ignored)
* @is_primary: if the cursor should be the primary cursor color.
* @direction: whether the cursor is left-to-right or
* 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.
*
* Since: 3.0
* Deprecated: 3.4: Use gtk_render_insertion_cursor() instead.
*/
void
gtk_draw_insertion_cursor (GtkWidget *widget,
cairo_t *cr,
const GdkRectangle *location,
gboolean is_primary,
GtkTextDirection direction,
gboolean draw_arrow)
{
GtkStyleContext *context;
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (cr != NULL);
g_return_if_fail (location != NULL);
g_return_if_fail (direction != GTK_TEXT_DIR_NONE);
context = gtk_widget_get_style_context (widget);
draw_insertion_cursor (context, cr,
location->x, location->y, location->height,
is_primary,
(direction == GTK_TEXT_DIR_RTL) ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR,
draw_arrow);
}
/**
* _gtk_style_context_get_changes:
* @context: the context to query
*
* Queries the context for the changes for the currently executing
* GtkStyleContext::invalidate signal. If no signal is currently
* emitted, this function returns %NULL.
*
* FIXME 4.0: Make this part of the signal.
*
* Returns: %NULL or the currently invalidating changes
**/
const GtkBitmask *
_gtk_style_context_get_changes (GtkStyleContext *context)
{
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
return context->priv->invalidating_context;
}
static AtkAttributeSet *
add_attribute (AtkAttributeSet *attributes,
AtkTextAttribute attr,
const gchar *value)
{
AtkAttribute *at;
at = g_new (AtkAttribute, 1);
at->name = g_strdup (atk_text_attribute_get_name (attr));
at->value = g_strdup (value);
return g_slist_prepend (attributes, at);
}
/*
* _gtk_style_context_get_attributes:
* @attributes: a #AtkAttributeSet to add attributes to
* @context: the #GtkStyleContext to get attributes from
* @flags: the state to use with @context
*
* Adds the foreground and background color from @context to
* @attributes, after translating them to ATK attributes.
*
* This is a convenience function that can be used in
* implementing the #AtkText interface in widgets.
*
* Returns: the modified #AtkAttributeSet
*/
AtkAttributeSet *
_gtk_style_context_get_attributes (AtkAttributeSet *attributes,
GtkStyleContext *context,
GtkStateFlags flags)
{
GdkRGBA color;
gchar *value;
gtk_style_context_get_background_color (context, flags, &color);
value = g_strdup_printf ("%u,%u,%u",
(guint) ceil (color.red * 65536 - color.red),
(guint) ceil (color.green * 65536 - color.green),
(guint) ceil (color.blue * 65536 - color.blue));
attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
g_free (value);
gtk_style_context_get_color (context, flags, &color);
value = g_strdup_printf ("%u,%u,%u",
(guint) ceil (color.red * 65536 - color.red),
(guint) ceil (color.green * 65536 - color.green),
(guint) ceil (color.blue * 65536 - color.blue));
attributes = add_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
g_free (value);
return attributes;
}
cairo_pattern_t *
gtk_gradient_resolve_for_context (GtkGradient *gradient,
GtkStyleContext *context)
{
GtkStyleContextPrivate *priv = context->priv;
GtkCssDependencies ignored = 0;
g_return_val_if_fail (gradient != NULL, NULL);
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
return _gtk_gradient_resolve_full (gradient,
GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
style_data_lookup (context)->store,
priv->parent ? style_data_lookup (priv->parent)->store : NULL,
&ignored);
}