diff options
Diffstat (limited to 'gtk/gtkplug.c')
-rw-r--r-- | gtk/gtkplug.c | 237 |
1 files changed, 233 insertions, 4 deletions
diff --git a/gtk/gtkplug.c b/gtk/gtkplug.c index 03d7e22f06..743a74090d 100644 --- a/gtk/gtkplug.c +++ b/gtk/gtkplug.c @@ -26,6 +26,7 @@ */ #include "gtkmain.h" +#include "gtkmarshal.h" #include "gtkplug.h" #include "gtkprivate.h" @@ -75,6 +76,13 @@ static void xembed_set_info (GdkWindow *window, static GtkWindowClass *parent_class = NULL; static GtkBinClass *bin_class = NULL; +enum { + EMBEDDED, + LAST_SIGNAL +}; + +static guint plug_signals[LAST_SIGNAL] = { 0 }; + GtkType gtk_plug_get_type () { @@ -129,6 +137,15 @@ gtk_plug_class_init (GtkPlugClass *class) #if 0 window_class->accel_entries_changed = gtk_plug_accel_entries_changed; #endif + + plug_signals[EMBEDDED] = + g_signal_new ("embedded", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkPlugClass, embedded), + NULL, NULL, + gtk_marshal_VOID__VOID, + GTK_TYPE_NONE, 0); } static void @@ -146,11 +163,34 @@ static void gtk_plug_set_is_child (GtkPlug *plug, gboolean is_child) { + g_assert (!GTK_WIDGET (plug)->parent); + if (is_child) { + if (plug->modality_window) + handle_modality_off (plug); + + if (plug->modality_group) + { + gtk_window_group_remove_window (plug->modality_group, GTK_WINDOW (plug)); + g_object_unref (plug->modality_group); + plug->modality_group = NULL; + } + GTK_WIDGET_UNSET_FLAGS (plug, GTK_TOPLEVEL); - GTK_PRIVATE_UNSET_FLAG (plug, GTK_ANCHORED); gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_PARENT); + + _gtk_widget_propagate_hierarchy_changed (GTK_WIDGET (plug), GTK_WIDGET (plug)); + } + else + { + plug->modality_group = gtk_window_group_new (); + gtk_window_group_add_window (plug->modality_group, GTK_WINDOW (plug)); + + GTK_WIDGET_SET_FLAGS (plug, GTK_TOPLEVEL); + gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_QUEUE); + + _gtk_widget_propagate_hierarchy_changed (GTK_WIDGET (plug), NULL); } } @@ -165,12 +205,14 @@ void _gtk_plug_add_to_socket (GtkPlug *plug, GtkSocket *socket) { - GtkWidget *widget = GTK_WIDGET (plug); + GtkWidget *widget; g_return_if_fail (GTK_IS_PLUG (plug)); g_return_if_fail (GTK_IS_SOCKET (socket)); g_return_if_fail (GTK_WIDGET_REALIZED (socket)); + widget = GTK_WIDGET (plug); + gtk_plug_set_is_child (plug, TRUE); plug->same_app = TRUE; socket->plug_widget = widget; @@ -189,6 +231,71 @@ _gtk_plug_add_to_socket (GtkPlug *plug, gtk_widget_queue_resize (widget); } + + g_signal_emit_by_name (G_OBJECT (socket), "plug_added", 0); +} + +/** + * _gtk_plug_add_to_socket: + * @plug: a #GtkPlug + * @socket: a #GtkSocket + * + * Remove a plug from a socket within the same application. + **/ +void +_gtk_plug_remove_from_socket (GtkPlug *plug, + GtkSocket *socket) +{ + GtkWidget *widget; + GdkEvent event; + gboolean result; + gboolean widget_was_visible; + + g_return_if_fail (GTK_IS_PLUG (plug)); + g_return_if_fail (GTK_IS_SOCKET (socket)); + g_return_if_fail (GTK_WIDGET_REALIZED (plug)); + + widget = GTK_WIDGET (plug); + + g_object_ref (plug); + g_object_ref (socket); + + widget_was_visible = GTK_WIDGET_VISIBLE (plug); + + gdk_window_hide (widget->window); + gdk_window_reparent (widget->window, GDK_ROOT_PARENT (), 0, 0); + + GTK_PRIVATE_SET_FLAG (plug, GTK_IN_REPARENT); + gtk_widget_unparent (GTK_WIDGET (plug)); + GTK_PRIVATE_UNSET_FLAG (plug, GTK_IN_REPARENT); + + socket->plug_widget = NULL; + socket->plug_window = NULL; + socket->same_app = FALSE; + + plug->same_app = FALSE; + plug->socket_window = FALSE; + + gtk_plug_set_is_child (plug, FALSE); + + g_signal_emit_by_name (G_OBJECT (socket), "plug_removed", &result); + if (!result) + gtk_widget_destroy (GTK_WIDGET (socket)); + + event.any.type = GDK_DELETE; + event.any.window = g_object_ref (widget->window); + event.any.send_event = FALSE; + + if (!gtk_widget_event (widget, &event)) + gtk_widget_destroy (widget); + + g_object_unref (event.any.window); + g_object_unref (plug); + + if (widget_was_visible && GTK_WIDGET_VISIBLE (socket)) + gtk_widget_queue_resize (GTK_WIDGET (socket)); + + g_object_unref (socket); } void @@ -216,6 +323,9 @@ gtk_plug_construct (GtkPlug *plug, plug->socket_window = NULL; } } + + if (plug->socket_window) + g_signal_emit (G_OBJECT (plug), plug_signals[EMBEDDED], 0); } } @@ -229,6 +339,27 @@ gtk_plug_new (GdkNativeWindow socket_id) return GTK_WIDGET (plug); } +/** + * gtk_plug_get_id: + * @plug: a #GtkPlug. + * + * Gets the window ID of a #GtkPlug widget, which can then + * be used to embed this window inside another window, for + * instance with gtk_sock_add_id (id). + * + * Return value: the window ID for the plug + **/ +GdkNativeWindow +gtk_plug_get_id (GtkPlug *plug) +{ + g_return_val_if_fail (GTK_IS_PLUG (plug), 0); + + if (!GTK_WIDGET_REALIZED (plug)) + gtk_widget_realize (GTK_WIDGET (plug)); + + return GDK_WINDOW_XWINDOW (GTK_WIDGET (plug)->window); +} + static void gtk_plug_unrealize (GtkWidget *widget) { @@ -300,6 +431,8 @@ gtk_plug_realize (GtkWidget *widget) if (GTK_WIDGET_TOPLEVEL (widget)) { + attributes.window_type = GDK_WINDOW_TOPLEVEL; + gdk_error_trap_push (); widget->window = gdk_window_new (plug->socket_window, &attributes, attributes_mask); @@ -312,8 +445,7 @@ gtk_plug_realize (GtkWidget *widget) gdk_error_trap_pop (); widget->window = gdk_window_new (NULL, &attributes, attributes_mask); } - - GDK_WINDOW_TYPE (widget->window) = GDK_WINDOW_TOPLEVEL; + gdk_window_add_filter (widget->window, gtk_plug_filter_func, widget); plug->modality_group = gtk_window_group_new (); @@ -906,7 +1038,104 @@ gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) return GDK_FILTER_REMOVE; } + else if (xevent->xclient.message_type == gdk_atom_intern ("WM_DELETE_WINDOW", FALSE)) + { + /* We filter these out because we take being reparented back to the + * root window as the reliable end of the embedding protocol + */ + + return GDK_FILTER_REMOVE; + } break; + case ReparentNotify: + { + XReparentEvent *xre = &xevent->xreparent; + gboolean was_embedded = plug->socket_window != NULL; + + return_val = GDK_FILTER_REMOVE; + + g_object_ref (plug); + + if (was_embedded) + { + /* End of embedding protocol for previous socket */ + + /* FIXME: race if we remove from another socket and + * then add to a local window before we get notification + * Probably need check in _gtk_plug_add_to_socket + */ + + if (xre->parent != GDK_WINDOW_XWINDOW (plug->socket_window)) + { + GtkWidget *widget = GTK_WIDGET (plug); + + gdk_window_set_user_data (plug->socket_window, NULL); + gdk_window_unref (plug->socket_window); + plug->socket_window = NULL; + + /* Emit a delete window, as if the user attempted + * to close the toplevel. Simple as to how we + * handle WM_DELETE_WINDOW, if it isn't handled + * we destroy the widget. BUt only do this if + * we are being reparented to the root window. + * Moving from one embedder to another should + * be invisible to the app. + */ + + if (xre->parent == GDK_ROOT_WINDOW()) + { + GdkEvent event; + + event.any.type = GDK_DELETE; + event.any.window = g_object_ref (widget->window); + event.any.send_event = FALSE; + + if (!gtk_widget_event (widget, &event)) + gtk_widget_destroy (widget); + + g_object_unref (event.any.window); + } + } + else + break; + } + + if (xre->parent != GDK_ROOT_WINDOW ()) + { + /* Start of embedding protocol */ + + plug->socket_window = gdk_window_lookup (xre->parent); + if (plug->socket_window) + { + gpointer user_data = NULL; + gdk_window_get_user_data (plug->socket_window, &user_data); + + if (user_data) + { + g_warning (G_STRLOC "Plug reparented unexpectedly into window in the same process"); + plug->socket_window = NULL; + break; + } + + g_object_ref (plug->socket_window); + } + else + { + plug->socket_window = gdk_window_foreign_new (xre->parent); + if (!plug->socket_window) /* Already gone */ + break; + } + + /* FIXME: Add grabbed keys here */ + + if (!was_embedded) + g_signal_emit (G_OBJECT (plug), plug_signals[EMBEDDED], 0); + } + + g_object_unref (plug); + + break; + } } return GDK_FILTER_CONTINUE; |