- From: Nat Friedman <nat helixcode com>
- To: Ettore Perazzoli <ettore helixcode com>
- Cc: federico helixcode com, Owen Taylor <otaylor redhat com>
- Subject: Reparenting problem in the GnomeDock.
- Date: Tue, 15 Feb 2000 18:59:44 -0500 (EST)
Hi Ettore, As you know, I've been working on the reparenting problems in Bonobo controls lately. Federico helped me for a few hours today, and we managed to track down the wacky issues we were seeing last night. The situation was this; on the component side, I was creating a GtkLabel, wrapping it in a BonoboControl, and then passing that up to the container. The container was creating a new GnomeDockItem and stuffing the Control into the DockItem. When the dock gets dragged out of the application, all of its windows need to get reparented into the new floating window. This causes the container-side GtkSocket which is embedding the BonoboControl to get re-realized. When the GtkSocket is re-realized, Bonobo transmits the new socket XID to the control via CORBA[1]. The BonoboControl then creates a new plug using the new XID and reparents the Control's top-level widget (in this case, the GtkLabel) into the new plug. The old plug is discarded. This is how things are supposed to work. For some reason, though, the plug was getting an X Delete event before the new XID arrived. This caused the old plug to be destroyed, and made it impossible to reparent the GtkLabel[2]. So Federico and I tracked down why the plug was getting the delete event. It turns out that when you reparent a GnomeDockItem, you do this (gnome-dock.c, line 910 or so, in drag_floating): gtk_container_remove (GTK_CONTAINER (item_widget->parent), item_widget); gtk_widget_set_parent (item_widget, dock_widget); The gtk_container_remove maps to gtk_bin_remove (since GnomeDockItem subclasses GtkBin), which invokes gtk_window_unparent. Which brings us to this little piece of code (gtkwidget.c, line 1363 or so): if (GTK_WIDGET_REALIZED (widget) && !GTK_WIDGET_IN_REPARENT (widget)) gtk_widget_unrealize (widget); So my plug was getting unrealized, which explains the X Delete event the control was getting. When I set the GTK_IN_REPARENT private widget flag in gnome-dock.c, it worked perfectly, since gtk_widget_unparent knew that we were in the process of reparenting a window, and that it would therefore be a bad idea to unrealize the window. So GnomeDock needs to be fixed. I don't know what the correct solution is, but here's my patch (the one that sets the private flag). Nat [1] One problem with the Bonobo code was that we were sending along the new XID without XSync'ing the server; this was unrelated to the reparenting problems, but is a pretty important lesson. Apparently XIDs can get allocated on the client side without any communication with the server, and so the XID which gets transmitted to the control may be completely unknown to the X server. [2] I don't know why it was impossible. I tried doing stuff like: gtk_object_ref (GTK_OBJECT (control->priv->widget)); gtk_widget_unparent (control->priv->widget); gtk_object_unref (GTK_OBJECT (control->priv->widget)); gtk_container_add (GTK_CONTAINER (control->priv->plug), control->priv->widget); where control->priv->widget is the GtkLabel, but this failed pretty horribly, and I never found out why. diff -u -r1.28 gnome-dock.c --- gnome-dock.c 1999/10/03 10:39:33 1.28 +++ gnome-dock.c 2000/02/16 03:08:48 @@ -21,6 +21,7 @@ */ #include <gtk/gtk.h> +#include <gtk/gtkprivate.h> #include "gnome-dock.h" #include "gnome-dock-band.h" @@ -904,10 +905,15 @@ { gtk_widget_ref (item_widget); + GTK_PRIVATE_SET_FLAG (item_widget, GTK_IN_REPARENT); + gtk_container_remove (GTK_CONTAINER (item_widget->parent), item_widget); gtk_widget_set_parent (item_widget, dock_widget); + GTK_PRIVATE_UNSET_FLAG (item_widget, GTK_IN_REPARENT); + + dock->floating_children = g_list_prepend (dock->floating_children, item);