gtk2/gtk/gtkstylecontext.c
Carlos Garnacho 4781f94de2 Move all theming stack to use GtkStateFlags.
This support goes from the theming engines, which are able to retrieve
style for different combined states to the CSS provider, where several
state pseudo-classes may be specified, such as:

GtkButton:active:prelight {}
2010-12-04 15:37:37 +01:00

1651 lines
42 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, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <gdk/gdk.h>
#include <stdlib.h>
#include <gobject/gvaluecollector.h>
#include "gtkstylecontext.h"
#include "gtktypebuiltins.h"
#include "gtkthemingengine.h"
#include "gtkintl.h"
#include "gtkwidget.h"
#include "gtkprivate.h"
#include "gtkalias.h"
typedef struct GtkStyleContextPrivate GtkStyleContextPrivate;
typedef struct GtkStyleProviderData GtkStyleProviderData;
typedef struct GtkStyleInfo GtkStyleInfo;
typedef struct GtkRegion GtkRegion;
typedef struct PropertyValue PropertyValue;
struct GtkRegion
{
GQuark class_quark;
GtkRegionFlags flags;
};
struct GtkStyleProviderData
{
GtkStyleProvider *provider;
guint priority;
};
struct PropertyValue
{
GType widget_type;
GParamSpec *pspec;
GValue value;
};
struct GtkStyleInfo
{
GArray *style_classes;
GArray *regions;
GtkJunctionSides junction_sides;
};
struct GtkStyleContextPrivate
{
GdkScreen *screen;
GList *providers;
GList *providers_last;
GSList *icon_factories;
GtkStyleSet *store;
GtkWidgetPath *widget_path;
GArray *property_cache;
GtkStateFlags state_flags;
GSList *info_stack;
GtkThemingEngine *theming_engine;
GtkTextDirection direction;
};
enum {
PROP_0,
PROP_SCREEN,
PROP_DIRECTION
};
static void gtk_style_context_finalize (GObject *object);
static void gtk_style_context_impl_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gtk_style_context_impl_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
G_DEFINE_TYPE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT)
static void
gtk_style_context_class_init (GtkStyleContextClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gtk_style_context_finalize;
object_class->set_property = gtk_style_context_impl_set_property;
object_class->get_property = gtk_style_context_impl_get_property;
g_object_class_install_property (object_class,
PROP_SCREEN,
g_param_spec_object ("screen",
P_("Screen"),
P_("The associated GdkScreen"),
GDK_TYPE_SCREEN,
GTK_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_DIRECTION,
g_param_spec_enum ("direction",
P_("Direction"),
P_("Text direction"),
GTK_TYPE_TEXT_DIRECTION,
GTK_TEXT_DIR_LTR,
GTK_PARAM_READWRITE));
g_type_class_add_private (object_class, sizeof (GtkStyleContextPrivate));
}
static GtkStyleInfo *
style_info_new (void)
{
GtkStyleInfo *info;
info = g_slice_new0 (GtkStyleInfo);
info->style_classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
info->regions = g_array_new (FALSE, FALSE, sizeof (GtkRegion));
return info;
}
static void
style_info_free (GtkStyleInfo *info)
{
g_array_free (info->style_classes, TRUE);
g_array_free (info->regions, TRUE);
g_slice_free (GtkStyleInfo, info);
}
static GtkStyleInfo *
style_info_copy (const GtkStyleInfo *info)
{
GtkStyleInfo *copy;
copy = style_info_new ();
g_array_insert_vals (copy->style_classes, 0,
info->style_classes->data,
info->style_classes->len);
g_array_insert_vals (copy->regions, 0,
info->regions->data,
info->regions->len);
copy->junction_sides = info->junction_sides;
return copy;
}
static void
gtk_style_context_init (GtkStyleContext *style_context)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
priv = style_context->priv = G_TYPE_INSTANCE_GET_PRIVATE (style_context,
GTK_TYPE_STYLE_CONTEXT,
GtkStyleContextPrivate);
priv->store = gtk_style_set_new ();
priv->theming_engine = (GtkThemingEngine *) gtk_theming_engine_load (NULL);
priv->direction = GTK_TEXT_DIR_RTL;
/* Create default info store */
info = style_info_new ();
priv->info_stack = g_slist_prepend (priv->info_stack, info);
}
static GtkStyleProviderData *
style_provider_data_new (GtkStyleProvider *provider,
guint priority)
{
GtkStyleProviderData *data;
data = g_slice_new (GtkStyleProviderData);
data->provider = g_object_ref (provider);
data->priority = priority;
return data;
}
static void
style_provider_data_free (GtkStyleProviderData *data)
{
g_object_unref (data->provider);
g_slice_free (GtkStyleProviderData, data);
}
static void
clear_property_cache (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
priv = context->priv;
if (priv->property_cache)
{
guint i;
for (i = 0; i < priv->property_cache->len; i++)
{
PropertyValue *node = &g_array_index (priv->property_cache, PropertyValue, i);
g_param_spec_unref (node->pspec);
g_value_unset (&node->value);
}
g_array_free (priv->property_cache, TRUE);
priv->property_cache = NULL;
}
}
static void
gtk_style_context_finalize (GObject *object)
{
GtkStyleContextPrivate *priv;
GtkStyleContext *style_context;
style_context = GTK_STYLE_CONTEXT (object);
priv = style_context->priv;
g_list_foreach (priv->providers, (GFunc) style_provider_data_free, NULL);
g_list_free (priv->providers);
clear_property_cache (GTK_STYLE_CONTEXT (object));
g_slist_foreach (priv->info_stack, (GFunc) style_info_free, NULL);
g_slist_free (priv->info_stack);
g_slist_foreach (priv->icon_factories, (GFunc) g_object_unref, NULL);
g_slist_free (priv->icon_factories);
G_OBJECT_CLASS (gtk_style_context_parent_class)->finalize (object);
}
static void
gtk_style_context_impl_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkStyleContextPrivate *priv;
GtkStyleContext *style_context;
style_context = GTK_STYLE_CONTEXT (object);
priv = style_context->priv;
switch (prop_id)
{
case PROP_SCREEN:
gtk_style_context_set_screen (style_context,
g_value_get_object (value));
break;
case PROP_DIRECTION:
gtk_style_context_set_direction (style_context,
g_value_get_enum (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_style_context_impl_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkStyleContextPrivate *priv;
GtkStyleContext *style_context;
style_context = GTK_STYLE_CONTEXT (object);
priv = style_context->priv;
switch (prop_id)
{
case PROP_SCREEN:
g_value_set_object (value, priv->screen);
break;
case PROP_DIRECTION:
g_value_set_enum (value, priv->direction);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
rebuild_properties (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
GList *list;
priv = context->priv;
list = priv->providers;
gtk_style_set_clear (priv->store);
while (list)
{
GtkStyleProviderData *data;
GtkStyleSet *provider_style;
data = list->data;
list = list->next;
provider_style = gtk_style_provider_get_style (data->provider,
priv->widget_path);
if (provider_style)
{
gtk_style_set_merge (priv->store, provider_style, TRUE);
g_object_unref (provider_style);
}
}
gtk_style_set_get (priv->store, 0,
"engine", &priv->theming_engine,
NULL);
}
static void
rebuild_icon_factories (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
GList *providers;
priv = context->priv;
g_slist_foreach (priv->icon_factories, (GFunc) g_object_unref, NULL);
g_slist_free (priv->icon_factories);
priv->icon_factories = NULL;
for (providers = priv->providers_last; providers; providers = providers->prev)
{
GtkIconFactory *factory;
GtkStyleProviderData *data;
data = providers->data;
factory = gtk_style_provider_get_icon_factory (data->provider,
priv->widget_path);
if (factory)
priv->icon_factories = g_slist_prepend (priv->icon_factories, factory);
}
}
void
gtk_style_context_add_provider (GtkStyleContext *context,
GtkStyleProvider *provider,
guint priority)
{
GtkStyleContextPrivate *priv;
GtkStyleProviderData *new_data;
gboolean added = FALSE;
GList *list;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
priv = context->priv;
new_data = style_provider_data_new (provider, priority);
list = priv->providers;
while (list)
{
GtkStyleProviderData *data;
data = list->data;
/* Provider was already attached to the style
* context, remove in order to add the new data
*/
if (data->provider == provider)
{
GList *link;
link = list;
list = list->next;
/* Remove and free link */
priv->providers = g_list_remove_link (priv->providers, link);
style_provider_data_free (link->data);
g_list_free_1 (link);
continue;
}
if (!added &&
data->priority > priority)
{
priv->providers = g_list_insert_before (priv->providers, list, new_data);
added = TRUE;
}
list = list->next;
}
if (!added)
priv->providers = g_list_append (priv->providers, new_data);
priv->providers_last = g_list_last (priv->providers);
if (priv->widget_path)
{
rebuild_properties (context);
clear_property_cache (context);
rebuild_icon_factories (context);
}
}
void
gtk_style_context_remove_provider (GtkStyleContext *context,
GtkStyleProvider *provider)
{
GtkStyleContextPrivate *priv;
gboolean removed = FALSE;
GList *list;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
priv = context->priv;
list = priv->providers;
while (list)
{
GtkStyleProviderData *data;
data = list->data;
if (data->provider == provider)
{
priv->providers = g_list_remove_link (priv->providers, list);
style_provider_data_free (list->data);
g_list_free_1 (list);
removed = TRUE;
break;
}
list = list->next;
}
if (removed)
{
priv->providers_last = g_list_last (priv->providers);
if (priv->widget_path)
{
rebuild_properties (context);
clear_property_cache (context);
rebuild_icon_factories (context);
}
}
}
void
gtk_style_context_get_property (GtkStyleContext *context,
const gchar *property,
GtkStateFlags state,
GValue *value)
{
GtkStyleContextPrivate *priv;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (property != NULL);
g_return_if_fail (value != NULL);
priv = context->priv;
gtk_style_set_get_property (priv->store, property, state, value);
}
void
gtk_style_context_get_valist (GtkStyleContext *context,
GtkStateFlags state,
va_list args)
{
GtkStyleContextPrivate *priv;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
gtk_style_set_get_valist (priv->store, state, args);
}
void
gtk_style_context_get (GtkStyleContext *context,
GtkStateFlags state,
...)
{
GtkStyleContextPrivate *priv;
va_list args;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
va_start (args, state);
gtk_style_set_get_valist (priv->store, state, args);
va_end (args);
}
void
gtk_style_context_set_state (GtkStyleContext *context,
GtkStateFlags flags)
{
GtkStyleContextPrivate *priv;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
priv->state_flags = flags;
}
GtkStateFlags
gtk_style_context_get_state (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
priv = context->priv;
return priv->state_flags;
}
gboolean
gtk_style_context_is_state_set (GtkStyleContext *context,
GtkStateType state)
{
GtkStyleContextPrivate *priv;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
priv = context->priv;
switch (state)
{
case GTK_STATE_NORMAL:
return priv->state_flags == 0;
case GTK_STATE_ACTIVE:
return priv->state_flags & GTK_STATE_FLAG_ACTIVE;
case GTK_STATE_PRELIGHT:
return priv->state_flags & GTK_STATE_FLAG_PRELIGHT;
case GTK_STATE_SELECTED:
return priv->state_flags & GTK_STATE_FLAG_SELECTED;
case GTK_STATE_INSENSITIVE:
return priv->state_flags & GTK_STATE_FLAG_INSENSITIVE;
case GTK_STATE_INCONSISTENT:
return priv->state_flags & GTK_STATE_FLAG_INCONSISTENT;
case GTK_STATE_FOCUSED:
return priv->state_flags & GTK_STATE_FLAG_FOCUSED;
default:
return FALSE;
}
}
void
gtk_style_context_set_path (GtkStyleContext *context,
GtkWidgetPath *path)
{
GtkStyleContextPrivate *priv;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (path != NULL);
priv = context->priv;
if (priv->widget_path)
{
gtk_widget_path_free (priv->widget_path);
priv->widget_path = NULL;
}
if (path)
{
priv->widget_path = gtk_widget_path_copy (path);
rebuild_properties (context);
clear_property_cache (context);
rebuild_icon_factories (context);
}
}
G_CONST_RETURN GtkWidgetPath *
gtk_style_context_get_path (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
priv = context->priv;
return priv->widget_path;
}
void
gtk_style_context_save (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
g_assert (priv->info_stack != NULL);
info = style_info_copy (priv->info_stack->data);
priv->info_stack = g_slist_prepend (priv->info_stack, info);
}
void
gtk_style_context_restore (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
if (priv->info_stack)
{
info = priv->info_stack->data;
priv->info_stack = g_slist_remove (priv->info_stack, info);
style_info_free (info);
}
if (!priv->info_stack)
{
g_warning ("Unpaired gtk_style_context_restore() call");
/* Create default region */
info = style_info_new ();
priv->info_stack = g_slist_prepend (priv->info_stack, info);
}
if (priv->widget_path)
{
guint i;
info = priv->info_stack->data;
/* Update widget path regions */
gtk_widget_path_iter_clear_regions (priv->widget_path, 0);
for (i = 0; i < info->regions->len; i++)
{
GtkRegion *region;
region = &g_array_index (info->regions, GtkRegion, i);
gtk_widget_path_iter_add_region (priv->widget_path, 0,
g_quark_to_string (region->class_quark),
region->flags);
}
/* Update widget path classes */
gtk_widget_path_iter_clear_classes (priv->widget_path, 0);
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 (priv->widget_path, 0,
g_quark_to_string (quark));
}
}
}
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;
}
void
gtk_style_context_set_class (GtkStyleContext *context,
const gchar *class_name)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
GQuark class_quark;
guint position;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (class_name != NULL);
priv = context->priv;
class_quark = g_quark_from_string (class_name);
g_assert (priv->info_stack != NULL);
info = priv->info_stack->data;
if (!style_class_find (info->style_classes, class_quark, &position))
{
g_array_insert_val (info->style_classes, position, class_quark);
if (priv->widget_path)
{
gtk_widget_path_iter_add_class (priv->widget_path, 0, class_name);
rebuild_properties (context);
}
}
}
void
gtk_style_context_unset_class (GtkStyleContext *context,
const gchar *class_name)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
GQuark class_quark;
guint position;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (class_name != NULL);
class_quark = g_quark_try_string (class_name);
if (!class_quark)
return;
priv = context->priv;
g_assert (priv->info_stack != NULL);
info = priv->info_stack->data;
if (style_class_find (info->style_classes, class_quark, &position))
{
g_array_remove_index (info->style_classes, position);
if (priv->widget_path)
{
gtk_widget_path_iter_remove_class (priv->widget_path, 0, class_name);
rebuild_properties (context);
}
}
}
gboolean
gtk_style_context_has_class (GtkStyleContext *context,
const gchar *class_name)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
GQuark class_quark;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
g_return_val_if_fail (class_name != NULL, FALSE);
class_quark = g_quark_try_string (class_name);
if (!class_quark)
return FALSE;
priv = context->priv;
g_assert (priv->info_stack != NULL);
info = priv->info_stack->data;
if (style_class_find (info->style_classes, class_quark, NULL))
return TRUE;
return FALSE;
}
GList *
gtk_style_context_list_classes (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
GList *classes = NULL;
guint i;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
priv = context->priv;
g_assert (priv->info_stack != NULL);
info = priv->info_stack->data;
for (i = 0; i < info->style_classes->len; i++)
{
GQuark quark;
quark = g_array_index (info->style_classes, GQuark, i);
classes = g_list_prepend (classes, (gchar *) g_quark_to_string (quark));
}
return classes;
}
GList *
gtk_style_context_list_regions (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
GList *classes = NULL;
guint i;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
priv = context->priv;
g_assert (priv->info_stack != NULL);
info = priv->info_stack->data;
for (i = 0; i < info->regions->len; i++)
{
GtkRegion *region;
const gchar *class_name;
region = &g_array_index (info->regions, GtkRegion, i);
class_name = g_quark_to_string (region->class_quark);
classes = g_list_prepend (classes, (gchar *) class_name);
}
return classes;
}
void
gtk_style_context_set_region (GtkStyleContext *context,
const gchar *class_name,
GtkRegionFlags flags)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
GQuark class_quark;
guint position;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (class_name != NULL);
priv = context->priv;
class_quark = g_quark_from_string (class_name);
g_assert (priv->info_stack != NULL);
info = priv->info_stack->data;
if (!region_find (info->regions, class_quark, &position))
{
GtkRegion region;
region.class_quark = class_quark;
region.flags = flags;
g_array_insert_val (info->regions, position, region);
if (priv->widget_path)
{
gtk_widget_path_iter_add_region (priv->widget_path, 0, class_name, flags);
rebuild_properties (context);
}
}
}
void
gtk_style_context_unset_region (GtkStyleContext *context,
const gchar *class_name)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
GQuark class_quark;
guint position;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (class_name != NULL);
class_quark = g_quark_try_string (class_name);
if (!class_quark)
return;
priv = context->priv;
g_assert (priv->info_stack != NULL);
info = priv->info_stack->data;
if (region_find (info->regions, class_quark, &position))
{
g_array_remove_index (info->regions, position);
if (priv->widget_path)
{
gtk_widget_path_iter_remove_region (priv->widget_path, 0, class_name);
rebuild_properties (context);
}
}
}
gboolean
gtk_style_context_has_region (GtkStyleContext *context,
const gchar *class_name,
GtkRegionFlags *flags_return)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
GQuark class_quark;
guint position;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
g_return_val_if_fail (class_name != NULL, FALSE);
if (flags_return)
*flags_return = 0;
class_quark = g_quark_try_string (class_name);
if (!class_quark)
return FALSE;
priv = context->priv;
g_assert (priv->info_stack != NULL);
info = priv->info_stack->data;
if (region_find (info->regions, class_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->pspec < val2->pspec ? -1 : val1->pspec == val2->pspec ? 0 : 1;
else
return val1->widget_type < val2->widget_type ? -1 : 1;
}
const GValue *
_gtk_style_context_peek_style_property (GtkStyleContext *context,
GType widget_type,
GParamSpec *pspec)
{
GtkStyleContextPrivate *priv;
PropertyValue *pcache, key = { 0 };
GList *list;
guint i;
priv = context->priv;
key.widget_type = widget_type;
key.pspec = pspec;
/* need value cache array */
if (!priv->property_cache)
priv->property_cache = g_array_new (FALSE, FALSE, sizeof (PropertyValue));
else
{
pcache = bsearch (&key,
priv->property_cache->data, priv->property_cache->len,
sizeof (PropertyValue), style_property_values_cmp);
if (pcache)
return &pcache->value;
}
i = 0;
while (i < priv->property_cache->len &&
style_property_values_cmp (&key, &g_array_index (priv->property_cache, PropertyValue, i)) >= 0)
i++;
g_array_insert_val (priv->property_cache, i, key);
pcache = &g_array_index (priv->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_path)
{
for (list = priv->providers_last; list; list = list->prev)
{
GtkStyleProviderData *data;
data = list->data;
if (gtk_style_provider_get_style_property (data->provider, priv->widget_path,
pspec->name, &pcache->value))
return &pcache->value;
}
}
/* not supplied by any provider, revert to default */
g_param_value_set_default (pspec, &pcache->value);
return &pcache->value;
}
void
gtk_style_context_get_style_property (GtkStyleContext *context,
const gchar *property_name,
GValue *value)
{
GtkStyleContextPrivate *priv;
GtkWidgetClass *widget_class;
GParamSpec *pspec;
const GValue *peek_value;
GType widget_type;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (property_name != NULL);
g_return_if_fail (value != NULL);
priv = context->priv;
if (!priv->widget_path)
return;
widget_type = gtk_widget_path_get_widget_type (priv->widget_path);
widget_class = g_type_class_ref (widget_type);
pspec = gtk_widget_class_find_style_property (widget_class, property_name);
g_type_class_unref (widget_class);
if (!pspec)
{
g_warning ("%s: widget class `%s' has no style property named `%s'",
G_STRLOC,
g_type_name (widget_type),
property_name);
return;
}
peek_value = _gtk_style_context_peek_style_property (context,
widget_type,
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));
}
void
gtk_style_context_get_style_valist (GtkStyleContext *context,
va_list args)
{
GtkStyleContextPrivate *priv;
const gchar *prop_name;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
prop_name = va_arg (args, const gchar *);
priv = context->priv;
if (!priv->widget_path)
return;
while (prop_name)
{
GtkWidgetClass *widget_class;
GParamSpec *pspec;
const GValue *peek_value;
GType widget_type;
gchar *error;
widget_type = gtk_widget_path_get_widget_type (priv->widget_path);
widget_class = g_type_class_ref (widget_type);
pspec = gtk_widget_class_find_style_property (widget_class, prop_name);
g_type_class_unref (widget_class);
if (!pspec)
{
g_warning ("%s: widget class `%s' has no style property named `%s'",
G_STRLOC,
g_type_name (widget_type),
prop_name);
continue;
}
peek_value = _gtk_style_context_peek_style_property (context,
widget_type,
pspec);
G_VALUE_LCOPY (peek_value, args, 0, &error);
if (error)
{
g_warning ("can't retrieve style property `%s' of type `%s': %s",
pspec->name,
G_VALUE_TYPE_NAME (peek_value),
error);
g_free (error);
}
prop_name = va_arg (args, const gchar *);
}
}
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);
}
GtkIconSet *
gtk_style_context_lookup_icon_set (GtkStyleContext *context,
const gchar *stock_id)
{
GtkStyleContextPrivate *priv;
GSList *list;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
g_return_val_if_fail (stock_id != NULL, NULL);
priv = context->priv;
for (list = priv->icon_factories; list; list = list->next)
{
GtkIconFactory *factory;
GtkIconSet *icon_set;
factory = list->data;
icon_set = gtk_icon_factory_lookup (factory, stock_id);
if (icon_set)
return icon_set;
}
return gtk_icon_factory_lookup_default (stock_id);
}
void
gtk_style_context_set_screen (GtkStyleContext *context,
GdkScreen *screen)
{
GtkStyleContextPrivate *priv;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
priv->screen = screen;
g_object_notify (G_OBJECT (context), "screen");
}
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;
}
void
gtk_style_context_set_direction (GtkStyleContext *context,
GtkTextDirection direction)
{
GtkStyleContextPrivate *priv;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
priv->direction = direction;
g_object_notify (G_OBJECT (context), "direction");
}
GtkTextDirection
gtk_style_context_get_direction (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), GTK_TEXT_DIR_LTR);
priv = context->priv;
return priv->direction;
}
void
gtk_style_context_set_junction_sides (GtkStyleContext *context,
GtkJunctionSides sides)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
priv = context->priv;
info = priv->info_stack->data;
info->junction_sides = sides;
}
GtkJunctionSides
gtk_style_context_get_junction_sides (GtkStyleContext *context)
{
GtkStyleContextPrivate *priv;
GtkStyleInfo *info;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);
priv = context->priv;
info = priv->info_stack->data;
return info->junction_sides;
}
gboolean
gtk_style_context_lookup_color (GtkStyleContext *context,
const gchar *color_name,
GdkColor *color)
{
GtkStyleContextPrivate *priv;
GtkSymbolicColor *sym_color;
g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);
g_return_val_if_fail (color_name != NULL, FALSE);
g_return_val_if_fail (color != NULL, FALSE);
priv = context->priv;
sym_color = gtk_style_set_lookup_color (priv->store, color_name);
if (!sym_color)
return FALSE;
return gtk_symbolic_color_resolve (sym_color, priv->store, color);
}
/* Paint methods */
void
gtk_render_check (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_check (priv->theming_engine, cr,
x, y, width, height);
}
void
gtk_render_option (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_option (priv->theming_engine, cr,
x, y, width, height);
}
void
gtk_render_arrow (GtkStyleContext *context,
cairo_t *cr,
gdouble angle,
gdouble x,
gdouble y,
gdouble size)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_arrow (priv->theming_engine, cr,
angle, x, y, size);
}
void
gtk_render_background (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_background (priv->theming_engine, cr, x, y, width, height);
}
void
gtk_render_frame (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_frame (priv->theming_engine, cr, x, y, width, height);
}
void
gtk_render_expander (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_expander (priv->theming_engine, cr, x, y, width, height);
}
void
gtk_render_focus (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_focus (priv->theming_engine, cr, x, y, width, height);
}
void
gtk_render_layout (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
PangoLayout *layout)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_layout (priv->theming_engine, cr, x, y, layout);
}
void
gtk_render_line (GtkStyleContext *context,
cairo_t *cr,
gdouble x0,
gdouble y0,
gdouble x1,
gdouble y1)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_line (priv->theming_engine, cr, x0, y0, x1, y1);
}
void
gtk_render_slider (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height,
GtkOrientation orientation)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_slider (priv->theming_engine, cr, x, y, width, height, orientation);
}
void
gtk_render_frame_gap (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height,
GtkPositionType gap_side,
gdouble xy0_gap,
gdouble xy1_gap)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_frame_gap (priv->theming_engine, cr,
x, y, width, height, gap_side,
xy0_gap, xy1_gap);
}
void
gtk_render_extension (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height,
GtkPositionType gap_side)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_extension (priv->theming_engine, cr, x, y, width, height, gap_side);
}
void
gtk_render_handle (GtkStyleContext *context,
cairo_t *cr,
gdouble x,
gdouble y,
gdouble width,
gdouble height,
GtkOrientation orientation)
{
GtkStyleContextPrivate *priv;
GtkThemingEngineClass *engine_class;
g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
g_return_if_fail (cr != NULL);
priv = context->priv;
engine_class = GTK_THEMING_ENGINE_GET_CLASS (priv->theming_engine);
_gtk_theming_engine_set_context (priv->theming_engine, context);
engine_class->render_handle (priv->theming_engine, cr, x, y, width, height, orientation);
}
#define __GTK_STYLE_CONTEXT_C__
#include "gtkaliasdef.c"