diff --git a/docs/reference/glib/tmpl/macros_misc.sgml b/docs/reference/glib/tmpl/macros_misc.sgml index 7b93d0a7e..cd71ee73b 100644 --- a/docs/reference/glib/tmpl/macros_misc.sgml +++ b/docs/reference/glib/tmpl/macros_misc.sgml @@ -71,6 +71,7 @@ arrays or arrays on the stack. Portable way to copy va_list variables. + @ap1: the va_list variable to place a copy of @ap2 in. @ap2: a va_list. diff --git a/docs/reference/gobject/tmpl/gboxed.sgml b/docs/reference/gobject/tmpl/gboxed.sgml index 0e6835741..87183cbca 100644 --- a/docs/reference/gobject/tmpl/gboxed.sgml +++ b/docs/reference/gobject/tmpl/gboxed.sgml @@ -14,12 +14,6 @@ gboxed - - - - - - diff --git a/docs/reference/gobject/tmpl/gobject-unused.sgml b/docs/reference/gobject/tmpl/gobject-unused.sgml index 0f00a4544..fcb15763b 100644 --- a/docs/reference/gobject/tmpl/gobject-unused.sgml +++ b/docs/reference/gobject/tmpl/gobject-unused.sgml @@ -1,3 +1,9 @@ + + + + + + diff --git a/docs/reference/gobject/tmpl/param_specs.sgml b/docs/reference/gobject/tmpl/param_specs.sgml index d57c0a40c..fa1123f70 100644 --- a/docs/reference/gobject/tmpl/param_specs.sgml +++ b/docs/reference/gobject/tmpl/param_specs.sgml @@ -25,6 +25,7 @@ Parameter Specifications @blurb: @flags: @value_type: +@owner_type: diff --git a/glib/gbsearcharray.h b/glib/gbsearcharray.h index e4b9201e9..1682a7606 100644 --- a/glib/gbsearcharray.h +++ b/glib/gbsearcharray.h @@ -26,7 +26,7 @@ G_BEGIN_DECLS /* helper macro to avoid signed overflow for value comparisions */ -#define G_BSEARCH_ARRAY_CMP(v1,v2) ((v1) < (v2) ? -1 : (v1) > (v2) ? 1 : 0) +#define G_BSEARCH_ARRAY_CMP(v1,v2) ((v1) < (v2) ? -1 : (v1) > (v2)) /* --- typedefs --- */ diff --git a/gobject/ChangeLog b/gobject/ChangeLog index 9a107afb0..1993ca34e 100644 --- a/gobject/ChangeLog +++ b/gobject/ChangeLog @@ -1,3 +1,24 @@ +Mon Jun 11 17:07:06 2001 Tim Janik + + * gboxed.[hc]: remove left-over usages of an anonymous GBoxed typedef. + + * gobjectnotifyqueue.c: moved property notify queue implementation + bits into this function. + + * gparam.[hc]: added g_param_spec_pool_belongings(), completed + g_param_spec_pool_list(). added GParameter for _setv() functions. + + * gobject.[hc]: use gobjectnotifyqueue.h implementation now. + got rid of properties_changed signal. + new functions g_object_newv(), g_object_class_list_properties(). + removed "properties_changed" signal. + + * gtype.[hc]: added g_type_depth() to figure number of parent + types + 1 for a type. + + * gsignal.h: add g_signal_connect() (as per owen's request) and + g_signal_connect_swapped(). + 2001-06-13 Havoc Pennington * Makefile.am (progs_LDADD): link to ./libgobject-1.3.la diff --git a/gobject/Makefile.am b/gobject/Makefile.am index 471b4ab96..22126089e 100644 --- a/gobject/Makefile.am +++ b/gobject/Makefile.am @@ -52,9 +52,11 @@ gruntime_public_h_sources = @STRIP_BEGIN@ \ gvaluearray.h \ gvaluecollector.h \ gvaluetypes.h \ + gobjectnotifyqueue.c \ @STRIP_END@ # GRuntime header files that don't get installed -gruntime_private_h_sources = +gruntime_private_h_sources = @STRIP_BEGIN@ \ +@STRIP_END@ # GRuntime C sources to build the library from gruntime_c_sources = @STRIP_BEGIN@ \ gboxed.c \ diff --git a/gobject/gboxed.c b/gobject/gboxed.c index 82fe36d7b..e61429aaf 100644 --- a/gobject/gboxed.c +++ b/gobject/gboxed.c @@ -333,7 +333,7 @@ g_boxed_type_register_static (const gchar *name, return type; } -GBoxed* +gpointer g_boxed_copy (GType boxed_type, gconstpointer src_boxed) { diff --git a/gobject/gboxed.h b/gobject/gboxed.h index 742fd1d44..1ab9f7ee4 100644 --- a/gobject/gboxed.h +++ b/gobject/gboxed.h @@ -29,14 +29,13 @@ G_BEGIN_DECLS /* --- typedefs --- */ -typedef struct _GBoxed GBoxed; typedef gpointer (*GBoxedInitFunc) (void); typedef gpointer (*GBoxedCopyFunc) (gpointer boxed); typedef void (*GBoxedFreeFunc) (gpointer boxed); /* --- prototypes --- */ -GBoxed* g_boxed_copy (GType boxed_type, +gpointer g_boxed_copy (GType boxed_type, gconstpointer src_boxed); void g_boxed_free (GType boxed_type, gpointer boxed); diff --git a/gobject/gbsearcharray.h b/gobject/gbsearcharray.h index e4b9201e9..1682a7606 100644 --- a/gobject/gbsearcharray.h +++ b/gobject/gbsearcharray.h @@ -26,7 +26,7 @@ G_BEGIN_DECLS /* helper macro to avoid signed overflow for value comparisions */ -#define G_BSEARCH_ARRAY_CMP(v1,v2) ((v1) < (v2) ? -1 : (v1) > (v2) ? 1 : 0) +#define G_BSEARCH_ARRAY_CMP(v1,v2) ((v1) < (v2) ? -1 : (v1) > (v2)) /* --- typedefs --- */ diff --git a/gobject/gobject.c b/gobject/gobject.c index e7e4566d5..0cb27a059 100644 --- a/gobject/gobject.c +++ b/gobject/gobject.c @@ -16,18 +16,17 @@ * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ +#include "gobject.h" /* * MT safe */ -#include "gobject.h" - - #include "gvaluecollector.h" #include "gsignal.h" #include "gparamspecs.h" #include "gvaluetypes.h" +#include "gobjectnotifyqueue.c" #include @@ -35,7 +34,8 @@ /* --- macros --- */ -#define PARAM_SPEC_PARAM_ID(pspec) (GPOINTER_TO_UINT (g_param_spec_get_qdata ((pspec), quark_property_id))) +#define PARAM_SPEC_PARAM_ID(pspec) ((pspec)->param_id) +#define PARAM_SPEC_SET_PARAM_ID(pspec, id) ((pspec)->param_id = (id)) /* --- signals --- */ @@ -52,10 +52,6 @@ enum { }; -/* --- typedefs --- */ -typedef struct _NotifyQueue NotifyQueue; - - /* --- prototypes --- */ static void g_object_base_class_init (GObjectClass *class); static void g_object_base_class_finalize (GObjectClass *class); @@ -93,41 +89,23 @@ static gchar* g_value_object_lcopy_value (const GValue *value, static void g_object_dispatch_properties_changed (GObject *object, guint n_pspecs, GParamSpec **pspecs); -static void g_object_properties_changed (GObject *object, - guint n_pspecs, - GParamSpec **pspecs); -static void g_object_notify_property_changed (GObject *object, - GParamSpec *pspec); -static inline NotifyQueue* object_freeze_notifies (GObject *object); -static inline void object_queue_property (GObject *object, - GParamSpec *pspec, - NotifyQueue *nqueue); -static inline void object_thaw_notifies (GObject *object, - NotifyQueue *nqueue); static inline void object_get_property (GObject *object, GParamSpec *pspec, GValue *value); static inline void object_set_property (GObject *object, GParamSpec *pspec, const GValue *value, - NotifyQueue *nqueue); + GObjectNotifyQueue *nqueue); /* --- structures --- */ -struct _NotifyQueue -{ - GSList *pspecs; - guint n_pspecs; - guint freeze_count; -}; /* --- variables --- */ -static GQuark quark_notify_queue = 0; -static GQuark quark_property_id = 0; -static GQuark quark_closure_array = 0; -static GParamSpecPool *pspec_pool = NULL; -static gulong gobject_signals[LAST_SIGNAL] = { 0, }; +static GQuark quark_closure_array = 0; +static GParamSpecPool *pspec_pool = NULL; +static GObjectNotifyContext property_notify_context = { 0, }; +static gulong gobject_signals[LAST_SIGNAL] = { 0, }; /* --- functions --- */ @@ -218,8 +196,6 @@ g_object_base_class_init (GObjectClass *class) GObjectClass *pclass = g_type_class_peek_parent (class); /* reset instance specific fields and methods that don't get inherited */ - class->n_property_specs = 0; - class->property_specs = NULL; class->construct_properties = pclass ? g_slist_copy (pclass->construct_properties) : NULL; class->get_property = NULL; class->set_property = NULL; @@ -228,7 +204,7 @@ g_object_base_class_init (GObjectClass *class) static void g_object_base_class_finalize (GObjectClass *class) { - guint i; + GList *list, *node; g_message ("finallizing base class of %s", G_OBJECT_CLASS_NAME (class)); @@ -236,26 +212,33 @@ g_object_base_class_finalize (GObjectClass *class) g_slist_free (class->construct_properties); class->construct_properties = NULL; - for (i = 0; i < class->n_property_specs; i++) + list = g_param_spec_pool_belongings (pspec_pool, G_OBJECT_CLASS_TYPE (class)); + for (node = list; node; node = node->next) { - GParamSpec *pspec = class->property_specs[i]; + GParamSpec *pspec = node->data; g_param_spec_pool_remove (pspec_pool, pspec); - g_param_spec_set_qdata (pspec, quark_property_id, NULL); + PARAM_SPEC_SET_PARAM_ID (pspec, 0); g_param_spec_unref (pspec); } - class->n_property_specs = 0; - g_free (class->property_specs); - class->property_specs = NULL; + g_list_free (list); +} + +static void +g_object_notify_dispatcher (GObject *object, + guint n_pspecs, + GParamSpec **pspecs) +{ + G_OBJECT_GET_CLASS (object)->dispatch_properties_changed (object, n_pspecs, pspecs); } static void g_object_do_class_init (GObjectClass *class) { - quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue"); - quark_property_id = g_quark_from_static_string ("GObject-property-id"); quark_closure_array = g_quark_from_static_string ("GObject-closure-array"); pspec_pool = g_param_spec_pool_new (TRUE); + property_notify_context.quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue"); + property_notify_context.dispatcher = g_object_notify_dispatcher; class->constructor = g_object_constructor; class->set_property = g_object_do_set_property; @@ -263,18 +246,8 @@ g_object_do_class_init (GObjectClass *class) class->shutdown = g_object_shutdown; class->finalize = g_object_finalize; class->dispatch_properties_changed = g_object_dispatch_properties_changed; - class->properties_changed = g_object_properties_changed; - class->notify = g_object_notify_property_changed; + class->notify = NULL; - gobject_signals[PROPERTIES_CHANGED] = - g_signal_newc ("properties_changed", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, - G_STRUCT_OFFSET (GObjectClass, properties_changed), - NULL, NULL, - g_cclosure_marshal_VOID__UINT_POINTER, - G_TYPE_NONE, - 2, G_TYPE_UINT, G_TYPE_POINTER); gobject_signals[NOTIFY] = g_signal_newc ("notify", G_TYPE_FROM_CLASS (class), @@ -291,8 +264,6 @@ g_object_class_install_property (GObjectClass *class, guint property_id, GParamSpec *pspec) { - guint i; - g_return_if_fail (G_IS_OBJECT_CLASS (class)); g_return_if_fail (G_IS_PARAM_SPEC (pspec)); if (pspec->flags & G_PARAM_WRITABLE) @@ -306,18 +277,6 @@ g_object_class_install_property (GObjectClass *class, if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) g_return_if_fail (pspec->flags & G_PARAM_WRITABLE); - /* expensive paranoia checks ;( */ - for (i = 0; i < class->n_property_specs; i++) - if (PARAM_SPEC_PARAM_ID (class->property_specs[i]) == property_id) - { - g_warning (G_STRLOC ": class `%s' already contains a property `%s' with id %u, " - "cannot install property `%s'", - G_OBJECT_CLASS_NAME (class), - class->property_specs[i]->name, - property_id, - pspec->name); - return; - } if (g_param_spec_pool_lookup (pspec_pool, pspec->name, G_OBJECT_CLASS_TYPE (class), FALSE)) { g_warning (G_STRLOC ": class `%s' already contains a property named `%s'", @@ -328,11 +287,8 @@ g_object_class_install_property (GObjectClass *class, g_param_spec_ref (pspec); g_param_spec_sink (pspec); - g_param_spec_set_qdata (pspec, quark_property_id, GUINT_TO_POINTER (property_id)); + PARAM_SPEC_SET_PARAM_ID (pspec, property_id); g_param_spec_pool_insert (pspec_pool, pspec, G_OBJECT_CLASS_TYPE (class)); - i = class->n_property_specs++; - class->property_specs = g_renew (GParamSpec*, class->property_specs, class->n_property_specs); - class->property_specs[i] = pspec; if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) class->construct_properties = g_slist_prepend (class->construct_properties, pspec); @@ -357,42 +313,22 @@ g_object_class_find_property (GObjectClass *class, TRUE); } -static void -free_notify_queue (gpointer data) +GParamSpec** /* free result */ +g_object_class_list_properties (GObjectClass *class, + guint *n_properties_p) { - NotifyQueue *nqueue = data; - - g_slist_free (nqueue->pspecs); - g_free (nqueue); -} + GParamSpec **pspecs; + guint n; -static inline NotifyQueue* -object_freeze_notifies (GObject *object) -{ - NotifyQueue *nqueue; + g_return_val_if_fail (G_IS_OBJECT_CLASS (class), NULL); - nqueue = g_object_get_qdata (object, quark_notify_queue); - if (!nqueue) - { - nqueue = g_new0 (NotifyQueue, 1); - g_object_set_qdata_full (object, quark_notify_queue, nqueue, free_notify_queue); - } - nqueue->freeze_count++; + pspecs = g_param_spec_pool_list (pspec_pool, + G_OBJECT_CLASS_TYPE (class), + &n); + if (n_properties_p) + *n_properties_p = n; - return nqueue; -} - -static inline void -object_queue_property (GObject *object, - GParamSpec *pspec, - NotifyQueue *nqueue) -{ - if (pspec->flags & G_PARAM_READABLE) - { - /* we will dedup later */ - nqueue->pspecs = g_slist_prepend (nqueue->pspecs, pspec); - nqueue->n_pspecs++; - } + return pspecs; } static void @@ -401,8 +337,8 @@ g_object_init (GObject *object) object->ref_count = 1; g_datalist_init (&object->qdata); - /* freeze object's notification queue, g_object_new_valist() takes care of that */ - object_freeze_notifies (object); + /* freeze object's notification queue, g_object_newv() preserves pairedness */ + g_object_notify_queue_freeze (object, &property_notify_context); #ifdef G_ENABLE_DEBUG IF_DEBUG (OBJECTS) @@ -505,54 +441,10 @@ g_object_finalize (GObject *object) #endif /* G_ENABLE_DEBUG */ } -static inline void -object_thaw_notifies (GObject *object, - NotifyQueue *nqueue) -{ - GParamSpec **pspecs; - GSList *slist; - guint n_pspecs = 0; - - nqueue->freeze_count--; - if (nqueue->freeze_count) - return; - g_return_if_fail (object->ref_count > 0); - - pspecs = g_new (GParamSpec*, nqueue->n_pspecs); - for (slist = nqueue->pspecs; slist; slist = slist->next) - { - GParamSpec *pspec = slist->data; - gint i = 0; - - /* dedup, make pspecs in the list unique */ - redo_dedup_check: - if (pspecs[i] == pspec) - continue; - if (++i < n_pspecs) - goto redo_dedup_check; - - pspecs[n_pspecs++] = pspec; - } - g_object_set_qdata (object, quark_notify_queue, NULL); - - if (n_pspecs) - G_OBJECT_GET_CLASS (object)->dispatch_properties_changed (object, n_pspecs, pspecs); - - g_free (pspecs); -} - static void g_object_dispatch_properties_changed (GObject *object, guint n_pspecs, GParamSpec **pspecs) -{ - g_signal_emit (object, gobject_signals[PROPERTIES_CHANGED], 0, n_pspecs, pspecs); -} - -static void -g_object_properties_changed (GObject *object, - guint n_pspecs, - GParamSpec **pspecs) { guint i; @@ -560,16 +452,6 @@ g_object_properties_changed (GObject *object, g_signal_emit (object, gobject_signals[NOTIFY], g_quark_from_string (pspecs[i]->name), pspecs[i]); } -static void -g_object_notify_property_changed (GObject *object, - GParamSpec *pspec) -{ - if (0) /* FIXME */ - g_message ("NOTIFICATION: property `%s' changed on object `%s'", - pspec->name, - G_OBJECT_TYPE_NAME (object)); -} - void g_object_freeze_notify (GObject *object) { @@ -578,7 +460,7 @@ g_object_freeze_notify (GObject *object) return; g_object_ref (object); - object_freeze_notifies (object); + g_object_notify_queue_freeze (object, &property_notify_context); g_object_unref (object); } @@ -605,10 +487,10 @@ g_object_notify (GObject *object, property_name); else { - NotifyQueue *nqueue = object_freeze_notifies (object); + GObjectNotifyQueue *nqueue = g_object_notify_queue_freeze (object, &property_notify_context); - object_queue_property (object, pspec, nqueue); - object_thaw_notifies (object, nqueue); + g_object_notify_queue_add (object, nqueue, pspec); + g_object_notify_queue_thaw (object, nqueue); } g_object_unref (object); } @@ -616,19 +498,19 @@ g_object_notify (GObject *object, void g_object_thaw_notify (GObject *object) { - NotifyQueue *nqueue; + GObjectNotifyQueue *nqueue; g_return_if_fail (G_IS_OBJECT (object)); if (!object->ref_count) return; g_object_ref (object); - nqueue = g_object_get_qdata (object, quark_notify_queue); + nqueue = g_object_notify_queue_from_object (object, &property_notify_context); if (!nqueue || !nqueue->freeze_count) g_warning (G_STRLOC ": property-changed notification for %s(%p) is not frozen", G_OBJECT_TYPE_NAME (object), object); else - object_thaw_notifies (object, nqueue); + g_object_notify_queue_thaw (object, nqueue); g_object_unref (object); } @@ -637,18 +519,16 @@ object_get_property (GObject *object, GParamSpec *pspec, GValue *value) { - GObjectClass *class; - - class = g_type_class_peek (pspec->owner_type); + GObjectClass *class = g_type_class_peek (pspec->owner_type); class->get_property (object, PARAM_SPEC_PARAM_ID (pspec), value, pspec); } static inline void -object_set_property (GObject *object, - GParamSpec *pspec, - const GValue *value, - NotifyQueue *nqueue) +object_set_property (GObject *object, + GParamSpec *pspec, + const GValue *value, + GObjectNotifyQueue *nqueue) { GValue tmp_value = { 0, }; GObjectClass *class = g_type_class_peek (pspec->owner_type); @@ -674,7 +554,7 @@ object_set_property (GObject *object, else { class->set_property (object, PARAM_SPEC_PARAM_ID (pspec), &tmp_value, pspec); - object_queue_property (object, pspec, nqueue); + g_object_notify_queue_add (object, nqueue, pspec); } g_value_unset (&tmp_value); } @@ -696,36 +576,159 @@ g_object_new (GType object_type, return object; } +gpointer +g_object_newv (GType object_type, + guint n_parameters, + GParameter *parameters) +{ + GObjectConstructParam *cparams, *oparams; + GObjectNotifyQueue *nqueue; + GObject *object; + GObjectClass *class; + GSList *slist; + guint n_total_cparams = 0, n_cparams = 0, n_oparams = 0, n_cvalues; + GValue *cvalues; + GList *clist = NULL; + guint i; + + g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL); + + class = g_type_class_ref (object_type); + for (slist = class->construct_properties; slist; slist = slist->next) + { + clist = g_list_prepend (clist, slist->data); + n_total_cparams += 1; + } + + /* collect parameters, sort into construction and normal ones */ + oparams = g_new (GObjectConstructParam, n_parameters); + cparams = g_new (GObjectConstructParam, n_total_cparams); + for (i = 0; i < n_parameters; i++) + { + GValue *value = ¶meters[i].value; + GParamSpec *pspec = g_param_spec_pool_lookup (pspec_pool, + parameters[i].name, + object_type, + TRUE); + if (!pspec) + { + g_warning ("%s: object class `%s' has no property named `%s'", + G_STRLOC, + g_type_name (object_type), + parameters[i].name); + continue; + } + if (!(pspec->flags & G_PARAM_WRITABLE)) + { + g_warning ("%s: property `%s' of object class `%s' is not writable", + G_STRLOC, + pspec->name, + g_type_name (object_type)); + continue; + } + if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) + { + GList *list = g_list_find (clist, pspec); + + if (!list) + { + g_warning (G_STRLOC ": construct property \"%s\" for object `%s' can't be set twice", + pspec->name, g_type_name (object_type)); + continue; + } + cparams[n_cparams].pspec = pspec; + cparams[n_cparams].value = value; + n_cparams++; + if (!list->prev) + clist = list->next; + else + list->prev->next = list->next; + if (list->next) + list->next->prev = list->prev; + g_list_free_1 (list); + } + else + { + oparams[n_oparams].pspec = pspec; + oparams[n_oparams].value = value; + n_oparams++; + } + } + + /* set remaining construction properties to default values */ + n_cvalues = n_total_cparams - n_cparams; + cvalues = g_new (GValue, n_cvalues); + while (clist) + { + GList *tmp = clist->next; + GParamSpec *pspec = clist->data; + GValue *value = cvalues + n_total_cparams - n_cparams - 1; + + value->g_type = 0; + g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + g_param_value_set_default (pspec, value); + + cparams[n_cparams].pspec = pspec; + cparams[n_cparams].value = value; + n_cparams++; + + g_list_free_1 (clist); + clist = tmp; + } + + /* construct object from construction parameters */ + object = class->constructor (object_type, n_total_cparams, cparams); + + /* free construction values */ + g_free (cparams); + while (n_cvalues--) + g_value_unset (cvalues + n_cvalues); + g_free (cvalues); + + /* release g_object_init() notification queue freeze_count */ + nqueue = g_object_notify_queue_freeze (object, &property_notify_context); + g_object_notify_queue_thaw (object, nqueue); + + /* set remaining properties */ + for (i = 0; i < n_oparams; i++) + object_set_property (object, oparams[i].pspec, oparams[i].value, nqueue); + g_free (oparams); + + g_type_class_unref (class); + + /* release our own freeze count and handle notifications */ + g_object_notify_queue_thaw (object, nqueue); + + return object; +} + gpointer g_object_new_valist (GType object_type, const gchar *first_property_name, va_list var_args) { - NotifyQueue *nqueue; - GObject *object; GObjectClass *class; + GParameter *params; const gchar *name; - GObjectConstructParam *cparams = NULL, *nparams = NULL; - guint n_cparams = 0, n_nparams = 0; - GSList *clist; + GObject *object; + guint n_params = 0, n_alloced_params = 16; g_return_val_if_fail (G_TYPE_IS_OBJECT (object_type), NULL); - class = g_type_class_ref (object_type); - clist = g_slist_copy (class->construct_properties); + if (!first_property_name) + return g_object_newv (object_type, 0, NULL); - /* collect parameters, sort into construction and normal ones */ + class = g_type_class_ref (object_type); + + params = g_new (GParameter, n_alloced_params); name = first_property_name; while (name) { - GValue *value; - GParamSpec *pspec; gchar *error = NULL; - - pspec = g_param_spec_pool_lookup (pspec_pool, - name, - object_type, - TRUE); + GParamSpec *pspec = g_param_spec_pool_lookup (pspec_pool, + name, + object_type, + TRUE); if (!pspec) { g_warning ("%s: object class `%s' has no property named `%s'", @@ -734,19 +737,15 @@ g_object_new_valist (GType object_type, name); break; } - if (!(pspec->flags & G_PARAM_WRITABLE)) + if (n_params >= n_alloced_params) { - g_warning ("%s: property `%s' of object class `%s' is not writable", - G_STRLOC, - pspec->name, - g_type_name (object_type)); - break; + n_alloced_params += 16; + params = g_renew (GParameter, params, n_alloced_params); } - - value = g_new (GValue, 1); - value->g_type = 0; - g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)); - G_VALUE_COLLECT (value, var_args, 0, &error); + params[n_params].name = name; + params[n_params].value.g_type = 0; + g_value_init (¶ms[n_params].value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + G_VALUE_COLLECT (¶ms[n_params].value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); @@ -757,86 +756,18 @@ g_object_new_valist (GType object_type, */ break; } - if (pspec->flags & (G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY)) - { - guint i; - - if (!n_cparams || n_cparams >= PREALLOC_CPARAMS) - cparams = g_renew (GObjectConstructParam, cparams, MAX (n_cparams + 1, PREALLOC_CPARAMS)); - cparams[n_cparams].pspec = pspec; - cparams[n_cparams].value = value; - for (i = 0; i < n_cparams; i++) /* picky, aren't we? ;) */ - if (cparams[i].pspec == pspec) - g_warning (G_STRLOC ": construct property \"%s\" for object `%s' is being set twice", - pspec->name, g_type_name (object_type)); - n_cparams++; - clist = g_slist_remove (clist, pspec); /* FIXME: unique */ - } - else - { - if (!n_nparams || n_nparams >= PREALLOC_CPARAMS) - nparams = g_renew (GObjectConstructParam, nparams, MAX (n_nparams + 1, PREALLOC_CPARAMS)); - nparams[n_nparams].pspec = pspec; - nparams[n_nparams].value = value; - n_nparams++; - } - + n_params++; name = va_arg (var_args, gchar*); } - /* construct object from construction parameters */ - while (clist) - { - GSList *tmp = clist->next; - GParamSpec *pspec = clist->data; - GValue *value = g_new (GValue, 1); + object = g_object_newv (object_type, n_params, params); - value->g_type = 0; - g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)); - g_param_value_set_default (pspec, value); - - if (!n_cparams || n_cparams >= PREALLOC_CPARAMS) - cparams = g_renew (GObjectConstructParam, cparams, MAX (n_cparams + 1, PREALLOC_CPARAMS)); - cparams[n_cparams].pspec = pspec; - cparams[n_cparams].value = value; - n_cparams++; - - g_slist_free_1 (clist); - clist = tmp; - } - object = class->constructor (object_type, n_cparams, cparams); - - /* free construction values */ - while (n_cparams--) - { - g_value_unset (cparams[n_cparams].value); - g_free (cparams[n_cparams].value); - } - g_free (cparams); - - /* release g_object_init() notification queue freeze_count */ - nqueue = object_freeze_notifies (object); - nqueue->freeze_count--; - - /* set remaining properties */ - cparams = nparams; - while (n_nparams--) - { - GValue *value = nparams->value; - GParamSpec *pspec = nparams->pspec; - - nparams++; - object_set_property (object, pspec, value, nqueue); - g_value_unset (value); - g_free (value); - } - g_free (cparams); + while (n_params--) + g_value_unset (¶ms[n_params].value); + g_free (params); g_type_class_unref (class); - /* release our own freeze count and handle notifications */ - object_thaw_notifies (object, nqueue); - return object; } @@ -853,7 +784,7 @@ g_object_constructor (GType type, /* set construction parameters */ if (n_construct_properties) { - NotifyQueue *nqueue = object_freeze_notifies (object); + GObjectNotifyQueue *nqueue = g_object_notify_queue_freeze (object, &property_notify_context); /* set construct properties */ while (n_construct_properties--) @@ -864,9 +795,9 @@ g_object_constructor (GType type, construct_params++; object_set_property (object, pspec, value, nqueue); } - nqueue->freeze_count--; + g_object_notify_queue_thaw (object, nqueue); /* the notification queue is still frozen from g_object_init(), so - * we don't need to handle it here, g_object_new_valist() takes + * we don't need to handle it here, g_object_newv() takes * care of that */ } @@ -879,13 +810,13 @@ g_object_set_valist (GObject *object, const gchar *first_property_name, va_list var_args) { - NotifyQueue *nqueue; + GObjectNotifyQueue *nqueue; const gchar *name; g_return_if_fail (G_IS_OBJECT (object)); g_object_ref (object); - nqueue = object_freeze_notifies (object); + nqueue = g_object_notify_queue_freeze (object, &property_notify_context); name = first_property_name; while (name) @@ -935,7 +866,7 @@ g_object_set_valist (GObject *object, name = va_arg (var_args, gchar*); } - object_thaw_notifies (object, nqueue); + g_object_notify_queue_thaw (object, nqueue); g_object_unref (object); } @@ -1037,7 +968,7 @@ g_object_set_property (GObject *object, const gchar *property_name, const GValue *value) { - NotifyQueue *nqueue; + GObjectNotifyQueue *nqueue; GParamSpec *pspec; g_return_if_fail (G_IS_OBJECT (object)); @@ -1045,7 +976,7 @@ g_object_set_property (GObject *object, g_return_if_fail (G_IS_VALUE (value)); g_object_ref (object); - nqueue = object_freeze_notifies (object); + nqueue = g_object_notify_queue_freeze (object, &property_notify_context); pspec = g_param_spec_pool_lookup (pspec_pool, property_name, @@ -1059,7 +990,7 @@ g_object_set_property (GObject *object, else object_set_property (object, pspec, value, nqueue); - object_thaw_notifies (object, nqueue); + g_object_notify_queue_thaw (object, nqueue); g_object_unref (object); } diff --git a/gobject/gobject.h b/gobject/gobject.h index 87f4de8fe..d46d5cefa 100644 --- a/gobject/gobject.h +++ b/gobject/gobject.h @@ -65,9 +65,7 @@ struct _GObjectClass { GTypeClass g_type_class; - /* private, these fields might vanish */ - guint n_property_specs; - GParamSpec **property_specs; + /*< private >*/ GSList *construct_properties; /* public overridable methods */ @@ -85,22 +83,19 @@ struct _GObjectClass void (*shutdown) (GObject *object); void (*finalize) (GObject *object); - /*< private >*/ + /* seldomly overidden */ void (*dispatch_properties_changed) (GObject *object, guint n_pspecs, GParamSpec **pspecs); /* signals */ - void (*properties_changed) (GObject *object, - guint n_pspecs, - GParamSpec **pspecs); void (*notify) (GObject *object, GParamSpec *pspec); }; struct _GObjectConstructParam { - GParamSpec *pspec; - GValue *value; + GParamSpec *pspec; + GValue *value; }; @@ -110,9 +105,14 @@ void g_object_class_install_property (GObjectClass *oclass, GParamSpec *pspec); GParamSpec* g_object_class_find_property (GObjectClass *oclass, const gchar *property_name); +GParamSpec**g_object_class_list_properties (GObjectClass *oclass, + guint *n_properties); gpointer g_object_new (GType object_type, const gchar *first_property_name, ...); +gpointer g_object_newv (GType object_type, + guint n_parameters, + GParameter *parameters); gpointer g_object_new_valist (GType object_type, const gchar *first_property_name, va_list var_args); @@ -189,18 +189,21 @@ guint g_signal_connect_object (gpointer instance, /* --- implementation macros --- */ -#define G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec) \ +#define G_OBJECT_WARN_INVALID_PSPEC(object, pname, property_id, pspec) \ G_STMT_START { \ GObject *_object = (GObject*) (object); \ GParamSpec *_pspec = (GParamSpec*) (pspec); \ guint _property_id = (property_id); \ - g_warning ("%s: invalid property id %u for \"%s\" of type `%s' in `%s'", \ + g_warning ("%s: invalid %s id %u for \"%s\" of type `%s' in `%s'", \ G_STRLOC, \ + (pname), \ _property_id, \ _pspec->name, \ g_type_name (G_PARAM_SPEC_TYPE (_pspec)), \ G_OBJECT_TYPE_NAME (_object)); \ } G_STMT_END +#define G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec) \ + G_OBJECT_WARN_INVALID_PSPEC ((object), "property id", (property_id), (pspec)) G_END_DECLS diff --git a/gobject/gobjectnotifyqueue.c b/gobject/gobjectnotifyqueue.c new file mode 100644 index 000000000..d51028131 --- /dev/null +++ b/gobject/gobjectnotifyqueue.c @@ -0,0 +1,161 @@ +/* GObject - GLib Type, Object, Parameter and Signal Library + * Copyright (C) 1998-1999, 2000-2001 Tim Janik and Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __G_NOTIFY_H__ +#define __G_NOTIFY_H__ + +#include + +G_BEGIN_DECLS + + +/* --- typedefs --- */ +typedef struct _GObjectNotifyContext GObjectNotifyContext; +typedef struct _GObjectNotifyQueue GObjectNotifyQueue; +typedef void (*GObjectNotifyQueueDispatcher) (GObject *object, + guint n_pspecs, + GParamSpec **pspecs); + + +/* --- structures --- */ +struct _GObjectNotifyContext +{ + GQuark quark_notify_queue; + GObjectNotifyQueueDispatcher dispatcher; + GTrashStack *nqueue_trash; +}; +struct _GObjectNotifyQueue +{ + GObjectNotifyContext *context; + GSList *pspecs; + guint n_pspecs; + guint freeze_count; +}; + + +/* --- functions --- */ +static void +g_object_notify_queue_free (gpointer data) +{ + GObjectNotifyQueue *nqueue = data; + + g_slist_free (nqueue->pspecs); + g_trash_stack_push (&nqueue->context->nqueue_trash, nqueue); +} + +static inline GObjectNotifyQueue* +g_object_notify_queue_freeze (GObject *object, + GObjectNotifyContext *context) +{ + GObjectNotifyQueue *nqueue; + + nqueue = g_datalist_id_get_data (&object->qdata, context->quark_notify_queue); + if (!nqueue) + { + nqueue = g_trash_stack_pop (&context->nqueue_trash); + if (!nqueue) + { + guint i; + + nqueue = g_new (GObjectNotifyQueue, 16); + for (i = 0; i < 15; i++) + g_trash_stack_push (&context->nqueue_trash, nqueue++); + } + memset (nqueue, 0, sizeof (*nqueue)); + nqueue->context = context; + g_datalist_id_set_data_full (&object->qdata, context->quark_notify_queue, + nqueue, g_object_notify_queue_free); + } + nqueue->freeze_count++; + + return nqueue; +} + +static inline void +g_object_notify_queue_thaw (GObject *object, + GObjectNotifyQueue *nqueue) +{ + GObjectNotifyContext *context = nqueue->context; + GParamSpec *pspecs_mem[16], **pspecs, **free_me = NULL; + GSList *slist; + guint n_pspecs = 0; + + g_return_if_fail (nqueue->freeze_count > 0); + + nqueue->freeze_count--; + if (nqueue->freeze_count) + return; + g_return_if_fail (object->ref_count > 0); + + pspecs = nqueue->n_pspecs > 16 ? free_me = g_new (GParamSpec*, nqueue->n_pspecs) : pspecs_mem; + for (slist = nqueue->pspecs; slist; slist = slist->next) + { + GParamSpec *pspec = slist->data; + gint i = 0; + + /* dedup, make pspecs in the list unique */ + redo_dedup_check: + if (pspecs[i] == pspec) + continue; + if (++i < n_pspecs) + goto redo_dedup_check; + + pspecs[n_pspecs++] = pspec; + } + g_datalist_id_set_data (&object->qdata, context->quark_notify_queue, NULL); + + if (n_pspecs) + context->dispatcher (object, n_pspecs, pspecs); + g_free (free_me); +} + +static inline void +g_object_notify_queue_clear (GObject *object, + GObjectNotifyQueue *nqueue) +{ + g_return_if_fail (nqueue->freeze_count > 0); + + g_slist_free (nqueue->pspecs); + nqueue->pspecs = NULL; + nqueue->n_pspecs = 0; +} + +static inline void +g_object_notify_queue_add (GObject *object, + GObjectNotifyQueue *nqueue, + GParamSpec *pspec) +{ + if (pspec->flags & G_PARAM_READABLE) + { + /* we do the deduping in _thaw */ + nqueue->pspecs = g_slist_prepend (nqueue->pspecs, pspec); + nqueue->n_pspecs++; + } +} + +static inline GObjectNotifyQueue* +g_object_notify_queue_from_object (GObject *object, + GObjectNotifyContext *context) +{ + return g_datalist_id_get_data (&object->qdata, context->quark_notify_queue); +} + + +G_END_DECLS + +#endif /* __G_OBJECT_H__ */ diff --git a/gobject/gparam.c b/gobject/gparam.c index 511717763..4fbf45588 100644 --- a/gobject/gparam.c +++ b/gobject/gparam.c @@ -144,6 +144,7 @@ g_param_spec_init (GParamSpec *pspec, pspec->owner_type = 0; pspec->qdata = NULL; pspec->ref_count = 1; + pspec->param_id = 0; g_datalist_id_set_data (&pspec->qdata, quark_floating, GUINT_TO_POINTER (TRUE)); } @@ -725,8 +726,8 @@ pool_list (gpointer key, } GList* -g_param_spec_pool_list (GParamSpecPool *pool, - GType owner_type) +g_param_spec_pool_belongings (GParamSpecPool *pool, + GType owner_type) { gpointer data[2]; @@ -742,6 +743,99 @@ g_param_spec_pool_list (GParamSpecPool *pool, return data[0]; } +static gint +pspec_compare_id (gconstpointer a, + gconstpointer b) +{ + const GParamSpec *pspec1 = a, *pspec2 = b; + + return pspec1->param_id < pspec2->param_id ? -1 : pspec1->param_id > pspec2->param_id; +} + +static inline GSList* +pspec_list_remove_overridden (GSList *plist, + GHashTable *ht, + GType owner_type, + guint *n_p) +{ + GSList *rlist = NULL; + + while (plist) + { + GSList *tmp = plist->next; + GParamSpec *pspec = plist->data; + + if (param_spec_ht_lookup (ht, pspec->name, owner_type, TRUE) != pspec) + g_slist_free_1 (plist); + else + { + plist->next = rlist; + rlist = plist; + *n_p += 1; + } + plist = tmp; + } + return rlist; +} + +static void +pool_depth_list (gpointer key, + gpointer value, + gpointer user_data) +{ + GParamSpec *pspec = value; + gpointer *data = user_data; + GSList **slists = data[0]; + GType owner_type = GPOINTER_TO_UINT (data[1]); + + if (g_type_is_a (owner_type, pspec->owner_type)) + { + guint d = g_type_depth (pspec->owner_type); + + slists[d - 1] = g_slist_prepend (slists[d - 1], pspec); + } +} + +GParamSpec** /* free result */ +g_param_spec_pool_list (GParamSpecPool *pool, + GType owner_type, + guint *n_pspecs_p) +{ + GParamSpec **pspecs, **p; + GSList **slists, *node; + gpointer data[2]; + guint d, i; + + g_return_val_if_fail (pool != NULL, NULL); + g_return_val_if_fail (owner_type > 0, NULL); + g_return_val_if_fail (n_pspecs_p != NULL, NULL); + + G_SLOCK (&pool->smutex); + *n_pspecs_p = 0; + d = g_type_depth (owner_type); + slists = g_new0 (GSList*, d); + data[0] = slists; + data[1] = GUINT_TO_POINTER (owner_type); + g_hash_table_foreach (pool->hash_table, pool_depth_list, &data); + for (i = 0; i < d - 1; i++) + slists[i] = pspec_list_remove_overridden (slists[i], pool->hash_table, owner_type, n_pspecs_p); + *n_pspecs_p += g_slist_length (slists[i]); + pspecs = g_new (GParamSpec*, *n_pspecs_p + 1); + p = pspecs; + for (i = 0; i < d; i++) + { + slists[i] = g_slist_sort (slists[i], pspec_compare_id); + for (node = slists[i]; node; node = node->next) + *p++ = node->data; + g_slist_free (slists[i]); + } + *p++ = NULL; + g_free (slists); + G_SUNLOCK (&pool->smutex); + + return pspecs; +} + /* --- auxillary functions --- */ typedef struct diff --git a/gobject/gparam.h b/gobject/gparam.h index 744a3978a..7def6d278 100644 --- a/gobject/gparam.h +++ b/gobject/gparam.h @@ -56,6 +56,7 @@ typedef enum /* --- typedefs & structures --- */ typedef struct _GParamSpec GParamSpec; typedef struct _GParamSpecClass GParamSpecClass; +typedef struct _GParameter GParameter; typedef struct _GParamSpecPool GParamSpecPool; struct _GParamSpec { @@ -66,11 +67,12 @@ struct _GParamSpec gchar *blurb; GParamFlags flags; GType value_type; + GType owner_type; /* class using this property */ /*< private >*/ - GType owner_type; GData *qdata; guint ref_count; + guint param_id; /* sort-criteria */ }; struct _GParamSpecClass { @@ -89,6 +91,11 @@ struct _GParamSpecClass const GValue *value1, const GValue *value2); }; +struct _GParameter /* auxillary structure for _setv() variants */ +{ + const gchar *name; + GValue value; +}; /* --- prototypes --- */ @@ -165,8 +172,11 @@ GParamSpec* g_param_spec_pool_lookup (GParamSpecPool *pool, const gchar *param_name, GType owner_type, gboolean walk_ancestors); -GList* g_param_spec_pool_list (GParamSpecPool *pool, +GList* g_param_spec_pool_belongings (GParamSpecPool *pool, GType owner_type); +GParamSpec** g_param_spec_pool_list (GParamSpecPool *pool, + GType owner_type, + guint *n_pspecs_p); diff --git a/gobject/gsignal.h b/gobject/gsignal.h index ddcae8037..2ba7a0bd2 100644 --- a/gobject/gsignal.h +++ b/gobject/gsignal.h @@ -222,6 +222,10 @@ guint g_signal_handlers_disconnect_matched (gpointer instance, /* --- convenience --- */ #define g_signal_connectc(instance, detailed_signal, c_handler, data, swapped) \ g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (swapped), FALSE) +#define g_signal_connect(instance, detailed_signal, c_handler, data) \ + g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, FALSE, FALSE) +#define g_signal_connect_swapped(instance, detailed_signal, c_handler, data) \ + g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, TRUE, FALSE) #define g_signal_disconnect_by_func(instance, func, data) \ g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, \ 0, 0, NULL, (func), (data)) diff --git a/gobject/gtype.c b/gobject/gtype.c index 590598c57..664580be3 100644 --- a/gobject/gtype.c +++ b/gobject/gtype.c @@ -2073,6 +2073,18 @@ g_type_parent (GType type) return node ? NODE_PARENT_TYPE (node) : 0; } +guint +g_type_depth (GType type) +{ + TypeNode *node; + + G_READ_LOCK (&type_rw_lock); + node = lookup_type_node_L (type); + G_READ_UNLOCK (&type_rw_lock); + + return node ? node->n_supers + 1 : 0; +} + GType g_type_next_base (GType type, GType base_type) diff --git a/gobject/gtype.h b/gobject/gtype.h index 2c9ed2d57..2bdb5c295 100644 --- a/gobject/gtype.h +++ b/gobject/gtype.h @@ -187,6 +187,7 @@ G_CONST_RETURN gchar* g_type_name (GType type); GQuark g_type_qname (GType type); GType g_type_from_name (const gchar *name); GType g_type_parent (GType type); +guint g_type_depth (GType type); GType g_type_next_base (GType leaf_type, GType root_type); gboolean g_type_is_a (GType type,