grim/guifications3

moved guifications-gtk to cmake
cmake
2010-12-13, Gary Kramlich
36e02fafe588
moved guifications-gtk to cmake
/*
* Guifications - The end-all, be-all notification framework
* Copyright (C) 2003-2009 Gary Kramlich <grim@reaperworld.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <gflib/gf_type.h>
#include <gflib/gf_intl.h>
#define GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE((obj), GF_TYPE_TYPE_QUERY_CONTEXT, GfTypeQueryContextPrivate))
/******************************************************************************
* structs
*****************************************************************************/
typedef struct {
GfTypeQuery *query;
gboolean recursive;
gpointer data;
GDestroyNotify destroy_notify;
} GfTypeQueryContextPrivate;
/******************************************************************************
* enums
*****************************************************************************/
enum {
PROP_ZERO,
PROP_QUERY,
PROP_RECURSIVE,
PROP_DATA,
PROP_DESTROY_NOTIFY,
PROP_LAST,
};
/******************************************************************************
* globals
*****************************************************************************/
static GObjectClass *parent_class = NULL;
/******************************************************************************
* query context stuff
*****************************************************************************/
static inline void
gf_type_query_context_real_params(GfTypeQueryContext *ctx,
GObjectClass *klass)
{
GfTypeQueryContextPrivate *priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
GParamSpec **pspecs = NULL;
guint n_pspecs = 0, p = 0;
pspecs = g_object_class_list_properties(klass, &n_pspecs);
for(p = 0; p < n_pspecs; p++)
if(priv->query->param)
priv->query->param(ctx, pspecs[p], priv->data);
g_free(pspecs);
}
static inline void
gf_type_query_context_real_signals(GfTypeQueryContext *ctx, GType type) {
GfTypeQueryContextPrivate *priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
guint *signal_ids = NULL, n_ids = 0, s = 0;
signal_ids = g_signal_list_ids(type, &n_ids);
for(s = 0; s < n_ids; s++) {
GSignalQuery query;
g_signal_query(signal_ids[s], &query);
if(priv->query->signal)
priv->query->signal(ctx, &query, priv->data);
}
g_free(signal_ids);
}
static inline void
gf_type_query_context_real_query_object(GfTypeQueryContext *ctx, GType type) {
GfTypeQueryContextPrivate *priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
GObjectClass *klass = NULL;
GTypePlugin *plugin = NULL;
GTypeQuery *query = NULL;
guint t = 0;
/* handle the passed in type */
klass = g_type_class_ref(type);
/* work around for glib bug 600505, refer to that at some point to see if
* this can be cleaned up.
*
* https://bugzilla.gnome.org/show_bug.cgi?id=600505
*/
query = g_new0(GTypeQuery, 1);
plugin = g_type_get_plugin(type);
if(plugin) {
GTypeInfo info;
GTypeValueTable table;
/* we don't use table, but it needs to be there to shutup a runtime
* warning.
*/
g_type_plugin_complete_type_info(plugin, type, &info, &table);
query->type = type;
query->type_name = g_type_name(type);
query->class_size = info.class_size;
query->instance_size = info.instance_size;
} else {
g_type_query(type, query);
}
if(priv->query->start_object)
priv->query->start_object(ctx, query, priv->data);
/* if the query doesn't have a function for params signals don't bother
* with digging through them.
*/
if(priv->query->param)
gf_type_query_context_real_params(ctx, klass);
/* ditto for signals */
if(priv->query->signal)
gf_type_query_context_real_signals(ctx, type);
/* we're done with this object. finish it up before moving to it's
* children.
*/
if(priv->query->end_object)
priv->query->end_object(ctx, query, priv->data);
/* now dig through the children if we're recursive */
if(priv->recursive) {
GType *types = g_type_children(type, NULL);
for(t = 0; types[t]; t++) {
GObjectClass *child = NULL;
child = g_type_class_ref(types[t]);
gf_type_query_context_real_query_object(ctx, types[t]);
g_type_class_unref(child);
}
g_free(types);
}
g_free(query);
g_type_class_unref(klass);
}
static void
gf_type_query_context_real_query(GfTypeQueryContext *ctx, GType type) {
gf_type_query_context_real_query_object(ctx, type);
}
/******************************************************************************
* helpers
*****************************************************************************/
static void
gf_type_query_context_set_query(GfTypeQueryContext *ctx,
GfTypeQuery *query)
{
GfTypeQueryContextPrivate *priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
priv->query = query;
}
static void
gf_type_query_context_set_data(GfTypeQueryContext *ctx, gpointer data) {
GfTypeQueryContextPrivate *priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
priv->data = data;
}
static void
gf_type_query_context_set_destroy_notify(GfTypeQueryContext *ctx,
GDestroyNotify destroy_notify)
{
GfTypeQueryContextPrivate *priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
priv->destroy_notify = destroy_notify;
}
/******************************************************************************
* object stuff
*****************************************************************************/
static void
gf_type_query_context_get_property(GObject *obj, guint param_id, GValue *value,
GParamSpec *pspec)
{
GfTypeQueryContext *ctx = GF_TYPE_QUERY_CONTEXT(obj);
switch(param_id) {
case PROP_QUERY:
g_value_set_pointer(value, gf_type_query_context_get_query(ctx));
break;
case PROP_RECURSIVE:
g_value_set_boolean(value,
gf_type_query_context_is_recursive(ctx));
break;
case PROP_DATA:
g_value_set_pointer(value,
gf_type_query_context_get_user_data(ctx));
break;
case PROP_DESTROY_NOTIFY:
g_value_set_pointer(value,
gf_type_query_context_get_destroy_notify(ctx));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
gf_type_query_context_set_property(GObject *obj, guint param_id,
const GValue *value, GParamSpec *pspec)
{
GfTypeQueryContext *ctx = GF_TYPE_QUERY_CONTEXT(obj);
switch(param_id) {
case PROP_QUERY:
gf_type_query_context_set_query(ctx, g_value_get_pointer(value));
break;
case PROP_RECURSIVE:
gf_type_query_context_set_recursive(ctx,
g_value_get_boolean(value));
break;
case PROP_DATA:
gf_type_query_context_set_data(ctx, g_value_get_pointer(value));
break;
case PROP_DESTROY_NOTIFY:
gf_type_query_context_set_destroy_notify(ctx,
(GDestroyNotify)g_value_get_pointer(value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
break;
}
}
static void
gf_type_query_context_finalize(GObject *obj) {
GfTypeQueryContextPrivate *priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(obj);
if(priv->destroy_notify)
priv->destroy_notify(priv->data);
G_OBJECT_CLASS(parent_class)->finalize(obj);
}
static void
gf_type_query_context_class_init(GfTypeQueryContextClass *klass) {
GObjectClass *obj_class = G_OBJECT_CLASS(klass);
parent_class = g_type_class_peek_parent(klass);
g_type_class_add_private(klass, sizeof(GfTypeQueryContextPrivate));
obj_class->get_property = gf_type_query_context_get_property;
obj_class->set_property = gf_type_query_context_set_property;
obj_class->finalize = gf_type_query_context_finalize;
klass->query = gf_type_query_context_real_query;
g_object_class_install_property(obj_class, PROP_QUERY,
g_param_spec_pointer("query", P_("query"),
P_("The GfTypeQuery to use."),
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property(obj_class, PROP_RECURSIVE,
g_param_spec_boolean("recursive", P_("recursive"),
P_("Whether or not to recurse into children"),
TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
g_object_class_install_property(obj_class, PROP_DATA,
g_param_spec_pointer("data", P_("data"),
P_("The user data to use while parsing."),
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property(obj_class, PROP_DESTROY_NOTIFY,
g_param_spec_pointer("destroy-notify", P_("destroy-notify"),
P_("The function to free the user data when the "
"context is freed."),
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
/******************************************************************************
* API
*****************************************************************************/
GType
gf_type_query_context_get_type(void) {
static GType type = 0;
if(G_UNLIKELY(type == 0)) {
static const GTypeInfo info = {
sizeof(GfTypeQueryContextClass),
NULL,
NULL,
(GClassInitFunc)gf_type_query_context_class_init,
NULL,
NULL,
sizeof(GfTypeQueryContext),
0,
NULL,
};
type = g_type_register_static(G_TYPE_OBJECT,
"GfTypeQueryContext",
&info, 0);
}
return type;
}
/**
* gf_type_query_context_new:
* @query: A #GfTypeQuery.
* @recursive: Whether or not to recurse into children.
* @data: user data to pass to the #GfTypeQuery functions.
* @destroy_notify: A function to free @data.
*
* Creates a new #GfTypeQueryContext using @query.
*
* Return Value: The new #GfTypeQueryContext.
*/
GfTypeQueryContext *
gf_type_query_context_new(GfTypeQuery *query, gboolean recursive,
gpointer data, GDestroyNotify destroy_notify)
{
g_return_val_if_fail(query, NULL);
return g_object_new(GF_TYPE_TYPE_QUERY_CONTEXT,
"query", query,
"recursive", recursive,
"data", data,
"destroy-notify", destroy_notify,
NULL);
}
/**
* gf_type_query_context_run:
* @ctx: The #GfTypeQueryContext instance.
* @type: The #GType to start the parsing at.
*
* Querys through all types descending from @type as well as @type itself.
*/
void
gf_type_query_context_run(GfTypeQueryContext *ctx, GType type) {
GfTypeQueryContextClass *klass = NULL;
g_return_if_fail(GF_IS_TYPE_QUERY_CONTEXT(ctx));
g_return_if_fail(type != G_TYPE_INVALID);
klass = GF_TYPE_QUERY_CONTEXT_GET_CLASS(ctx);
if(klass && klass->query)
klass->query(ctx, type);
}
/**
* gf_type_query_context_get_query:
* @ctx: The #GfTypeQueryContext instance.
*
* Gets the #GfTypeQuery used in @ctx.
*
* Return Value: The #GfTypeQuery used in @ctx.
*/
GfTypeQuery *
gf_type_query_context_get_query(const GfTypeQueryContext *ctx) {
GfTypeQueryContextPrivate *priv = NULL;
g_return_val_if_fail(GF_IS_TYPE_QUERY_CONTEXT(ctx), NULL);
priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
return priv->query;
}
/**
* gf_type_query_context_get_user_data:
* @ctx: The #GfTypeQueryContext instance.
*
* Gets the user data from @ctx.
*
* Return Value: The user data used from @ctx.
*/
gpointer
gf_type_query_context_get_user_data(const GfTypeQueryContext *ctx) {
GfTypeQueryContextPrivate *priv = NULL;
g_return_val_if_fail(GF_IS_TYPE_QUERY_CONTEXT(ctx), NULL);
priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
return priv->data;
}
/**
* gf_type_query_context_get_destroy_notify:
* @ctx: The #GfTypeQueryContext instance.
*
* Gets the destroy notify function from @ctx.
*
* Return Value: The destroy notify function from @ctx.
*/
GDestroyNotify
gf_type_query_context_get_destroy_notify(const GfTypeQueryContext *ctx) {
GfTypeQueryContextPrivate *priv = NULL;
g_return_val_if_fail(GF_IS_TYPE_QUERY_CONTEXT(ctx), NULL);
priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
return priv->destroy_notify;
}
/**
* gf_type_query_context_is_recursive:
* @ctx: The #GfTypeQueryContext instance.
*
* Gets whether or not @ctx will recurse into children.
*
* Return Value: TRUE if recursive, FALSE otherwise.
*/
gboolean
gf_type_query_context_is_recursive(const GfTypeQueryContext *ctx) {
GfTypeQueryContextPrivate *priv = NULL;
g_return_val_if_fail(GF_IS_TYPE_QUERY_CONTEXT(ctx), FALSE);
priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
return priv->recursive;
}
/**
* gf_type_query_context_set_recursive:
* @ctx: The #GfTypeQueryContext instance.
* @recursive: Whether or not to recurse into children.
*
* Set's whether or not @ctx will recurse into children.
*/
void
gf_type_query_context_set_recursive(GfTypeQueryContext *ctx,
gboolean recursive)
{
GfTypeQueryContextPrivate *priv = NULL;
g_return_if_fail(GF_IS_TYPE_QUERY_CONTEXT(ctx));
priv = GF_TYPE_QUERY_CONTEXT_GET_PRIVATE(ctx);
if(priv->recursive != recursive) {
priv->recursive = recursive;
g_object_notify(G_OBJECT(ctx), "recursive");
}
}
/******************************************************************************
* Builtin Queries
*****************************************************************************/
typedef gboolean (*GfTypeQueryBuiltInTest)(GType type, gpointer data);
typedef struct {
GQueue *queue;
GfTypeQueryBuiltInTest test;
gpointer data;
} GfTypeQueryBuiltInData;
static void
gf_type_query_built_in_object_start(GfTypeQueryContext *ctx,
const GTypeQuery *query, gpointer data)
{
GfTypeQueryBuiltInData *built_in_data = (GfTypeQueryBuiltInData *)data;
if(built_in_data->test) {
if(built_in_data->test(query->type, built_in_data->data)) {
g_queue_push_tail(built_in_data->queue, GSIZE_TO_POINTER(query->type));
}
} else {
g_queue_push_tail(built_in_data->queue, GSIZE_TO_POINTER(query->type));
}
}
static GfTypeQuery built_in_query = {
gf_type_query_built_in_object_start,
NULL,
NULL,
NULL
};
static GType *
gf_type_query_built_in_helper(GType type, GfTypeQueryBuiltInTest test,
guint *n_children, gpointer data)
{
GfTypeQueryContext *ctx = NULL;
GfTypeQueryBuiltInData *built_in_data = NULL;
GQueue *queue = NULL;
GType *types = NULL;
GList *l = NULL;
guint i = 0;
queue = g_queue_new();
built_in_data = g_new0(GfTypeQueryBuiltInData, 1);
built_in_data->queue = queue;
built_in_data->test = test;
built_in_data->data = data;
ctx = gf_type_query_context_new(&built_in_query, TRUE, built_in_data,
g_free);
gf_type_query_context_run(ctx, type);
types = g_new0(GType, queue->length + 1);
for(i = 0, l = queue->head; i < queue->length; i++, l = l->next)
types[i] = GPOINTER_TO_SIZE(l->data);
if(n_children)
*n_children = queue->length;
/* clean up the queue and the query context. The queue context will kill
* the data when it is unrefed.
*/
g_queue_free(queue);
g_object_unref(G_OBJECT(ctx));
return types;
}
/******************************************************************************
* Built in tests
*****************************************************************************/
static gboolean
gf_type_children_test_concrete(GType type, gpointer data) {
return (!g_type_test_flags(type, G_TYPE_FLAG_ABSTRACT));
}
static gboolean
gf_type_interface_implementor(GType type, gpointer data) {
GType interface = GPOINTER_TO_INT(data);
return g_type_is_a(type, interface);
}
/******************************************************************************
* Introspection API
*****************************************************************************/
/**
* gf_type_children:
* @type : The #GType of the base class who's children we want.
* @n_children : Return address for the number of children or NULL.
*
* Returns a newly allocated array of #GType's for each child of @type.
*
* Note: Unlike #g_type_children, gf_type_children returns all subclasses,
* not just direct subclasses.
*
* Return Value: A newly allocated array of #GType's for each subclass which
* must be freed with #g_free().
*/
GType *
gf_type_children(GType type, guint *n_children) {
return gf_type_query_built_in_helper(type, NULL, n_children, NULL);
}
/**
* gf_type_concrete_children:
* @type : The #GType of the base class who's children we want.
* @n_children : Return address for the number of children, or NULL.
*
* This function is identical to #gf_type_children() except that it only
* returns concrete classes, that is non-abstract classes.
*
* Return Value: A newly allocated array of #GType's for each non-abstract
* subclass, which must be freed with #g_free().
*/
GType *
gf_type_concrete_children(GType type, guint *n_children) {
return gf_type_query_built_in_helper(type,
gf_type_children_test_concrete,
n_children, NULL);
}
/**
* gf_type_interface_implementors:
* @type : The #GType of the Interface.
* @n_children : Return address for the number of children, or @NULL.
*
* This function is identical to #gf_type_children() except that it only
* returns types that implement @type.
*
* Return Value: A newly allocated array of #GType's for each implementor
* of @type.
*/
GType *
gf_type_interface_implementors(GType type, guint *n_children) {
return gf_type_query_built_in_helper(G_TYPE_OBJECT,
gf_type_interface_implementor,
n_children, GINT_TO_POINTER(type));
}