gtk2/gtk/gtkstylecascade.c

399 lines
12 KiB
C
Raw Normal View History

/* GTK - The GIMP Toolkit
* Copyright (C) 2012 Benjamin Otte <otte@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 "gtkstylecascadeprivate.h"
#include "gtkstyleprovider.h"
#include "gtkstyleproviderprivate.h"
typedef struct _GtkStyleCascadeIter GtkStyleCascadeIter;
typedef struct _GtkStyleProviderData GtkStyleProviderData;
struct _GtkStyleCascadeIter {
int parent_index; /* pointing at last index that was returned, not next one that should be returned */
int index; /* pointing at last index that was returned, not next one that should be returned */
};
struct _GtkStyleProviderData
{
GtkStyleProvider *provider;
guint priority;
guint changed_signal_id;
};
static GtkStyleProvider *
gtk_style_cascade_iter_next (GtkStyleCascade *cascade,
GtkStyleCascadeIter *iter)
{
if (iter->parent_index > 0)
{
if (iter->index > 0)
{
GtkStyleProviderData *data, *parent_data;
data = &g_array_index (cascade->providers, GtkStyleProviderData, iter->index - 1);
parent_data = &g_array_index (cascade->parent->providers, GtkStyleProviderData, iter->parent_index - 1);
if (data->priority >= parent_data->priority)
{
iter->index--;
return data->provider;
}
else
{
iter->parent_index--;
return parent_data->provider;
}
}
else
{
iter->parent_index--;
return g_array_index (cascade->parent->providers, GtkStyleProviderData, iter->parent_index).provider;
}
}
else
{
if (iter->index > 0)
{
iter->index--;
return g_array_index (cascade->providers, GtkStyleProviderData, iter->index).provider;
}
else
{
return NULL;
}
}
}
static GtkStyleProvider *
gtk_style_cascade_iter_init (GtkStyleCascade *cascade,
GtkStyleCascadeIter *iter)
{
iter->parent_index = cascade->parent ? cascade->parent->providers->len : 0;
iter->index = cascade->providers->len;
return gtk_style_cascade_iter_next (cascade, iter);
}
static gboolean
gtk_style_cascade_get_style_property (GtkStyleProvider *provider,
GtkWidgetPath *path,
GtkStateFlags state,
GParamSpec *pspec,
GValue *value)
{
GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
GtkStyleCascadeIter iter;
GtkStyleProvider *item;
for (item = gtk_style_cascade_iter_init (cascade, &iter);
item;
item = gtk_style_cascade_iter_next (cascade, &iter))
{
if (gtk_style_provider_get_style_property (item,
path,
state,
pspec,
value))
return TRUE;
}
return FALSE;
}
static void
gtk_style_cascade_provider_iface_init (GtkStyleProviderIface *iface)
{
iface->get_style_property = gtk_style_cascade_get_style_property;
}
static GtkSettings *
gtk_style_cascade_get_settings (GtkStyleProviderPrivate *provider)
{
GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
GtkStyleCascadeIter iter;
GtkSettings *settings;
GtkStyleProvider *item;
for (item = gtk_style_cascade_iter_init (cascade, &iter);
item;
item = gtk_style_cascade_iter_next (cascade, &iter))
{
if (!GTK_IS_STYLE_PROVIDER_PRIVATE (item))
continue;
settings = _gtk_style_provider_private_get_settings (GTK_STYLE_PROVIDER_PRIVATE (item));
if (settings)
return settings;
}
return NULL;
}
static GtkCssValue *
gtk_style_cascade_get_color (GtkStyleProviderPrivate *provider,
const char *name)
{
GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
GtkStyleCascadeIter iter;
GtkCssValue *color;
GtkStyleProvider *item;
for (item = gtk_style_cascade_iter_init (cascade, &iter);
item;
item = gtk_style_cascade_iter_next (cascade, &iter))
{
if (GTK_IS_STYLE_PROVIDER_PRIVATE (item))
{
color = _gtk_style_provider_private_get_color (GTK_STYLE_PROVIDER_PRIVATE (item), name);
if (color)
return color;
}
else
{
/* If somebody hits this code path, shout at them */
}
}
return NULL;
}
static GtkCssKeyframes *
gtk_style_cascade_get_keyframes (GtkStyleProviderPrivate *provider,
const char *name)
{
GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
GtkStyleCascadeIter iter;
GtkCssKeyframes *keyframes;
GtkStyleProvider *item;
for (item = gtk_style_cascade_iter_init (cascade, &iter);
item;
item = gtk_style_cascade_iter_next (cascade, &iter))
{
if (!GTK_IS_STYLE_PROVIDER_PRIVATE (item))
continue;
keyframes = _gtk_style_provider_private_get_keyframes (GTK_STYLE_PROVIDER_PRIVATE (item), name);
if (keyframes)
return keyframes;
}
return NULL;
}
static void
gtk_style_cascade_lookup (GtkStyleProviderPrivate *provider,
const GtkCssMatcher *matcher,
GtkCssLookup *lookup,
GtkCssChange *change)
{
GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
GtkStyleCascadeIter iter;
GtkStyleProvider *item;
GtkCssChange iter_change;
for (item = gtk_style_cascade_iter_init (cascade, &iter);
item;
item = gtk_style_cascade_iter_next (cascade, &iter))
{
if (GTK_IS_STYLE_PROVIDER_PRIVATE (item))
{
_gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (item),
matcher,
lookup,
change ? &iter_change : NULL);
if (change)
*change |= iter_change;
}
else
{
/* you lose */
g_warn_if_reached ();
}
}
}
static void
gtk_style_cascade_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface)
{
iface->get_color = gtk_style_cascade_get_color;
iface->get_settings = gtk_style_cascade_get_settings;
iface->get_keyframes = gtk_style_cascade_get_keyframes;
iface->lookup = gtk_style_cascade_lookup;
}
G_DEFINE_TYPE_EXTENDED (GtkStyleCascade, _gtk_style_cascade, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
gtk_style_cascade_provider_iface_init)
G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER_PRIVATE,
gtk_style_cascade_provider_private_iface_init));
static void
gtk_style_cascade_dispose (GObject *object)
{
GtkStyleCascade *cascade = GTK_STYLE_CASCADE (object);
_gtk_style_cascade_set_parent (cascade, NULL);
g_array_unref (cascade->providers);
G_OBJECT_CLASS (_gtk_style_cascade_parent_class)->dispose (object);
}
static void
_gtk_style_cascade_class_init (GtkStyleCascadeClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = gtk_style_cascade_dispose;
}
static void
style_provider_data_clear (gpointer data_)
{
GtkStyleProviderData *data = data_;
g_signal_handler_disconnect (data->provider, data->changed_signal_id);
g_object_unref (data->provider);
}
static void
_gtk_style_cascade_init (GtkStyleCascade *cascade)
{
cascade->scale = 1;
cascade->providers = g_array_new (FALSE, FALSE, sizeof (GtkStyleProviderData));
g_array_set_clear_func (cascade->providers, style_provider_data_clear);
}
GtkStyleCascade *
_gtk_style_cascade_new (void)
{
return g_object_new (GTK_TYPE_STYLE_CASCADE, NULL);
}
void
_gtk_style_cascade_set_parent (GtkStyleCascade *cascade,
GtkStyleCascade *parent)
{
g_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
g_return_if_fail (parent == NULL || GTK_IS_STYLE_CASCADE (parent));
if (parent)
g_return_if_fail (parent->parent == NULL);
if (cascade->parent == parent)
return;
if (parent)
{
g_object_ref (parent);
g_signal_connect_swapped (parent,
"-gtk-private-changed",
G_CALLBACK (_gtk_style_provider_private_changed),
cascade);
}
if (cascade->parent)
{
g_signal_handlers_disconnect_by_func (cascade->parent,
_gtk_style_provider_private_changed,
cascade);
g_object_unref (cascade->parent);
}
cascade->parent = parent;
}
void
_gtk_style_cascade_add_provider (GtkStyleCascade *cascade,
GtkStyleProvider *provider,
guint priority)
{
GtkStyleProviderData data;
guint i;
g_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
g_return_if_fail (GTK_STYLE_PROVIDER (cascade) != provider);
data.provider = g_object_ref (provider);
data.priority = priority;
data.changed_signal_id = g_signal_connect_swapped (provider,
"-gtk-private-changed",
G_CALLBACK (_gtk_style_provider_private_changed),
cascade);
/* ensure it gets removed first */
_gtk_style_cascade_remove_provider (cascade, provider);
for (i = 0; i < cascade->providers->len; i++)
{
if (g_array_index (cascade->providers, GtkStyleProviderData, i).priority > priority)
break;
}
g_array_insert_val (cascade->providers, i, data);
_gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (cascade));
}
void
_gtk_style_cascade_remove_provider (GtkStyleCascade *cascade,
GtkStyleProvider *provider)
{
guint i;
g_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
for (i = 0; i < cascade->providers->len; i++)
{
GtkStyleProviderData *data = &g_array_index (cascade->providers, GtkStyleProviderData, i);
if (data->provider == provider)
{
g_array_remove_index (cascade->providers, i);
_gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (cascade));
break;
}
}
}
void
_gtk_style_cascade_set_scale (GtkStyleCascade *cascade,
int scale)
{
g_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
if (cascade->scale == scale)
return;
cascade->scale = scale;
_gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (cascade));
}
int
_gtk_style_cascade_get_scale (GtkStyleCascade *cascade)
{
g_return_val_if_fail (GTK_IS_STYLE_CASCADE (cascade), 1);
return cascade->scale;
}