/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* By Owen Taylor 98/4/4 */ /* * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include "config.h" #include "gtksocket.h" #include #include "gdk/gdkkeysyms.h" #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtksizerequest.h" #include "gtkwindowprivate.h" #include "gtkplug.h" #include "gtkprivate.h" #include "gtksocketprivate.h" #include "gtkdnd.h" #include "gtkdebug.h" #include "gtkintl.h" #include "gtkwidgetprivate.h" #ifdef GDK_WINDOWING_X11 #include "x11/gdkx.h" #endif /** * SECTION:gtksocket * @Short_description: Container for widgets from other processes * @Title: GtkSocket * @See_also: #GtkPlug, XEmbed * * Together with #GtkPlug, #GtkSocket provides the ability * to embed widgets from one process into another process * in a fashion that is transparent to the user. One * process creates a #GtkSocket widget and passes * that widget's window ID to the other process, * which then creates a #GtkPlug with that window ID. * Any widgets contained in the #GtkPlug then will appear * inside the first application's window. * * The socket's window ID is obtained by using * gtk_socket_get_id(). Before using this function, * the socket must have been realized, and for hence, * have been added to its parent. * * * Obtaining the window ID of a socket. * * GtkWidget *socket = gtk_socket_new (); * gtk_widget_show (socket); * gtk_container_add (GTK_CONTAINER (parent), socket); * * /* The following call is only necessary if one of * * the ancestors of the socket is not yet visible. * */ * gtk_widget_realize (socket); * g_print ("The ID of the sockets window is %#x\n", * gtk_socket_get_id (socket)); * * * * Note that if you pass the window ID of the socket to another * process that will create a plug in the socket, you * must make sure that the socket widget is not destroyed * until that plug is created. Violating this rule will * cause unpredictable consequences, the most likely * consequence being that the plug will appear as a * separate toplevel window. You can check if the plug * has been created by using gtk_socket_get_plug_window(). If * it returns a non-%NULL value, then the plug has been * successfully created inside of the socket. * * When GTK+ is notified that the embedded window has been * destroyed, then it will destroy the socket as well. You * should always, therefore, be prepared for your sockets * to be destroyed at any time when the main event loop * is running. To prevent this from happening, you can * connect to the #GtkSocket::plug-removed signal. * * The communication between a #GtkSocket and a #GtkPlug follows the * XEmbed * protocol. This protocol has also been implemented in other toolkits, e.g. * Qt, allowing the same level of integration * when embedding a Qt widget in GTK or vice versa. * * * The #GtkPlug and #GtkSocket widgets are currently not available * on all platforms supported by GTK+. * */ /* Forward declararations */ static void gtk_socket_finalize (GObject *object); static void gtk_socket_notify (GObject *object, GParamSpec *pspec); static void gtk_socket_realize (GtkWidget *widget); static void gtk_socket_unrealize (GtkWidget *widget); static void gtk_socket_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural); static void gtk_socket_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural); static void gtk_socket_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void gtk_socket_hierarchy_changed (GtkWidget *widget, GtkWidget *old_toplevel); static void gtk_socket_grab_notify (GtkWidget *widget, gboolean was_grabbed); static gboolean gtk_socket_key_event (GtkWidget *widget, GdkEventKey *event); static gboolean gtk_socket_focus (GtkWidget *widget, GtkDirectionType direction); static void gtk_socket_remove (GtkContainer *container, GtkWidget *widget); static void gtk_socket_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); /* Local data */ typedef struct { guint accel_key; GdkModifierType accel_mods; } GrabbedKey; enum { PLUG_ADDED, PLUG_REMOVED, LAST_SIGNAL }; static guint socket_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (GtkSocket, gtk_socket, GTK_TYPE_CONTAINER) static void gtk_socket_finalize (GObject *object) { GtkSocket *socket = GTK_SOCKET (object); GtkSocketPrivate *priv = socket->priv; g_object_unref (priv->accel_group); G_OBJECT_CLASS (gtk_socket_parent_class)->finalize (object); } static void gtk_socket_class_init (GtkSocketClass *class) { GtkWidgetClass *widget_class; GtkContainerClass *container_class; GObjectClass *gobject_class; gobject_class = (GObjectClass *) class; widget_class = (GtkWidgetClass*) class; container_class = (GtkContainerClass*) class; gobject_class->finalize = gtk_socket_finalize; gobject_class->notify = gtk_socket_notify; widget_class->realize = gtk_socket_realize; widget_class->unrealize = gtk_socket_unrealize; widget_class->get_preferred_width = gtk_socket_get_preferred_width; widget_class->get_preferred_height = gtk_socket_get_preferred_height; widget_class->size_allocate = gtk_socket_size_allocate; widget_class->hierarchy_changed = gtk_socket_hierarchy_changed; widget_class->grab_notify = gtk_socket_grab_notify; widget_class->key_press_event = gtk_socket_key_event; widget_class->key_release_event = gtk_socket_key_event; widget_class->focus = gtk_socket_focus; /* We don't want to show_all the in-process plug, if any. */ widget_class->show_all = gtk_widget_show; container_class->remove = gtk_socket_remove; container_class->forall = gtk_socket_forall; /** * GtkSocket::plug-added: * @socket_: the object which received the signal * * This signal is emitted when a client is successfully * added to the socket. */ socket_signals[PLUG_ADDED] = g_signal_new (I_("plug-added"), G_OBJECT_CLASS_TYPE (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkSocketClass, plug_added), NULL, NULL, _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0); /** * GtkSocket::plug-removed: * @socket_: the object which received the signal * * This signal is emitted when a client is removed from the socket. * The default action is to destroy the #GtkSocket widget, so if you * want to reuse it you must add a signal handler that returns %TRUE. * * Return value: %TRUE to stop other handlers from being invoked. */ socket_signals[PLUG_REMOVED] = g_signal_new (I_("plug-removed"), G_OBJECT_CLASS_TYPE (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkSocketClass, plug_removed), _gtk_boolean_handled_accumulator, NULL, _gtk_marshal_BOOLEAN__VOID, G_TYPE_BOOLEAN, 0); g_type_class_add_private (gobject_class, sizeof (GtkSocketPrivate)); } static void gtk_socket_init (GtkSocket *socket) { GtkSocketPrivate *priv; priv = G_TYPE_INSTANCE_GET_PRIVATE (socket, GTK_TYPE_SOCKET, GtkSocketPrivate); socket->priv = priv; priv->request_width = 0; priv->request_height = 0; priv->current_width = 0; priv->current_height = 0; priv->plug_window = NULL; priv->plug_widget = NULL; priv->focus_in = FALSE; priv->have_size = FALSE; priv->need_map = FALSE; priv->active = FALSE; priv->accel_group = gtk_accel_group_new (); g_object_set_data (G_OBJECT (priv->accel_group), I_("gtk-socket"), socket); } /** * gtk_socket_new: * * Create a new empty #GtkSocket. * * Return value: the new #GtkSocket. **/ GtkWidget* gtk_socket_new (void) { GtkSocket *socket; socket = g_object_new (GTK_TYPE_SOCKET, NULL); return GTK_WIDGET (socket); } /** * gtk_socket_add_id: * @socket_: a #GtkSocket * @window_id: the window ID of a client participating in the XEMBED protocol. * * Adds an XEMBED client, such as a #GtkPlug, to the #GtkSocket. The * client may be in the same process or in a different process. * * To embed a #GtkPlug in a #GtkSocket, you can either create the * #GtkPlug with gtk_plug_new (0), call * gtk_plug_get_id() to get the window ID of the plug, and then pass that to the * gtk_socket_add_id(), or you can call gtk_socket_get_id() to get the * window ID for the socket, and call gtk_plug_new() passing in that * ID. * * The #GtkSocket must have already be added into a toplevel window * before you can make this call. **/ void gtk_socket_add_id (GtkSocket *socket, GdkNativeWindow window_id) { g_return_if_fail (GTK_IS_SOCKET (socket)); g_return_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket))); if (!gtk_widget_get_realized (GTK_WIDGET (socket))) gtk_widget_realize (GTK_WIDGET (socket)); _gtk_socket_add_window (socket, window_id, TRUE); } /** * gtk_socket_get_id: * @socket_: a #GtkSocket. * * Gets the window ID of a #GtkSocket widget, which can then * be used to create a client embedded inside the socket, for * instance with gtk_plug_new(). * * The #GtkSocket must have already be added into a toplevel window * before you can make this call. * * Return value: the window ID for the socket **/ GdkNativeWindow gtk_socket_get_id (GtkSocket *socket) { g_return_val_if_fail (GTK_IS_SOCKET (socket), 0); g_return_val_if_fail (_gtk_widget_get_anchored (GTK_WIDGET (socket)), 0); if (!gtk_widget_get_realized (GTK_WIDGET (socket))) gtk_widget_realize (GTK_WIDGET (socket)); return _gtk_socket_windowing_get_id (socket); } /** * gtk_socket_get_plug_window: * @socket_: a #GtkSocket. * * Retrieves the window of the plug. Use this to check if the plug has * been created inside of the socket. * * Return value: (transfer none): the window of the plug if available, or %NULL * * Since: 2.14 **/ GdkWindow* gtk_socket_get_plug_window (GtkSocket *socket) { g_return_val_if_fail (GTK_IS_SOCKET (socket), NULL); return socket->priv->plug_window; } static void gtk_socket_realize (GtkWidget *widget) { GtkAllocation allocation; GtkSocket *socket = GTK_SOCKET (widget); GdkWindow *window; GdkWindowAttr attributes; gint attributes_mask; gtk_widget_set_realized (widget, TRUE); gtk_widget_get_allocation (widget, &allocation); attributes.window_type = GDK_WINDOW_CHILD; attributes.x = allocation.x; attributes.y = allocation.y; attributes.width = allocation.width; attributes.height = allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gtk_widget_get_visual (widget); attributes.event_mask = GDK_FOCUS_CHANGE_MASK; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window (widget, window); gdk_window_set_user_data (window, socket); gtk_style_context_set_background (gtk_widget_get_style_context (widget), window); _gtk_socket_windowing_realize_window (socket); gdk_window_add_filter (window, _gtk_socket_windowing_filter_func, widget); /* We sync here so that we make sure that if the XID for * our window is passed to another application, SubstructureRedirectMask * will be set by the time the other app creates its window. */ gdk_display_sync (gtk_widget_get_display (widget)); } /** * _gtk_socket_end_embedding: * * @socket: a #GtkSocket * * Called to end the embedding of a plug in the socket. */ void _gtk_socket_end_embedding (GtkSocket *socket) { GtkSocketPrivate *private = socket->priv; GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); if (GTK_IS_WINDOW (toplevel)) _gtk_socket_windowing_end_embedding_toplevel (socket); g_object_unref (private->plug_window); private->plug_window = NULL; private->current_width = 0; private->current_height = 0; private->resize_count = 0; gtk_accel_group_disconnect (private->accel_group, NULL); } static void gtk_socket_unrealize (GtkWidget *widget) { GtkSocket *socket = GTK_SOCKET (widget); GtkSocketPrivate *private = socket->priv; gtk_widget_set_realized (widget, FALSE); if (private->plug_widget) { _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket); } else if (private->plug_window) { _gtk_socket_end_embedding (socket); } GTK_WIDGET_CLASS (gtk_socket_parent_class)->unrealize (widget); } static void gtk_socket_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural) { GtkSocket *socket = GTK_SOCKET (widget); GtkSocketPrivate *private = socket->priv; if (private->plug_widget) { gtk_widget_get_preferred_width (private->plug_widget, minimum, natural); } else { if (private->is_mapped && !private->have_size && private->plug_window) _gtk_socket_windowing_size_request (socket); if (private->is_mapped && private->have_size) *minimum = *natural = MAX (private->request_width, 1); else *minimum = *natural = 1; } } static void gtk_socket_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural) { GtkSocket *socket = GTK_SOCKET (widget); GtkSocketPrivate *private = socket->priv; if (private->plug_widget) { gtk_widget_get_preferred_height (private->plug_widget, minimum, natural); } else { if (private->is_mapped && !private->have_size && private->plug_window) _gtk_socket_windowing_size_request (socket); if (private->is_mapped && private->have_size) *minimum = *natural = MAX (private->request_height, 1); else *minimum = *natural = 1; } } static void gtk_socket_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkSocket *socket = GTK_SOCKET (widget); GtkSocketPrivate *private = socket->priv; gtk_widget_set_allocation (widget, allocation); if (gtk_widget_get_realized (widget)) { gdk_window_move_resize (gtk_widget_get_window (widget), allocation->x, allocation->y, allocation->width, allocation->height); if (private->plug_widget) { GtkAllocation child_allocation; child_allocation.x = 0; child_allocation.y = 0; child_allocation.width = allocation->width; child_allocation.height = allocation->height; gtk_widget_size_allocate (private->plug_widget, &child_allocation); } else if (private->plug_window) { gdk_error_trap_push (); if (allocation->width != private->current_width || allocation->height != private->current_height) { gdk_window_move_resize (private->plug_window, 0, 0, allocation->width, allocation->height); if (private->resize_count) private->resize_count--; GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - allocated: %d %d", allocation->width, allocation->height)); private->current_width = allocation->width; private->current_height = allocation->height; } if (private->need_map) { gdk_window_show (private->plug_window); private->need_map = FALSE; } while (private->resize_count) { _gtk_socket_windowing_send_configure_event (socket); private->resize_count--; GTK_NOTE (PLUGSOCKET, g_message ("GtkSocket - sending synthetic configure: %d %d", allocation->width, allocation->height)); } gdk_error_trap_pop_ignored (); } } } static gboolean activate_key (GtkAccelGroup *accel_group, GObject *acceleratable, guint accel_key, GdkModifierType accel_mods, GrabbedKey *grabbed_key) { GdkEvent *gdk_event = gtk_get_current_event (); GtkSocket *socket = g_object_get_data (G_OBJECT (accel_group), "gtk-socket"); gboolean retval = FALSE; if (gdk_event && gdk_event->type == GDK_KEY_PRESS && socket->priv->plug_window) { _gtk_socket_windowing_send_key_event (socket, gdk_event, FALSE); retval = TRUE; } if (gdk_event) gdk_event_free (gdk_event); return retval; } static gboolean find_accel_key (GtkAccelKey *key, GClosure *closure, gpointer data) { GrabbedKey *grabbed_key = data; return (key->accel_key == grabbed_key->accel_key && key->accel_mods == grabbed_key->accel_mods); } /** * _gtk_socket_add_grabbed_key: * * @socket: a #GtkSocket * @keyval: a key * @modifiers: modifiers for the key * * Called from the GtkSocket platform-specific backend when the * corresponding plug has told the socket to grab a key. */ void _gtk_socket_add_grabbed_key (GtkSocket *socket, guint keyval, GdkModifierType modifiers) { GClosure *closure; GrabbedKey *grabbed_key; grabbed_key = g_new (GrabbedKey, 1); grabbed_key->accel_key = keyval; grabbed_key->accel_mods = modifiers; if (gtk_accel_group_find (socket->priv->accel_group, find_accel_key, &grabbed_key)) { g_warning ("GtkSocket: request to add already present grabbed key %u,%#x\n", keyval, modifiers); g_free (grabbed_key); return; } closure = g_cclosure_new (G_CALLBACK (activate_key), grabbed_key, (GClosureNotify)g_free); gtk_accel_group_connect (socket->priv->accel_group, keyval, modifiers, GTK_ACCEL_LOCKED, closure); } /** * _gtk_socket_remove_grabbed_key: * * @socket: a #GtkSocket * @keyval: a key * @modifiers: modifiers for the key * * Called from the GtkSocket backend when the corresponding plug has * told the socket to remove a key grab. */ void _gtk_socket_remove_grabbed_key (GtkSocket *socket, guint keyval, GdkModifierType modifiers) { if (!gtk_accel_group_disconnect_key (socket->priv->accel_group, keyval, modifiers)) g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n", keyval, modifiers); } static void socket_update_focus_in (GtkSocket *socket) { GtkSocketPrivate *private = socket->priv; gboolean focus_in = FALSE; if (private->plug_window) { GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); if (gtk_widget_is_toplevel (toplevel) && gtk_window_has_toplevel_focus (GTK_WINDOW (toplevel)) && gtk_widget_is_focus (GTK_WIDGET (socket))) focus_in = TRUE; } if (focus_in != private->focus_in) { private->focus_in = focus_in; _gtk_socket_windowing_focus_change (socket, focus_in); } } static void socket_update_active (GtkSocket *socket) { GtkSocketPrivate *private = socket->priv; gboolean active = FALSE; if (private->plug_window) { GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); if (gtk_widget_is_toplevel (toplevel) && gtk_window_is_active (GTK_WINDOW (toplevel))) active = TRUE; } if (active != private->active) { private->active = active; _gtk_socket_windowing_update_active (socket, active); } } static void gtk_socket_hierarchy_changed (GtkWidget *widget, GtkWidget *old_toplevel) { GtkSocket *socket = GTK_SOCKET (widget); GtkSocketPrivate *private = socket->priv; GtkWidget *toplevel = gtk_widget_get_toplevel (widget); if (toplevel && !GTK_IS_WINDOW (toplevel)) toplevel = NULL; if (toplevel != private->toplevel) { if (private->toplevel) { gtk_window_remove_accel_group (GTK_WINDOW (private->toplevel), private->accel_group); g_signal_handlers_disconnect_by_func (private->toplevel, socket_update_focus_in, socket); g_signal_handlers_disconnect_by_func (private->toplevel, socket_update_active, socket); } private->toplevel = toplevel; if (toplevel) { gtk_window_add_accel_group (GTK_WINDOW (private->toplevel), private->accel_group); g_signal_connect_swapped (private->toplevel, "notify::has-toplevel-focus", G_CALLBACK (socket_update_focus_in), socket); g_signal_connect_swapped (private->toplevel, "notify::is-active", G_CALLBACK (socket_update_active), socket); } socket_update_focus_in (socket); socket_update_active (socket); } } static void gtk_socket_grab_notify (GtkWidget *widget, gboolean was_grabbed) { GtkSocket *socket = GTK_SOCKET (widget); if (!socket->priv->same_app) _gtk_socket_windowing_update_modality (socket, !was_grabbed); } static gboolean gtk_socket_key_event (GtkWidget *widget, GdkEventKey *event) { GtkSocket *socket = GTK_SOCKET (widget); GtkSocketPrivate *private = socket->priv; if (gtk_widget_has_focus (widget) && private->plug_window && !private->plug_widget) { _gtk_socket_windowing_send_key_event (socket, (GdkEvent *) event, FALSE); return TRUE; } else return FALSE; } static void gtk_socket_notify (GObject *object, GParamSpec *pspec) { if (!strcmp (pspec->name, "is-focus")) return; socket_update_focus_in (GTK_SOCKET (object)); } /** * _gtk_socket_claim_focus: * * @socket: a #GtkSocket * @send_event: huh? * * Claims focus for the socket. XXX send_event? */ void _gtk_socket_claim_focus (GtkSocket *socket, gboolean send_event) { GtkWidget *widget = GTK_WIDGET (socket); GtkSocketPrivate *private = socket->priv; if (!send_event) private->focus_in = TRUE; /* Otherwise, our notify handler will send FOCUS_IN */ /* Oh, the trickery... */ gtk_widget_set_can_focus (widget, TRUE); gtk_widget_grab_focus (widget); gtk_widget_set_can_focus (widget, FALSE); } static gboolean gtk_socket_focus (GtkWidget *widget, GtkDirectionType direction) { GtkSocket *socket = GTK_SOCKET (widget); GtkSocketPrivate *private = socket->priv; if (private->plug_widget) return gtk_widget_child_focus (private->plug_widget, direction); if (!gtk_widget_is_focus (widget)) { _gtk_socket_windowing_focus (socket, direction); _gtk_socket_claim_focus (socket, FALSE); return TRUE; } else return FALSE; } static void gtk_socket_remove (GtkContainer *container, GtkWidget *child) { GtkSocket *socket = GTK_SOCKET (container); GtkSocketPrivate *private = socket->priv; g_return_if_fail (child == private->plug_widget); _gtk_plug_remove_from_socket (GTK_PLUG (private->plug_widget), socket); } static void gtk_socket_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { GtkSocket *socket = GTK_SOCKET (container); GtkSocketPrivate *private = socket->priv; if (private->plug_widget) (* callback) (private->plug_widget, callback_data); } /** * _gtk_socket_add_window: * * @socket: a #GtkSocket * @xid: the native identifier for a window * @need_reparent: whether the socket's plug's window needs to be * reparented to the socket * * Adds a window to a GtkSocket. */ void _gtk_socket_add_window (GtkSocket *socket, GdkNativeWindow xid, gboolean need_reparent) { GtkWidget *widget = GTK_WIDGET (socket); GdkDisplay *display = gtk_widget_get_display (widget); gpointer user_data = NULL; GtkSocketPrivate *private = socket->priv; #ifdef GDK_WINDOWING_X11 if (GDK_IS_X11_DISPLAY (display)) private->plug_window = gdk_x11_window_lookup_for_display (display, xid); else #endif private->plug_window = NULL; if (private->plug_window) { g_object_ref (private->plug_window); gdk_window_get_user_data (private->plug_window, &user_data); } if (user_data) /* A widget's window in this process */ { GtkWidget *child_widget = user_data; if (!GTK_IS_PLUG (child_widget)) { g_warning (G_STRLOC ": Can't add non-GtkPlug to GtkSocket"); private->plug_window = NULL; gdk_error_trap_pop_ignored (); return; } _gtk_plug_add_to_socket (GTK_PLUG (child_widget), socket); } else /* A foreign window */ { GtkWidget *toplevel; GdkDragProtocol protocol; gdk_error_trap_push (); if (!private->plug_window) { #ifdef GDK_WINDOWING_X11 if (GDK_IS_X11_DISPLAY (display)) private->plug_window = gdk_x11_window_foreign_new_for_display (display, xid); #endif if (!private->plug_window) /* was deleted before we could get it */ { gdk_error_trap_pop_ignored (); return; } } _gtk_socket_windowing_select_plug_window_input (socket); if (gdk_error_trap_pop ()) { g_object_unref (private->plug_window); private->plug_window = NULL; return; } /* OK, we now will reliably get destroy notification on socket->plug_window */ gdk_error_trap_push (); if (need_reparent) { gdk_window_hide (private->plug_window); /* Shouldn't actually be necessary for XEMBED, but just in case */ gdk_window_reparent (private->plug_window, gtk_widget_get_window (widget), 0, 0); } private->have_size = FALSE; _gtk_socket_windowing_embed_get_info (socket); private->need_map = private->is_mapped; if (gdk_drag_get_protocol_for_display (display, xid, &protocol)) gtk_drag_dest_set_proxy (GTK_WIDGET (socket), private->plug_window, protocol, TRUE); gdk_error_trap_pop_ignored (); gdk_window_add_filter (private->plug_window, _gtk_socket_windowing_filter_func, socket); /* Add a pointer to the socket on our toplevel window */ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); if (GTK_IS_WINDOW (toplevel)) gtk_window_add_embedded_xid (GTK_WINDOW (toplevel), xid); _gtk_socket_windowing_embed_notify (socket); socket_update_active (socket); socket_update_focus_in (socket); gtk_widget_queue_resize (GTK_WIDGET (socket)); } if (private->plug_window) g_signal_emit (socket, socket_signals[PLUG_ADDED], 0); } /** * _gtk_socket_handle_map_request: * * @socket: a #GtkSocket * * Called from the GtkSocket backend when the plug has been mapped. */ void _gtk_socket_handle_map_request (GtkSocket *socket) { GtkSocketPrivate *private = socket->priv; if (!private->is_mapped) { private->is_mapped = TRUE; private->need_map = TRUE; gtk_widget_queue_resize (GTK_WIDGET (socket)); } } /** * _gtk_socket_unmap_notify: * * @socket: a #GtkSocket * * Called from the GtkSocket backend when the plug has been unmapped ??? */ void _gtk_socket_unmap_notify (GtkSocket *socket) { GtkSocketPrivate *private = socket->priv; if (private->is_mapped) { private->is_mapped = FALSE; gtk_widget_queue_resize (GTK_WIDGET (socket)); } } /** * _gtk_socket_advance_toplevel_focus: * * @socket: a #GtkSocket * @direction: a direction * * Called from the GtkSocket backend when the corresponding plug * has told the socket to move the focus. */ void _gtk_socket_advance_toplevel_focus (GtkSocket *socket, GtkDirectionType direction) { GtkBin *bin; GtkWindow *window; GtkContainer *container; GtkWidget *child; GtkWidget *focus_widget; GtkWidget *toplevel; GtkWidget *old_focus_child; GtkWidget *parent; toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); if (!toplevel) return; if (!gtk_widget_is_toplevel (toplevel) || GTK_IS_PLUG (toplevel)) { gtk_widget_child_focus (toplevel,direction); return; } container = GTK_CONTAINER (toplevel); window = GTK_WINDOW (toplevel); bin = GTK_BIN (toplevel); /* This is a copy of gtk_window_focus(), modified so that we * can detect wrap-around. */ old_focus_child = gtk_container_get_focus_child (container); if (old_focus_child) { if (gtk_widget_child_focus (old_focus_child, direction)) return; /* We are allowed exactly one wrap-around per sequence of focus * events */ if (_gtk_socket_windowing_embed_get_focus_wrapped ()) return; else _gtk_socket_windowing_embed_set_focus_wrapped (); } focus_widget = gtk_window_get_focus (window); if (window) { /* Wrapped off the end, clear the focus setting for the toplevel */ parent = gtk_widget_get_parent (focus_widget); while (parent) { gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL); parent = gtk_widget_get_parent (parent); } gtk_window_set_focus (GTK_WINDOW (container), NULL); } /* Now try to focus the first widget in the window */ child = gtk_bin_get_child (bin); if (child) { if (gtk_widget_child_focus (child, direction)) return; } }