diff options
author | Chris Coulson <chrisccoulson@ubuntu.com> | 2011-02-24 15:43:25 +0000 |
---|---|---|
committer | Chris Coulson <chrisccoulson@ubuntu.com> | 2011-02-24 15:43:25 +0000 |
commit | 37fb090b4f9fb1e1a894c5c57e7c3d28eb71f92d (patch) | |
tree | 6d1140f1c4f04c82c8d9951151c4eb5dcffce654 /libdbusmenu-glib | |
parent | 5653c7e5642ae25fb54445f749ecfb0c947ead0c (diff) | |
download | libdbusmenu-37fb090b4f9fb1e1a894c5c57e7c3d28eb71f92d.tar.gz libdbusmenu-37fb090b4f9fb1e1a894c5c57e7c3d28eb71f92d.tar.bz2 libdbusmenu-37fb090b4f9fb1e1a894c5c57e7c3d28eb71f92d.zip |
Hold a weak pointer to each DbusmenuMenuitems parent. This avoids
using g_object_{get/set}_data for storing a nodes parent in the parser code,
and the associated problems with that (ie, a child out-living its parent, leading
to invalid reads
Diffstat (limited to 'libdbusmenu-glib')
-rw-r--r-- | libdbusmenu-glib/menuitem.c | 102 | ||||
-rw-r--r-- | libdbusmenu-glib/menuitem.h | 4 |
2 files changed, 104 insertions, 2 deletions
diff --git a/libdbusmenu-glib/menuitem.c b/libdbusmenu-glib/menuitem.c index 89b2b91..49b83ce 100644 --- a/libdbusmenu-glib/menuitem.c +++ b/libdbusmenu-glib/menuitem.c @@ -62,6 +62,7 @@ struct _DbusmenuMenuitemPrivate gboolean realized; DbusmenuDefaults * defaults; gboolean exposed; + DbusmenuMenuitem * parent; }; /* Signals */ @@ -339,6 +340,11 @@ dbusmenu_menuitem_dispose (GObject *object) priv->defaults = NULL; } + if (priv->parent) { + g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent); + priv->parent = NULL; + } + G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->dispose (object); return; } @@ -565,11 +571,12 @@ dbusmenu_menuitem_get_children (DbusmenuMenuitem * mi) /* For all the taken children we need to signal that they were removed */ static void -take_children_signal (gpointer data, gpointer user_data) +take_children_helper (gpointer data, gpointer user_data) { #ifdef MASSIVEDEBUGGING g_debug("Menuitem %d (%s) signalling child removed %d (%s)", ID(user_data), LABEL(user_data), ID(data), LABEL(data)); #endif + dbusmenu_menuitem_unparent(DBUSMENU_MENUITEM(data)); g_signal_emit(G_OBJECT(user_data), signals[CHILD_REMOVED], 0, DBUSMENU_MENUITEM(data), TRUE); return; } @@ -595,7 +602,7 @@ dbusmenu_menuitem_take_children (DbusmenuMenuitem * mi) DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); GList * children = priv->children; priv->children = NULL; - g_list_foreach(children, take_children_signal, mi); + g_list_foreach(children, take_children_helper, mi); dbusmenu_menuitem_property_remove(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY); @@ -704,6 +711,10 @@ dbusmenu_menuitem_child_append (DbusmenuMenuitem * mi, DbusmenuMenuitem * child) DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); g_return_val_if_fail(g_list_find(priv->children, child) == NULL, FALSE); + if (!dbusmenu_menuitem_set_parent(child, mi)) { + return FALSE; + } + if (priv->children == NULL && !dbusmenu_menuitem_property_exist(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) { dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU); } @@ -736,6 +747,10 @@ dbusmenu_menuitem_child_prepend (DbusmenuMenuitem * mi, DbusmenuMenuitem * child DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); g_return_val_if_fail(g_list_find(priv->children, child) == NULL, FALSE); + if (!dbusmenu_menuitem_set_parent(child, mi)) { + return FALSE; + } + if (priv->children == NULL && !dbusmenu_menuitem_property_exist(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) { dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU); } @@ -768,6 +783,7 @@ dbusmenu_menuitem_child_delete (DbusmenuMenuitem * mi, DbusmenuMenuitem * child) DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); priv->children = g_list_remove(priv->children, child); + dbusmenu_menuitem_unparent(child); #ifdef MASSIVEDEBUGGING g_debug("Menuitem %d (%s) signalling child removed %d (%s)", ID(mi), LABEL(mi), ID(child), LABEL(child)); #endif @@ -802,6 +818,10 @@ dbusmenu_menuitem_child_add_position (DbusmenuMenuitem * mi, DbusmenuMenuitem * DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); g_return_val_if_fail(g_list_find(priv->children, child) == NULL, FALSE); + if (!dbusmenu_menuitem_set_parent(child, mi)) { + return FALSE; + } + if (priv->children == NULL && !dbusmenu_menuitem_property_exist(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY)) { dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY, DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU); } @@ -937,6 +957,84 @@ dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, gint id) } /** + * dbusmenu_menuitem_set_parent: + * @mi: The #DbusmenuMenuitem for which to set the parent + * @parent: The new parent #DbusmenuMenuitem + * + * Sets the parent of @mi to @parent. If @mi already + * has a parent, then this call will fail. The parent will + * be set automatically when using the usual methods to add a + * child menuitem, so this function should not normally be + * called directly + * + * Return value: Whether the parent was set successfully + */ +gboolean +dbusmenu_menuitem_set_parent (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE); + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE); + + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + + if (priv->parent != NULL) { + g_warning ("Menu item already has a parent"); + return FALSE; + } + + priv->parent = parent; + g_object_add_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent); + + return TRUE; +} + +/** + * dbusmenu_menuitem_unparent: + * @mi: The #DbusmenuMenuitem to unparent + * + * Unparents the menu item @mi. If @mi doesn't have a + * parent, then this call will fail. The menuitem will + * be unparented automatically when using the usual methods + * to delete a child menuitem, so this function should not + * normally be called directly + * + * Return value: Whether the menu item was unparented successfully + */ +gboolean +dbusmenu_menuitem_unparent (DbusmenuMenuitem * mi) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE); + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + + if (priv->parent == NULL) { + g_warning("Menu item doesn't have a parent"); + return FALSE; + } + + g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent); + priv->parent = NULL; + + return TRUE; +} + +/** + * dbusmenu_menuitem_get_parent: + * @mi: The #DbusmenuMenuitem for which to inspect the parent + * + * This function looks up the parent of @mi + * + * Return value: The parent of this menu item + */ +DbusmenuMenuitem * +dbusmenu_menuitem_get_parent (DbusmenuMenuitem * mi) +{ + g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL); + DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi); + + return priv->parent; +} + +/** * dbusmenu_menuitem_property_set: * @mi: The #DbusmenuMenuitem to set the property on. * @property: Name of the property to set. diff --git a/libdbusmenu-glib/menuitem.h b/libdbusmenu-glib/menuitem.h index 4a1c156..5f56803 100644 --- a/libdbusmenu-glib/menuitem.h +++ b/libdbusmenu-glib/menuitem.h @@ -379,6 +379,10 @@ gboolean dbusmenu_menuitem_child_reorder (DbusmenuMenuitem * mi, DbusmenuMenuite DbusmenuMenuitem * dbusmenu_menuitem_child_find (DbusmenuMenuitem * mi, gint id); DbusmenuMenuitem * dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, gint id); +gboolean dbusmenu_menuitem_set_parent (DbusmenuMenuitem * mi, DbusmenuMenuitem * parent); +gboolean dbusmenu_menuitem_unparent (DbusmenuMenuitem *mi); +DbusmenuMenuitem * dbusmenu_menuitem_get_parent (DbusmenuMenuitem * mi); + gboolean dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, const gchar * value); gboolean dbusmenu_menuitem_property_set_variant (DbusmenuMenuitem * mi, const gchar * property, GVariant * value); gboolean dbusmenu_menuitem_property_set_bool (DbusmenuMenuitem * mi, const gchar * property, const gboolean value); |