BonoboSocket fixes in CVS now



Hi,

I just committed the attached patch to CVS, with Michael's a-priori
approval.  It fixes the situation where in-process controls were not
being activated/deactivated, as a socket with an in-proc plug will not
get a focus_in_event; prior to the patch, BonoboSocket would only do
activation in the focus event handler.

Now, BonoboSocket keeps track of the focused widget within its toplevel
and activates or deactivates its child control as appropriate.

I am about to send a patch for the Evolution composer and GtkHTML's
editor control to make them work properly with this.  Code that properly
merges and de-merges its menu/toolbar items in its controls' activate
callbacks will need no changes; Evo and GtkHTML do need changes because
they didn't do this.

This is the ChangeLog for the patch:

2003-07-23  Federico Mena Quintero  <federico ximian com>

	* bonobo/bonobo-socket.c (bonobo_socket_instance_init): Create a
	private structure for the socket.
	(bonobo_socket_finalize): Free the private structure.
	(toplevel_set_focus_cb): New callback for GtkWindow::set_focus().
	We use this to keep track of the focused widget and
	activate/deactivate the control frame as appropriate --- our
	focus_in/out handlers will not be called if the embedded plug is
	in-process.
	(bonobo_socket_hierarchy_changed): New handler for
	GtkWidget::hierarchy_changed().  We must monitor the focused
	widget on our toplevel, so we pick up toplevel changes here.

  Federico
Index: bonobo/bonobo-socket.c
===================================================================
RCS file: /cvs/gnome/libbonoboui/bonobo/bonobo-socket.c,v
retrieving revision 1.45
diff -u -r1.45 bonobo-socket.c
--- bonobo/bonobo-socket.c	21 Aug 2002 10:46:32 -0000	1.45
+++ bonobo/bonobo-socket.c	24 Jul 2003 02:57:16 -0000
@@ -25,14 +25,40 @@
  * bits layered over gtk we have */
 #undef DEBUG_RAW_GTK
 
+/* Private part of the BonoboSocket structure */
+typedef struct {
+	/* Our toplevel, used to track the currently-focused widget */
+	GtkWidget *toplevel;
+
+	/* Signal handler ID for the toplevel's GtkWindow::set_focus() */
+	gulong set_focus_id;
+
+	/* Whether a descendant of us has the focus.  If this is the case, it
+	 * means that we are out-of-process.
+	 */
+	gboolean descendant_has_focus : 1;
+} BonoboSocketPrivate;
+
 GNOME_CLASS_BOILERPLATE (BonoboSocket, bonobo_socket,
 			 GObject, GTK_TYPE_SOCKET);
 
 static void
 bonobo_socket_finalize (GObject *object)
 {
+	BonoboSocket *socket;
+	BonoboSocketPrivate *priv;
+
 	dprintf ("bonobo_socket_finalize %p\n", object);
 
+	socket = BONOBO_SOCKET (object);
+	priv = socket->priv;
+
+	priv->toplevel = NULL;
+	priv->descendant_has_focus = FALSE;
+
+	g_free (priv);
+	socket->priv = NULL;
+
 	GNOME_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
 }
 
@@ -46,14 +72,23 @@
 bonobo_socket_dispose (GObject *object)
 {
 	BonoboSocket *socket = (BonoboSocket *) object;
+	BonoboSocketPrivate *priv;
 
 	dprintf ("bonobo_socket_dispose %p\n", object);
 
+	priv= socket->priv;
+
 	if (socket->frame) {
 		bonobo_socket_set_control_frame (socket, NULL);
 		g_assert (socket->frame == NULL);
 	}
 
+	if (priv->set_focus_id) {
+		g_assert (priv->toplevel != NULL);
+		g_signal_handler_disconnect (priv->toplevel, priv->set_focus_id);
+		priv->set_focus_id = 0;
+	}
+
 	GNOME_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
 }
 
@@ -134,6 +169,80 @@
 		socket->frame, GTK_WIDGET_STATE (widget));
 }
 
+/* Callback for GtkWindow::set_focus().  We watch the focused widget in this way. */
+static void
+toplevel_set_focus_cb (GtkWindow *window, GtkWidget *focus, gpointer data)
+{
+	BonoboSocket *socket;
+	BonoboSocketPrivate *priv;
+	GtkWidget *socket_widget;
+	gboolean descendant_had_focus;
+	gboolean should_autoactivate;
+
+	socket = BONOBO_SOCKET (data);
+	priv = socket->priv;
+
+	g_assert (priv->toplevel == GTK_WIDGET (window));
+
+	socket_widget = GTK_WIDGET (socket);
+
+	descendant_had_focus = priv->descendant_has_focus;
+
+	should_autoactivate = (socket->socket.plug_widget	/* Only in the in-process case */
+			       && socket->frame			/* We need an auto-activatable frame */
+			       && bonobo_control_frame_get_autoactivate (socket->frame));
+
+	/* If a descendant of ours is focused then possibly activate its
+	 * control, unless there are intermediate sockets between us --- they
+	 * should take care of that themselves.
+	 */
+
+	if (focus && gtk_widget_get_ancestor (focus, GTK_TYPE_SOCKET) == socket_widget) {
+		priv->descendant_has_focus = TRUE;
+
+		if (!descendant_had_focus && should_autoactivate)
+			bonobo_control_frame_control_activate (socket->frame);
+	} else {
+		priv->descendant_has_focus = FALSE;
+
+		if (descendant_had_focus && should_autoactivate)
+			bonobo_control_frame_control_deactivate (socket->frame);
+	}
+}
+
+/* GtkWidget::hierarchy_changed() handler.  We have to monitor our toplevel so
+ * that we can connect to its GtkWindow::set_focus() signal, so that we can keep
+ * track of the currently focused widget.
+ */
+static void
+bonobo_socket_hierarchy_changed (GtkWidget *widget, GtkWidget *previous_toplevel)
+{
+	BonoboSocket *socket;
+	BonoboSocketPrivate *priv;
+	GtkWidget *new_toplevel;
+
+	socket = BONOBO_SOCKET (widget);
+	priv = socket->priv;
+
+	g_assert (priv->toplevel == previous_toplevel);
+
+	if (priv->set_focus_id) {
+		g_assert (priv->toplevel != NULL);
+		g_signal_handler_disconnect (priv->toplevel, priv->set_focus_id);
+		priv->set_focus_id = 0;
+	}
+
+	new_toplevel = gtk_widget_get_toplevel (widget);
+	if (new_toplevel && GTK_IS_WINDOW (new_toplevel)) {
+		priv->toplevel = new_toplevel;
+		priv->set_focus_id = g_signal_connect_after (priv->toplevel, "set_focus",
+							     G_CALLBACK (toplevel_set_focus_cb), socket);
+	}
+}
+
+/* NOTE: This will only get called in the out-of-process case.  GTK+ only sends
+ * focus-in/out events to leaf widgets, not their ancestors.
+ */
 static gint
 bonobo_socket_focus_in (GtkWidget     *widget,
 			GdkEventFocus *focus)
@@ -149,6 +258,9 @@
 	return GTK_WIDGET_CLASS (parent_class)->focus_in_event (widget, focus);
 }
 
+/* NOTE: This will only get called in the out-of-process case.  GTK+ only sends
+ * focus-in/out events to leaf widgets, not their ancestors.
+ */
 static gint
 bonobo_socket_focus_out (GtkWidget     *widget,
 			 GdkEventFocus *focus)
@@ -271,23 +383,30 @@
 	gobject_class->finalize = bonobo_socket_finalize;
 	gobject_class->dispose  = bonobo_socket_dispose;
 
-	widget_class->realize         = bonobo_socket_realize;
-	widget_class->unrealize       = bonobo_socket_unrealize;
-	widget_class->state_changed   = bonobo_socket_state_changed;
-	widget_class->focus_in_event  = bonobo_socket_focus_in;
-	widget_class->focus_out_event = bonobo_socket_focus_out;
-	widget_class->size_request    = bonobo_socket_size_request;
-	widget_class->size_allocate   = bonobo_socket_size_allocate;
-	widget_class->expose_event    = bonobo_socket_expose_event;
-	widget_class->show            = bonobo_socket_show;
-	widget_class->show_all        = bonobo_socket_show_all;
+	widget_class->realize           = bonobo_socket_realize;
+	widget_class->unrealize         = bonobo_socket_unrealize;
+	widget_class->state_changed     = bonobo_socket_state_changed;
+	widget_class->hierarchy_changed = bonobo_socket_hierarchy_changed;
+	widget_class->focus_in_event    = bonobo_socket_focus_in;
+	widget_class->focus_out_event   = bonobo_socket_focus_out;
+	widget_class->size_request    	= bonobo_socket_size_request;
+	widget_class->size_allocate   	= bonobo_socket_size_allocate;
+	widget_class->expose_event    	= bonobo_socket_expose_event;
+	widget_class->show            	= bonobo_socket_show;
+	widget_class->show_all        	= bonobo_socket_show_all;
 
-	socket_class->plug_removed    = bonobo_socket_plug_removed;
+	socket_class->plug_removed = bonobo_socket_plug_removed;
 }
 
 static void
 bonobo_socket_instance_init (BonoboSocket *socket)
 {
+	BonoboSocketPrivate *priv;
+
+	priv = g_new0 (BonoboSocketPrivate, 1);
+	socket->priv = priv;
+
+	priv->toplevel = NULL;
 }
 
 /**


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]