X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkplug.c;h=01fc9799e5225122b89be82f71bcc7932ae11edb;hb=dc331ccb171151d737112d8dc55b25709271d2c7;hp=45f06e0337e0c241d2cda66d63b8ef6432db4c02;hpb=cca8dd63475ab582d8592039f1f69d7f22a3b928;p=~andy%2Fgtk diff --git a/gtk/gtkplug.c b/gtk/gtkplug.c index 45f06e033..01fc9799e 100644 --- a/gtk/gtkplug.c +++ b/gtk/gtkplug.c @@ -12,8 +12,7 @@ * 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. + * License along with this library. If not, see .Free */ /* By Owen Taylor 98/4/4 */ @@ -25,20 +24,68 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#include "config.h" + +#include "gtkdebug.h" #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtkplug.h" +#include "gtkintl.h" #include "gtkprivate.h" +#include "gtksocketprivate.h" +#include "gtkwidgetprivate.h" +#include "gtkwindowprivate.h" +#include "gtkxembed.h" -#include "gdk/gdkkeysyms.h" -#include "x11/gdkx.h" +#include -#include "gtkxembed.h" +/** + * SECTION:gtkplug + * @Short_description: Toplevel for embedding into other processes + * @Title: GtkPlug + * @include: gtk/gtkx.h + * @See_also: #GtkSocket + * + * Together with #GtkSocket, #GtkPlug 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 the ID of that widget's window 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 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 only available when GTK+ + * is compiled for the X11 platform and %GDK_WINDOWING_X11 is defined. + * They can only be used on a #GdkX11Display. To use #GtkPlug and + * #GtkSocket, you need to include the gtk/gtkx.h + * header. + * + */ -#include "gtkalias.h" +struct _GtkPlugPrivate +{ + GtkWidget *modality_window; + GtkWindowGroup *modality_group; + + GdkWindow *socket_window; + + GHashTable *grabbed_keys; + + guint same_app : 1; +}; -static void gtk_plug_class_init (GtkPlugClass *klass); -static void gtk_plug_init (GtkPlug *plug); +static void gtk_plug_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); static void gtk_plug_finalize (GObject *object); static void gtk_plug_realize (GtkWidget *widget); static void gtk_plug_unrealize (GtkWidget *widget); @@ -58,20 +105,24 @@ static gboolean gtk_plug_focus (GtkWidget *widget, GtkDirectionType direction); static void gtk_plug_check_resize (GtkContainer *container); static void gtk_plug_keys_changed (GtkWindow *window); -static GdkFilterReturn gtk_plug_filter_func (GdkXEvent *gdk_xevent, - GdkEvent *event, - gpointer data); -static void handle_modality_off (GtkPlug *plug); -static void xembed_set_info (GdkWindow *window, - unsigned long flags); +static void xembed_set_info (GdkWindow *window, + unsigned long flags); -/* From Tk */ -#define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20 - -static GtkWindowClass *parent_class = NULL; static GtkBinClass *bin_class = NULL; +typedef struct +{ + guint accelerator_key; + GdkModifierType accelerator_mods; +} GrabbedKey; + +enum { + PROP_0, + PROP_EMBEDDED, + PROP_SOCKET_WINDOW +}; + enum { EMBEDDED, LAST_SIGNAL @@ -79,31 +130,29 @@ enum { static guint plug_signals[LAST_SIGNAL] = { 0 }; -GType -gtk_plug_get_type (void) +G_DEFINE_TYPE (GtkPlug, gtk_plug, GTK_TYPE_WINDOW) + +static void +gtk_plug_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - static GType plug_type = 0; + GtkPlug *plug = GTK_PLUG (object); + GtkPlugPrivate *priv = plug->priv; - if (!plug_type) + switch (prop_id) { - static const GTypeInfo plug_info = - { - sizeof (GtkPlugClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_plug_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkPlug), - 16, /* n_preallocs */ - (GInstanceInitFunc) gtk_plug_init, - }; - - plug_type = g_type_register_static (GTK_TYPE_WINDOW, "GtkPlug", - &plug_info, 0); + case PROP_EMBEDDED: + g_value_set_boolean (value, priv->socket_window != NULL); + break; + case PROP_SOCKET_WINDOW: + g_value_set_object (value, priv->socket_window); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } - - return plug_type; } static void @@ -114,9 +163,9 @@ gtk_plug_class_init (GtkPlugClass *class) GtkWindowClass *window_class = (GtkWindowClass *)class; GtkContainerClass *container_class = (GtkContainerClass *)class; - parent_class = g_type_class_peek_parent (class); bin_class = g_type_class_peek (GTK_TYPE_BIN); + gobject_class->get_property = gtk_plug_get_property; gobject_class->finalize = gtk_plug_finalize; widget_class->realize = gtk_plug_realize; @@ -133,47 +182,130 @@ gtk_plug_class_init (GtkPlugClass *class) widget_class->focus = gtk_plug_focus; + gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_PANEL); + container_class->check_resize = gtk_plug_check_resize; window_class->set_focus = gtk_plug_set_focus; window_class->keys_changed = gtk_plug_keys_changed; + /** + * GtkPlug:embedded: + * + * %TRUE if the plug is embedded in a socket. + * + * Since: 2.12 + */ + g_object_class_install_property (gobject_class, + PROP_EMBEDDED, + g_param_spec_boolean ("embedded", + P_("Embedded"), + P_("Whether the plug is embedded"), + FALSE, + GTK_PARAM_READABLE)); + + /** + * GtkPlug:socket-window: + * + * The window of the socket the plug is embedded in. + * + * Since: 2.14 + */ + g_object_class_install_property (gobject_class, + PROP_SOCKET_WINDOW, + g_param_spec_object ("socket-window", + P_("Socket Window"), + P_("The window of the socket the plug is embedded in"), + GDK_TYPE_WINDOW, + GTK_PARAM_READABLE)); + + /** + * GtkPlug::embedded: + * @plug: the object on which the signal was emitted + * + * Gets emitted when the plug becomes embedded in a socket. + */ plug_signals[EMBEDDED] = - g_signal_new ("embedded", + g_signal_new (I_("embedded"), G_OBJECT_CLASS_TYPE (class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkPlugClass, embedded), NULL, NULL, _gtk_marshal_VOID__VOID, G_TYPE_NONE, 0); + + g_type_class_add_private (class, sizeof (GtkPlugPrivate)); } static void gtk_plug_init (GtkPlug *plug) { - GtkWindow *window; + plug->priv = G_TYPE_INSTANCE_GET_PRIVATE (plug, + GTK_TYPE_PLUG, + GtkPlugPrivate); +} + +/** + * gtk_plug_handle_modality_on: + * @plug: a #GtkPlug + * + * Called from the GtkPlug backend when the corresponding socket has + * told the plug that it modality has toggled on. + */ +static void +gtk_plug_handle_modality_on (GtkPlug *plug) +{ + GtkPlugPrivate *priv = plug->priv; + + if (!priv->modality_window) + { + priv->modality_window = gtk_window_new (GTK_WINDOW_POPUP); + gtk_window_set_screen (GTK_WINDOW (priv->modality_window), + gtk_widget_get_screen (GTK_WIDGET (plug))); + gtk_widget_realize (priv->modality_window); + gtk_window_group_add_window (priv->modality_group, GTK_WINDOW (priv->modality_window)); + gtk_grab_add (priv->modality_window); + } +} - window = GTK_WINDOW (plug); +/** + * gtk_plug_handle_modality_off: + * @plug: a #GtkPlug + * + * Called from the GtkPlug backend when the corresponding socket has + * told the plug that it modality has toggled off. + */ +static void +gtk_plug_handle_modality_off (GtkPlug *plug) +{ + GtkPlugPrivate *priv = plug->priv; - window->type = GTK_WINDOW_TOPLEVEL; + if (priv->modality_window) + { + gtk_widget_destroy (priv->modality_window); + priv->modality_window = NULL; + } } static void gtk_plug_set_is_child (GtkPlug *plug, gboolean is_child) { - g_assert (!GTK_WIDGET (plug)->parent); - + GtkPlugPrivate *priv = plug->priv; + GtkWidget *widget = GTK_WIDGET (plug); + + g_assert (!gtk_widget_get_parent (widget)); + if (is_child) { - if (plug->modality_window) - handle_modality_off (plug); + if (priv->modality_window) + gtk_plug_handle_modality_off (plug); - if (plug->modality_group) + if (priv->modality_group) { - gtk_window_group_remove_window (plug->modality_group, GTK_WINDOW (plug)); - g_object_unref (plug->modality_group); - plug->modality_group = NULL; + gtk_window_group_remove_window (priv->modality_group, GTK_WINDOW (plug)); + g_object_unref (priv->modality_group); + priv->modality_group = NULL; } /* As a toplevel, the MAPPED flag doesn't correspond @@ -181,31 +313,88 @@ gtk_plug_set_is_child (GtkPlug *plug, * here, but don't bother remapping -- we will get mapped * by gtk_widget_set_parent (). */ - if (GTK_WIDGET_MAPPED (plug)) - gtk_widget_unmap (GTK_WIDGET (plug)); - - GTK_WIDGET_UNSET_FLAGS (plug, GTK_TOPLEVEL); + if (gtk_widget_get_mapped (widget)) + gtk_widget_unmap (widget); + + _gtk_window_set_is_toplevel (GTK_WINDOW (plug), FALSE); gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_PARENT); - _gtk_widget_propagate_hierarchy_changed (GTK_WIDGET (plug), GTK_WIDGET (plug)); + _gtk_widget_propagate_hierarchy_changed (widget, widget); } else { - if (GTK_WINDOW (plug)->focus_widget) + if (gtk_window_get_focus (GTK_WINDOW (plug))) gtk_window_set_focus (GTK_WINDOW (plug), NULL); - if (GTK_WINDOW (plug)->default_widget) + if (gtk_window_get_default_widget (GTK_WINDOW (plug))) gtk_window_set_default (GTK_WINDOW (plug), NULL); - - 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); + + priv->modality_group = gtk_window_group_new (); + gtk_window_group_add_window (priv->modality_group, GTK_WINDOW (plug)); + + _gtk_window_set_is_toplevel (GTK_WINDOW (plug), TRUE); gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_QUEUE); _gtk_widget_propagate_hierarchy_changed (GTK_WIDGET (plug), NULL); } } +/** + * 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_socket_add_id(). + * + * Return value: the window ID for the plug + **/ +Window +gtk_plug_get_id (GtkPlug *plug) +{ + g_return_val_if_fail (GTK_IS_PLUG (plug), 0); + + if (!gtk_widget_get_realized (GTK_WIDGET (plug))) + gtk_widget_realize (GTK_WIDGET (plug)); + + return GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (plug))); +} + +/** + * gtk_plug_get_embedded: + * @plug: a #GtkPlug + * + * Determines whether the plug is embedded in a socket. + * + * Return value: %TRUE if the plug is embedded in a socket + * + * Since: 2.14 + **/ +gboolean +gtk_plug_get_embedded (GtkPlug *plug) +{ + g_return_val_if_fail (GTK_IS_PLUG (plug), FALSE); + + return plug->priv->socket_window != NULL; +} + +/** + * gtk_plug_get_socket_window: + * @plug: a #GtkPlug + * + * Retrieves the socket the plug is embedded in. + * + * Return value: (transfer none): the window of the socket, or %NULL + * + * Since: 2.14 + **/ +GdkWindow * +gtk_plug_get_socket_window (GtkPlug *plug) +{ + g_return_val_if_fail (GTK_IS_PLUG (plug), NULL); + + return plug->priv->socket_window; +} + /** * _gtk_plug_add_to_socket: * @plug: a #GtkPlug @@ -215,49 +404,65 @@ gtk_plug_set_is_child (GtkPlug *plug, **/ void _gtk_plug_add_to_socket (GtkPlug *plug, - GtkSocket *socket) + GtkSocket *socket_) { + GtkPlugPrivate *priv; GtkWidget *widget; - gint w, h; g_return_if_fail (GTK_IS_PLUG (plug)); - g_return_if_fail (GTK_IS_SOCKET (socket)); - g_return_if_fail (GTK_WIDGET_REALIZED (socket)); + g_return_if_fail (GTK_IS_SOCKET (socket_)); + g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (socket_))); + priv = plug->priv; widget = GTK_WIDGET (plug); gtk_plug_set_is_child (plug, TRUE); - plug->same_app = TRUE; - socket->same_app = TRUE; - socket->plug_widget = widget; + priv->same_app = TRUE; + socket_->priv->same_app = TRUE; + socket_->priv->plug_widget = widget; - plug->socket_window = GTK_WIDGET (socket)->window; + priv->socket_window = gtk_widget_get_window (GTK_WIDGET (socket_)); + g_object_ref (priv->socket_window); + g_signal_emit (plug, plug_signals[EMBEDDED], 0); + g_object_notify (G_OBJECT (plug), "embedded"); - if (GTK_WIDGET_REALIZED (widget)) + if (gtk_widget_get_realized (widget)) { - gdk_drawable_get_size (GDK_DRAWABLE (widget->window), &w, &h); - gdk_window_reparent (widget->window, plug->socket_window, -w, -h); + GdkWindow *window; + + window = gtk_widget_get_window (widget); + gdk_window_reparent (window, priv->socket_window, + -gdk_window_get_width (window), + -gdk_window_get_height (window)); } - gtk_widget_set_parent (widget, GTK_WIDGET (socket)); + gtk_widget_set_parent (widget, GTK_WIDGET (socket_)); - g_signal_emit_by_name (socket, "plug_added", 0); + g_signal_emit_by_name (socket_, "plug-added"); } +/* + * gtk_plug_send_delete_event: + * @widget: a #GtkWidget + * + * Send a GDK_DELETE event to the @widget and destroy it if + * necessary. Internal GTK function, called from this file or the + * backend-specific GtkPlug implementation. + */ static void -send_delete_event (GtkWidget *widget) +gtk_plug_send_delete_event (GtkWidget *widget) { GdkEvent *event = gdk_event_new (GDK_DELETE); - - event->any.window = g_object_ref (widget->window); + + event->any.window = g_object_ref (gtk_widget_get_window (widget)); event->any.send_event = FALSE; - gtk_widget_ref (widget); - + g_object_ref (widget); + if (!gtk_widget_event (widget, event)) gtk_widget_destroy (widget); - - gtk_widget_unref (widget); + + g_object_unref (widget); gdk_event_free (event); } @@ -271,58 +476,68 @@ send_delete_event (GtkWidget *widget) **/ void _gtk_plug_remove_from_socket (GtkPlug *plug, - GtkSocket *socket) + GtkSocket *socket_) { + GtkPlugPrivate *priv; GtkWidget *widget; + GdkWindow *window; 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)); + g_return_if_fail (GTK_IS_SOCKET (socket_)); + g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (plug))); + priv = plug->priv; widget = GTK_WIDGET (plug); + if (_gtk_widget_get_in_reparent (widget)) + return; + g_object_ref (plug); - g_object_ref (socket); + g_object_ref (socket_); - widget_was_visible = GTK_WIDGET_VISIBLE (plug); - - gdk_window_hide (widget->window); - gdk_window_reparent (widget->window, + widget_was_visible = gtk_widget_get_visible (widget); + window = gtk_widget_get_window (widget); + + gdk_window_hide (window); + _gtk_widget_set_in_reparent (widget, TRUE); + gdk_window_reparent (window, gtk_widget_get_root_window (widget), 0, 0); - - GTK_PRIVATE_SET_FLAG (plug, GTK_IN_REPARENT); gtk_widget_unparent (GTK_WIDGET (plug)); - GTK_PRIVATE_UNSET_FLAG (plug, GTK_IN_REPARENT); + _gtk_widget_set_in_reparent (widget, FALSE); - socket->plug_widget = NULL; - if (socket->plug_window != NULL) + socket_->priv->plug_widget = NULL; + if (socket_->priv->plug_window != NULL) { - g_object_unref (socket->plug_window); - socket->plug_window = NULL; + g_object_unref (socket_->priv->plug_window); + socket_->priv->plug_window = NULL; } - socket->same_app = FALSE; - - plug->same_app = FALSE; - plug->socket_window = NULL; + socket_->priv->same_app = FALSE; + priv->same_app = FALSE; + if (priv->socket_window != NULL) + { + g_object_unref (priv->socket_window); + priv->socket_window = NULL; + } gtk_plug_set_is_child (plug, FALSE); - - g_signal_emit_by_name (socket, "plug_removed", &result); + + g_signal_emit_by_name (socket_, "plug-removed", &result); if (!result) - gtk_widget_destroy (GTK_WIDGET (socket)); + gtk_widget_destroy (GTK_WIDGET (socket_)); - send_delete_event (widget); + if (window) + gtk_plug_send_delete_event (widget); g_object_unref (plug); - if (widget_was_visible && GTK_WIDGET_VISIBLE (socket)) - gtk_widget_queue_resize (GTK_WIDGET (socket)); + if (widget_was_visible && gtk_widget_get_visible (GTK_WIDGET (socket_))) + gtk_widget_queue_resize (GTK_WIDGET (socket_)); - g_object_unref (socket); + g_object_unref (socket_); } /** @@ -334,8 +549,8 @@ _gtk_plug_remove_from_socket (GtkPlug *plug, * @socket_id. This function will generally only be used by classes deriving from #GtkPlug. **/ void -gtk_plug_construct (GtkPlug *plug, - GdkNativeWindow socket_id) +gtk_plug_construct (GtkPlug *plug, + Window socket_id) { gtk_plug_construct_for_display (plug, gdk_display_get_default (), socket_id); } @@ -354,34 +569,51 @@ gtk_plug_construct (GtkPlug *plug, * Since: 2.2 **/ void -gtk_plug_construct_for_display (GtkPlug *plug, - GdkDisplay *display, - GdkNativeWindow socket_id) +gtk_plug_construct_for_display (GtkPlug *plug, + GdkDisplay *display, + Window socket_id) { + GtkPlugPrivate *priv; + + g_return_if_fail (GTK_IS_PLUG (plug)); + g_return_if_fail (GDK_IS_DISPLAY (display)); + + priv = plug->priv; + if (socket_id) { gpointer user_data = NULL; - plug->socket_window = gdk_window_lookup_for_display (display, socket_id); - - if (plug->socket_window) - gdk_window_get_user_data (plug->socket_window, &user_data); + if (GDK_IS_X11_DISPLAY (display)) + priv->socket_window = gdk_x11_window_lookup_for_display (display, socket_id); else - plug->socket_window = gdk_window_foreign_new_for_display (display, socket_id); - - if (user_data) + priv->socket_window = NULL; + + if (priv->socket_window) { - if (GTK_IS_SOCKET (user_data)) - _gtk_plug_add_to_socket (plug, user_data); - else + gdk_window_get_user_data (priv->socket_window, &user_data); + + if (user_data) { - g_warning (G_STRLOC "Can't create GtkPlug as child of non-GtkSocket"); - plug->socket_window = NULL; + if (GTK_IS_SOCKET (user_data)) + _gtk_plug_add_to_socket (plug, user_data); + else + { + g_warning (G_STRLOC "Can't create GtkPlug as child of non-GtkSocket"); + priv->socket_window = NULL; + } } + else + g_object_ref (priv->socket_window); } + else if (GDK_IS_X11_DISPLAY (display)) + priv->socket_window = gdk_x11_window_foreign_new_for_display (display, socket_id); - if (plug->socket_window) + if (priv->socket_window) { g_signal_emit (plug, plug_signals[EMBEDDED], 0); + + g_object_notify (G_OBJECT (plug), "embedded"); + } } } @@ -396,14 +628,14 @@ gtk_plug_construct_for_display (GtkPlug *plug, * Return value: the new #GtkPlug widget. **/ GtkWidget* -gtk_plug_new (GdkNativeWindow socket_id) +gtk_plug_new (Window socket_id) { return gtk_plug_new_for_display (gdk_display_get_default (), socket_id); } /** * gtk_plug_new_for_display: - * @display : the #GdkDisplay on which @socket_id is displayed + * @display: the #GdkDisplay on which @socket_id is displayed * @socket_id: the XID of the socket's window. * * Create a new plug widget inside the #GtkSocket identified by socket_id. @@ -413,200 +645,498 @@ gtk_plug_new (GdkNativeWindow socket_id) * Since: 2.2 */ GtkWidget* -gtk_plug_new_for_display (GdkDisplay *display, - GdkNativeWindow socket_id) +gtk_plug_new_for_display (GdkDisplay *display, + Window socket_id) { GtkPlug *plug; + g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); + plug = g_object_new (GTK_TYPE_PLUG, NULL); gtk_plug_construct_for_display (plug, display, 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_socket_add_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_finalize (GObject *object) { GtkPlug *plug = GTK_PLUG (object); + GtkPlugPrivate *priv = plug->priv; - if (plug->grabbed_keys) - { - g_hash_table_destroy (plug->grabbed_keys); - plug->grabbed_keys = NULL; - } - - G_OBJECT_CLASS (parent_class)->finalize (object); + if (priv->grabbed_keys) + g_hash_table_destroy (priv->grabbed_keys); + + G_OBJECT_CLASS (gtk_plug_parent_class)->finalize (object); } static void gtk_plug_unrealize (GtkWidget *widget) { - GtkPlug *plug; - - g_return_if_fail (GTK_IS_PLUG (widget)); - - plug = GTK_PLUG (widget); + GtkPlug *plug = GTK_PLUG (widget); + GtkPlugPrivate *priv = plug->priv; - if (plug->socket_window != NULL) + if (priv->socket_window != NULL) { - gdk_window_set_user_data (plug->socket_window, NULL); - g_object_unref (plug->socket_window); - plug->socket_window = NULL; + gtk_widget_unregister_window (widget, priv->socket_window); + g_object_unref (priv->socket_window); + priv->socket_window = NULL; + + g_object_notify (G_OBJECT (widget), "embedded"); } - if (!plug->same_app) + if (!priv->same_app) { - if (plug->modality_window) - handle_modality_off (plug); + if (priv->modality_window) + gtk_plug_handle_modality_off (plug); - gtk_window_group_remove_window (plug->modality_group, GTK_WINDOW (plug)); - g_object_unref (plug->modality_group); + gtk_window_group_remove_window (priv->modality_group, GTK_WINDOW (plug)); + g_object_unref (priv->modality_group); } - - if (GTK_WIDGET_CLASS (parent_class)->unrealize) - (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); + + GTK_WIDGET_CLASS (gtk_plug_parent_class)->unrealize (widget); } static void -gtk_plug_realize (GtkWidget *widget) +xembed_set_info (GdkWindow *window, + unsigned long flags) { - GtkWindow *window; - GtkPlug *plug; - GdkWindowAttr attributes; - gint attributes_mask; - - g_return_if_fail (GTK_IS_PLUG (widget)); + GdkDisplay *display = gdk_window_get_display (window); + unsigned long buffer[2]; - GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); - window = GTK_WINDOW (widget); - plug = GTK_PLUG (widget); + Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"); - attributes.window_type = GDK_WINDOW_CHILD; /* XXX GDK_WINDOW_PLUG ? */ - attributes.title = window->title; - attributes.wmclass_name = window->wmclass_name; - attributes.wmclass_class = window->wmclass_class; - attributes.width = widget->allocation.width; - attributes.height = widget->allocation.height; - attributes.wclass = GDK_INPUT_OUTPUT; + buffer[0] = GTK_XEMBED_PROTOCOL_VERSION; + buffer[1] = flags; - /* this isn't right - we should match our parent's visual/colormap. - * though that will require handling "foreign" colormaps */ - attributes.visual = gtk_widget_get_visual (widget); - attributes.colormap = gtk_widget_get_colormap (widget); - attributes.event_mask = gtk_widget_get_events (widget); - attributes.event_mask |= (GDK_EXPOSURE_MASK | - GDK_KEY_PRESS_MASK | - GDK_KEY_RELEASE_MASK | - GDK_ENTER_NOTIFY_MASK | - GDK_LEAVE_NOTIFY_MASK | - GDK_STRUCTURE_MASK); + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_WINDOW_XID (window), + xembed_info_atom, xembed_info_atom, 32, + PropModeReplace, + (unsigned char *)buffer, 2); +} - attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP; - attributes_mask |= (window->title ? GDK_WA_TITLE : 0); - attributes_mask |= (window->wmclass_name ? GDK_WA_WMCLASS : 0); +/** + * gtk_plug_focus_first_last: + * @plug: a #GtkPlug + * @direction: a direction + * + * Called from the GtkPlug backend when the corresponding socket has + * told the plug that it has received the focus. + */ +static void +gtk_plug_focus_first_last (GtkPlug *plug, + GtkDirectionType direction) +{ + GtkWindow *window = GTK_WINDOW (plug); + GtkWidget *focus_widget; + GtkWidget *parent; - if (GTK_WIDGET_TOPLEVEL (widget)) + focus_widget = gtk_window_get_focus (window); + if (focus_widget) { - attributes.window_type = GDK_WINDOW_TOPLEVEL; - - gdk_error_trap_push (); - if (plug->socket_window) - widget->window = gdk_window_new (plug->socket_window, - &attributes, attributes_mask); - else /* If it's a passive plug, we use the root window */ - widget->window = gdk_window_new (gtk_widget_get_root_window (widget), - &attributes, attributes_mask); - - gdk_display_sync (gtk_widget_get_display (widget)); - if (gdk_error_trap_pop ()) /* Uh-oh */ + parent = gtk_widget_get_parent (focus_widget); + while (parent) { - gdk_error_trap_push (); - gdk_window_destroy (widget->window); - gdk_flush (); - gdk_error_trap_pop (); - widget->window = gdk_window_new (gtk_widget_get_root_window (widget), - &attributes, attributes_mask); + gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL); + parent = gtk_widget_get_parent (parent); } - gdk_window_add_filter (widget->window, gtk_plug_filter_func, widget); - - plug->modality_group = gtk_window_group_new (); - gtk_window_group_add_window (plug->modality_group, window); - - xembed_set_info (widget->window, 0); + gtk_window_set_focus (GTK_WINDOW (plug), NULL); } - else - widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), - &attributes, attributes_mask); - - gdk_window_set_user_data (widget->window, window); - - widget->style = gtk_style_attach (widget->style, widget->window); - gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); - - gdk_window_enable_synchronized_configure (widget->window); -} -static void -gtk_plug_show (GtkWidget *widget) -{ - if (GTK_WIDGET_TOPLEVEL (widget)) - GTK_WIDGET_CLASS (parent_class)->show (widget); - else - GTK_WIDGET_CLASS (bin_class)->show (widget); + gtk_widget_child_focus (GTK_WIDGET (plug), direction); } static void -gtk_plug_hide (GtkWidget *widget) +handle_xembed_message (GtkPlug *plug, + XEmbedMessageType message, + glong detail, + glong data1, + glong data2, + guint32 time) { - if (GTK_WIDGET_TOPLEVEL (widget)) - GTK_WIDGET_CLASS (parent_class)->hide (widget); - else - GTK_WIDGET_CLASS (bin_class)->hide (widget); -} - -/* From gdkinternals.h */ -void gdk_synthesize_window_state (GdkWindow *window, - GdkWindowState unset_flags, - GdkWindowState set_flags); + GtkWindow *window = GTK_WINDOW (plug); -static void -gtk_plug_map (GtkWidget *widget) -{ - if (GTK_WIDGET_TOPLEVEL (widget)) + GTK_NOTE (PLUGSOCKET, + g_message ("GtkPlug: %s received", _gtk_xembed_message_name (message))); + + switch (message) { - GtkBin *bin = GTK_BIN (widget); - - GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); - - if (bin->child && - GTK_WIDGET_VISIBLE (bin->child) && - !GTK_WIDGET_MAPPED (bin->child)) - gtk_widget_map (bin->child); - - xembed_set_info (widget->window, XEMBED_MAPPED); + case XEMBED_EMBEDDED_NOTIFY: + break; + case XEMBED_WINDOW_ACTIVATE: + _gtk_window_set_is_active (window, TRUE); + break; + case XEMBED_WINDOW_DEACTIVATE: + _gtk_window_set_is_active (window, FALSE); + break; - gdk_synthesize_window_state (widget->window, + case XEMBED_MODALITY_ON: + gtk_plug_handle_modality_on (plug); + break; + case XEMBED_MODALITY_OFF: + gtk_plug_handle_modality_off (plug); + break; + + case XEMBED_FOCUS_IN: + _gtk_window_set_has_toplevel_focus (window, TRUE); + switch (detail) + { + case XEMBED_FOCUS_FIRST: + gtk_plug_focus_first_last (plug, GTK_DIR_TAB_FORWARD); + break; + case XEMBED_FOCUS_LAST: + gtk_plug_focus_first_last (plug, GTK_DIR_TAB_BACKWARD); + break; + case XEMBED_FOCUS_CURRENT: + break; + } + break; + + case XEMBED_FOCUS_OUT: + _gtk_window_set_has_toplevel_focus (window, FALSE); + break; + + case XEMBED_GRAB_KEY: + case XEMBED_UNGRAB_KEY: + case XEMBED_GTK_GRAB_KEY: + case XEMBED_GTK_UNGRAB_KEY: + case XEMBED_REQUEST_FOCUS: + case XEMBED_FOCUS_NEXT: + case XEMBED_FOCUS_PREV: + g_warning ("GtkPlug: Invalid _XEMBED message %s received", _gtk_xembed_message_name (message)); + break; + + default: + GTK_NOTE(PLUGSOCKET, + g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %d", message)); + break; + } +} +static GdkFilterReturn +gtk_plug_filter_func (GdkXEvent *gdk_xevent, + GdkEvent *event, + gpointer data) +{ + GdkScreen *screen = gdk_window_get_screen (event->any.window); + GdkDisplay *display = gdk_screen_get_display (screen); + GtkPlug *plug = GTK_PLUG (data); + GtkPlugPrivate *priv = plug->priv; + XEvent *xevent = (XEvent *)gdk_xevent; + GHashTableIter iter; + gpointer key; + GdkFilterReturn return_val; + + return_val = GDK_FILTER_CONTINUE; + + switch (xevent->type) + { + case ClientMessage: + if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED")) + { + _gtk_xembed_push_message (xevent); + handle_xembed_message (plug, + xevent->xclient.data.l[1], + xevent->xclient.data.l[2], + xevent->xclient.data.l[3], + xevent->xclient.data.l[4], + xevent->xclient.data.l[0]); + _gtk_xembed_pop_message (); + + return_val = GDK_FILTER_REMOVE; + } + else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW")) + { + /* We filter these out because we take being reparented back to the + * root window as the reliable end of the embedding protocol + */ + + return_val = GDK_FILTER_REMOVE; + } + break; + case ReparentNotify: + { + XReparentEvent *xre = &xevent->xreparent; + gboolean was_embedded = priv->socket_window != NULL; + + GTK_NOTE (PLUGSOCKET, g_message("GtkPlug: ReparentNotify received")); + + return_val = GDK_FILTER_REMOVE; + + g_object_ref (plug); + + if (was_embedded) + { + /* End of embedding protocol for previous socket */ + + GTK_NOTE (PLUGSOCKET, g_message ("GtkPlug: end of embedding")); + /* 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_XID (priv->socket_window)) + { + GtkWidget *widget = GTK_WIDGET (plug); + + gtk_widget_unregister_window (widget, priv->socket_window); + g_object_unref (priv->socket_window); + priv->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_WINDOW_XID (gdk_screen_get_root_window (screen))) + { + GTK_NOTE (PLUGSOCKET, g_message ("GtkPlug: calling gtk_plug_send_delete_event()")); + gtk_plug_send_delete_event (widget); + + g_object_notify (G_OBJECT (plug), "embedded"); + } + } + else + goto done; + } + + if (xre->parent != GDK_WINDOW_XID (gdk_screen_get_root_window (screen))) + { + /* Start of embedding protocol */ + + GTK_NOTE (PLUGSOCKET, g_message ("GtkPlug: start of embedding")); + + priv->socket_window = gdk_x11_window_lookup_for_display (display, xre->parent); + if (priv->socket_window) + { + gpointer user_data = NULL; + gdk_window_get_user_data (priv->socket_window, &user_data); + + if (user_data) + { + g_warning (G_STRLOC "Plug reparented unexpectedly into window in the same process"); + priv->socket_window = NULL; + break; /* FIXME: shouldn't this unref the plug? i.e. "goto done;" instead */ + } + + g_object_ref (priv->socket_window); + } + else + { + priv->socket_window = gdk_x11_window_foreign_new_for_display (display, xre->parent); + if (!priv->socket_window) /* Already gone */ + break; /* FIXME: shouldn't this unref the plug? i.e. "goto done;" instead */ + } + + if (priv->grabbed_keys) + { + g_hash_table_iter_init (&iter, priv->grabbed_keys); + while (g_hash_table_iter_next (&iter, &key, NULL)) + { + GrabbedKey *grabbed_key = key; + + _gtk_xembed_send_message (priv->socket_window, XEMBED_GTK_GRAB_KEY, 0, + grabbed_key->accelerator_key, + grabbed_key->accelerator_mods); + } + } + + if (!was_embedded) + g_signal_emit_by_name (plug, "embedded"); + + g_object_notify (G_OBJECT (plug), "embedded"); + } + + done: + g_object_unref (plug); + + break; + } + case KeyPress: + case KeyRelease: + { + GdkModifierType state, consumed; + GdkDeviceManager *device_manager; + GdkDevice *pointer, *keyboard; + GdkKeymap *keymap; + + if (xevent->type == KeyPress) + event->key.type = GDK_KEY_PRESS; + else + event->key.type = GDK_KEY_RELEASE; + + event->key.window = gdk_x11_window_lookup_for_display (display, xevent->xany.window); + event->key.send_event = TRUE; + event->key.time = xevent->xkey.time; + event->key.state = (GdkModifierType) xevent->xkey.state; + event->key.hardware_keycode = xevent->xkey.keycode; + event->key.keyval = GDK_KEY_VoidSymbol; + + device_manager = gdk_display_get_device_manager (display); + pointer = gdk_device_manager_get_client_pointer (device_manager); + keyboard = gdk_device_get_associated_device (pointer); + gdk_event_set_device (event, keyboard); + + keymap = gdk_keymap_get_for_display (display); + + event->key.group = gdk_x11_keymap_get_group_for_state (keymap, xevent->xkey.state); + event->key.is_modifier = gdk_x11_keymap_key_is_modifier (keymap, event->key.hardware_keycode); + + gdk_keymap_translate_keyboard_state (keymap, + event->key.hardware_keycode, + event->key.state, + event->key.group, + &event->key.keyval, + NULL, NULL, &consumed); + + state = event->key.state & ~consumed; + gdk_keymap_add_virtual_modifiers (keymap, &state); + event->key.state |= state; + + event->key.length = 0; + event->key.string = g_strdup (""); + + return_val = GDK_FILTER_TRANSLATE; + } + } + + return return_val; +} +static void +gtk_plug_realize (GtkWidget *widget) +{ + GtkAllocation allocation; + GtkPlug *plug = GTK_PLUG (widget); + GtkPlugPrivate *priv = plug->priv; + GtkWindow *window = GTK_WINDOW (widget); + GdkWindow *gdk_window; + GdkWindowAttr attributes; + const gchar *title; + gchar *wmclass_name, *wmclass_class; + gint attributes_mask; + + gtk_widget_set_realized (widget, TRUE); + + title = gtk_window_get_title (window); + _gtk_window_get_wmclass (window, &wmclass_name, &wmclass_class); + gtk_widget_get_allocation (widget, &allocation); + + attributes.window_type = GDK_WINDOW_CHILD; /* XXX GDK_WINDOW_PLUG ? */ + attributes.title = (gchar *) title; + attributes.wmclass_name = wmclass_name; + attributes.wmclass_class = wmclass_class; + attributes.width = allocation.width; + attributes.height = allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + + /* this isn't right - we should match our parent's visual/colormap. + * though that will require handling "foreign" colormaps */ + attributes.visual = gtk_widget_get_visual (widget); + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= (GDK_EXPOSURE_MASK | + GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK | + GDK_ENTER_NOTIFY_MASK | + GDK_LEAVE_NOTIFY_MASK | + GDK_STRUCTURE_MASK); + + attributes_mask = GDK_WA_VISUAL; + attributes_mask |= (title ? GDK_WA_TITLE : 0); + attributes_mask |= (wmclass_name ? GDK_WA_WMCLASS : 0); + + if (gtk_widget_is_toplevel (widget)) + { + attributes.window_type = GDK_WINDOW_TOPLEVEL; + + gdk_error_trap_push (); + if (priv->socket_window) + gdk_window = gdk_window_new (priv->socket_window, + &attributes, attributes_mask); + else /* If it's a passive plug, we use the root window */ + gdk_window = gdk_window_new (gtk_widget_get_root_window (widget), + &attributes, attributes_mask); + gtk_widget_set_window (widget, gdk_window); + + gdk_display_sync (gtk_widget_get_display (widget)); + if (gdk_error_trap_pop ()) /* Uh-oh */ + { + gdk_error_trap_push (); + gdk_window_destroy (gdk_window); + gdk_error_trap_pop_ignored (); + gdk_window = gdk_window_new (gtk_widget_get_root_window (widget), + &attributes, attributes_mask); + gtk_widget_set_window (widget, gdk_window); + } + + gdk_window_add_filter (gdk_window, + gtk_plug_filter_func, + widget); + + priv->modality_group = gtk_window_group_new (); + gtk_window_group_add_window (priv->modality_group, window); + + xembed_set_info (gtk_widget_get_window (GTK_WIDGET (plug)), 0); + } + else + { + gdk_window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); + gtk_widget_set_window (widget, gdk_window); + } + + gtk_widget_register_window (widget, gdk_window); + + gtk_style_context_set_background (gtk_widget_get_style_context (widget), + gdk_window); +} + +static void +gtk_plug_show (GtkWidget *widget) +{ + if (gtk_widget_is_toplevel (widget)) + GTK_WIDGET_CLASS (gtk_plug_parent_class)->show (widget); + else + GTK_WIDGET_CLASS (bin_class)->show (widget); +} + +static void +gtk_plug_hide (GtkWidget *widget) +{ + if (gtk_widget_is_toplevel (widget)) + GTK_WIDGET_CLASS (gtk_plug_parent_class)->hide (widget); + else + GTK_WIDGET_CLASS (bin_class)->hide (widget); +} + +/* From gdkinternals.h */ +void gdk_synthesize_window_state (GdkWindow *window, + GdkWindowState unset_flags, + GdkWindowState set_flags); + +static void +gtk_plug_map (GtkWidget *widget) +{ + if (gtk_widget_is_toplevel (widget)) + { + GtkBin *bin = GTK_BIN (widget); + GtkPlug *plug = GTK_PLUG (widget); + GtkWidget *child; + + gtk_widget_set_mapped (widget, TRUE); + + child = gtk_bin_get_child (bin); + if (child != NULL && + gtk_widget_get_visible (child) && + !gtk_widget_get_mapped (child)) + gtk_widget_map (child); + + xembed_set_info (gtk_widget_get_window (GTK_WIDGET (plug)), XEMBED_MAPPED); + + gdk_synthesize_window_state (gtk_widget_get_window (widget), GDK_WINDOW_STATE_WITHDRAWN, 0); } @@ -617,14 +1147,25 @@ gtk_plug_map (GtkWidget *widget) static void gtk_plug_unmap (GtkWidget *widget) { - if (GTK_WIDGET_TOPLEVEL (widget)) + if (gtk_widget_is_toplevel (widget)) { - GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); + GtkPlug *plug = GTK_PLUG (widget); + GdkWindow *window; + GtkWidget *child; - gdk_window_hide (widget->window); - xembed_set_info (widget->window, 0); - - gdk_synthesize_window_state (widget->window, + window = gtk_widget_get_window (widget); + + gtk_widget_set_mapped (widget, FALSE); + + gdk_window_hide (window); + + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child != NULL) + gtk_widget_unmap (child); + + xembed_set_info (gtk_widget_get_window (GTK_WIDGET (plug)), 0); + + gdk_synthesize_window_state (window, 0, GDK_WINDOW_STATE_WITHDRAWN); } @@ -636,30 +1177,34 @@ static void gtk_plug_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { - if (GTK_WIDGET_TOPLEVEL (widget)) - GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation); + GtkWidget *child; + + if (gtk_widget_is_toplevel (widget)) + GTK_WIDGET_CLASS (gtk_plug_parent_class)->size_allocate (widget, allocation); else { GtkBin *bin = GTK_BIN (widget); - widget->allocation = *allocation; + gtk_widget_set_allocation (widget, allocation); - if (GTK_WIDGET_REALIZED (widget)) - gdk_window_move_resize (widget->window, + if (gtk_widget_get_realized (widget)) + gdk_window_move_resize (gtk_widget_get_window (widget), allocation->x, allocation->y, allocation->width, allocation->height); - if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) + child = gtk_bin_get_child (bin); + + if (child != NULL && gtk_widget_get_visible (child)) { GtkAllocation child_allocation; - child_allocation.x = child_allocation.y = GTK_CONTAINER (widget)->border_width; + child_allocation.x = child_allocation.y = gtk_container_get_border_width (GTK_CONTAINER (widget)); child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2); child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2); - gtk_widget_size_allocate (bin->child, &child_allocation); + gtk_widget_size_allocate (child, &child_allocation); } } @@ -669,8 +1214,8 @@ static gboolean gtk_plug_key_press_event (GtkWidget *widget, GdkEventKey *event) { - if (GTK_WIDGET_TOPLEVEL (widget)) - return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event); + if (gtk_widget_is_toplevel (widget)) + return GTK_WIDGET_CLASS (gtk_plug_parent_class)->key_press_event (widget, event); else return FALSE; } @@ -691,25 +1236,18 @@ gtk_plug_set_focus (GtkWindow *window, GtkWidget *focus) { GtkPlug *plug = GTK_PLUG (window); + GtkPlugPrivate *priv = plug->priv; - GTK_WINDOW_CLASS (parent_class)->set_focus (window, focus); + GTK_WINDOW_CLASS (gtk_plug_parent_class)->set_focus (window, focus); /* Ask for focus from embedder */ - if (focus && !window->has_toplevel_focus) - { - _gtk_xembed_send_message (plug->socket_window, - XEMBED_REQUEST_FOCUS, 0, 0, 0); - } + if (focus && !gtk_window_has_toplevel_focus (window)) + _gtk_xembed_send_message (priv->socket_window, + XEMBED_REQUEST_FOCUS, 0, 0, 0); } -typedef struct -{ - guint accelerator_key; - GdkModifierType accelerator_mods; -} GrabbedKey; - static guint grabbed_key_hash (gconstpointer a) { @@ -738,36 +1276,30 @@ add_grabbed_key (gpointer key, gpointer val, gpointer data) { GrabbedKey *grabbed_key = key; GtkPlug *plug = data; + GtkPlugPrivate *priv = plug->priv; - if (!plug->grabbed_keys || - !g_hash_table_lookup (plug->grabbed_keys, grabbed_key)) + if (!priv->grabbed_keys || + !g_hash_table_lookup (priv->grabbed_keys, grabbed_key)) { - _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_GRAB_KEY, 0, - grabbed_key->accelerator_key, grabbed_key->accelerator_mods); + _gtk_xembed_send_message (priv->socket_window, XEMBED_GTK_GRAB_KEY, 0, + grabbed_key->accelerator_key, + grabbed_key->accelerator_mods); } } -static void -add_grabbed_key_always (gpointer key, gpointer val, gpointer data) -{ - GrabbedKey *grabbed_key = key; - GtkPlug *plug = data; - - _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_GRAB_KEY, 0, - grabbed_key->accelerator_key, grabbed_key->accelerator_mods); -} - static void remove_grabbed_key (gpointer key, gpointer val, gpointer data) { GrabbedKey *grabbed_key = key; GtkPlug *plug = data; + GtkPlugPrivate *priv = plug->priv; - if (!plug->grabbed_keys || - !g_hash_table_lookup (plug->grabbed_keys, grabbed_key)) + if (!priv->grabbed_keys || + !g_hash_table_lookup (priv->grabbed_keys, grabbed_key)) { - _gtk_xembed_send_message (plug->socket_window, XEMBED_GTK_UNGRAB_KEY, 0, - grabbed_key->accelerator_key, grabbed_key->accelerator_mods); + _gtk_xembed_send_message (priv->socket_window, XEMBED_GTK_UNGRAB_KEY, 0, + grabbed_key->accelerator_key, + grabbed_key->accelerator_mods); } } @@ -779,7 +1311,7 @@ keys_foreach (GtkWindow *window, gpointer data) { GHashTable *new_grabbed_keys = data; - GrabbedKey *key = g_new (GrabbedKey, 1); + GrabbedKey *key = g_slice_new (GrabbedKey); key->accelerator_key = keyval; key->accelerator_mods = modifiers; @@ -787,34 +1319,42 @@ keys_foreach (GtkWindow *window, g_hash_table_replace (new_grabbed_keys, key, key); } +static void +grabbed_key_free (gpointer data) +{ + g_slice_free (GrabbedKey, data); +} + static void gtk_plug_keys_changed (GtkWindow *window) { GHashTable *new_grabbed_keys, *old_grabbed_keys; GtkPlug *plug = GTK_PLUG (window); + GtkPlugPrivate *priv = plug->priv; - new_grabbed_keys = g_hash_table_new_full (grabbed_key_hash, grabbed_key_equal, (GDestroyNotify)g_free, NULL); + new_grabbed_keys = g_hash_table_new_full (grabbed_key_hash, grabbed_key_equal, (GDestroyNotify)grabbed_key_free, NULL); _gtk_window_keys_foreach (window, keys_foreach, new_grabbed_keys); - if (plug->socket_window) + if (priv->socket_window) g_hash_table_foreach (new_grabbed_keys, add_grabbed_key, plug); - old_grabbed_keys = plug->grabbed_keys; - plug->grabbed_keys = new_grabbed_keys; + old_grabbed_keys = priv->grabbed_keys; + priv->grabbed_keys = new_grabbed_keys; if (old_grabbed_keys) { - if (plug->socket_window) - g_hash_table_foreach (old_grabbed_keys, remove_grabbed_key, plug); + if (priv->socket_window) + g_hash_table_foreach (old_grabbed_keys, remove_grabbed_key, plug); g_hash_table_destroy (old_grabbed_keys); } } static void -focus_to_parent (GtkPlug *plug, - GtkDirectionType direction) +gtk_plug_focus_to_parent (GtkPlug *plug, + GtkDirectionType direction) { - XEmbedMessageType message = XEMBED_FOCUS_PREV; /* Quiet GCC */ + GtkPlugPrivate *priv = plug->priv; + XEmbedMessageType message; switch (direction) { @@ -828,9 +1368,13 @@ focus_to_parent (GtkPlug *plug, case GTK_DIR_TAB_FORWARD: message = XEMBED_FOCUS_NEXT; break; + default: + g_assert_not_reached (); + message = XEMBED_FOCUS_PREV; + break; } - - _gtk_xembed_send_focus_message (plug->socket_window, message, 0); + + _gtk_xembed_send_focus_message (priv->socket_window, message, 0); } static gboolean @@ -841,24 +1385,29 @@ gtk_plug_focus (GtkWidget *widget, GtkPlug *plug = GTK_PLUG (widget); GtkWindow *window = GTK_WINDOW (widget); GtkContainer *container = GTK_CONTAINER (widget); - GtkWidget *old_focus_child = container->focus_child; + GtkWidget *child; + GtkWidget *old_focus_child; GtkWidget *parent; - + + old_focus_child = gtk_container_get_focus_child (container); /* We override GtkWindow's behavior, since we don't want wrapping here. */ if (old_focus_child) { + GtkWidget *focus_widget; + if (gtk_widget_child_focus (old_focus_child, direction)) return TRUE; - if (window->focus_widget) + focus_widget = gtk_window_get_focus (window); + if (focus_widget) { /* Wrapped off the end, clear the focus setting for the toplevel */ - parent = window->focus_widget->parent; + parent = gtk_widget_get_parent (focus_widget); while (parent) { gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL); - parent = GTK_WIDGET (parent)->parent; + parent = gtk_widget_get_parent (parent); } gtk_window_set_focus (GTK_WINDOW (container), NULL); @@ -867,12 +1416,13 @@ gtk_plug_focus (GtkWidget *widget, else { /* Try to focus the first widget in the window */ - if (bin->child && gtk_widget_child_focus (bin->child, direction)) + child = gtk_bin_get_child (bin); + if (child && gtk_widget_child_focus (child, direction)) return TRUE; } - if (!GTK_CONTAINER (window)->focus_child) - focus_to_parent (plug, direction); + if (!gtk_container_get_focus_child (GTK_CONTAINER (window))) + gtk_plug_focus_to_parent (plug, direction); return FALSE; } @@ -880,265 +1430,9 @@ gtk_plug_focus (GtkWidget *widget, static void gtk_plug_check_resize (GtkContainer *container) { - if (GTK_WIDGET_TOPLEVEL (container)) - GTK_CONTAINER_CLASS (parent_class)->check_resize (container); + if (gtk_widget_is_toplevel (GTK_WIDGET (container))) + GTK_CONTAINER_CLASS (gtk_plug_parent_class)->check_resize (container); else GTK_CONTAINER_CLASS (bin_class)->check_resize (container); } -static void -focus_first_last (GtkPlug *plug, - GtkDirectionType direction) -{ - GtkWindow *window = GTK_WINDOW (plug); - GtkWidget *parent; - - if (window->focus_widget) - { - parent = window->focus_widget->parent; - while (parent) - { - gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL); - parent = GTK_WIDGET (parent)->parent; - } - - gtk_window_set_focus (GTK_WINDOW (plug), NULL); - } - - gtk_widget_child_focus (GTK_WIDGET (plug), direction); -} - -static void -handle_modality_on (GtkPlug *plug) -{ - if (!plug->modality_window) - { - plug->modality_window = gtk_window_new (GTK_WINDOW_POPUP); - gtk_window_set_screen (GTK_WINDOW (plug->modality_window), - gtk_widget_get_screen (GTK_WIDGET (plug))); - gtk_widget_realize (plug->modality_window); - gtk_window_group_add_window (plug->modality_group, GTK_WINDOW (plug->modality_window)); - gtk_grab_add (plug->modality_window); - } -} - -static void -handle_modality_off (GtkPlug *plug) -{ - if (plug->modality_window) - { - gtk_widget_destroy (plug->modality_window); - plug->modality_window = NULL; - } -} - -static void -xembed_set_info (GdkWindow *window, - unsigned long flags) -{ - GdkDisplay *display = gdk_drawable_get_display (window); - unsigned long buffer[2]; - - Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO"); - - buffer[0] = GTK_XEMBED_PROTOCOL_VERSION; - buffer[1] = flags; - - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_WINDOW_XWINDOW (window), - xembed_info_atom, xembed_info_atom, 32, - PropModeReplace, - (unsigned char *)buffer, 2); -} - -static void -handle_xembed_message (GtkPlug *plug, - XEmbedMessageType message, - glong detail, - glong data1, - glong data2, - guint32 time) -{ - GtkWindow *window = GTK_WINDOW (plug); - - GTK_NOTE (PLUGSOCKET, - g_message ("GtkPlug: Message of type %d received", message)); - - switch (message) - { - case XEMBED_EMBEDDED_NOTIFY: - break; - case XEMBED_WINDOW_ACTIVATE: - _gtk_window_set_is_active (window, TRUE); - break; - case XEMBED_WINDOW_DEACTIVATE: - _gtk_window_set_is_active (window, FALSE); - break; - - case XEMBED_MODALITY_ON: - handle_modality_on (plug); - break; - case XEMBED_MODALITY_OFF: - handle_modality_off (plug); - break; - - case XEMBED_FOCUS_IN: - _gtk_window_set_has_toplevel_focus (window, TRUE); - switch (detail) - { - case XEMBED_FOCUS_FIRST: - focus_first_last (plug, GTK_DIR_TAB_FORWARD); - break; - case XEMBED_FOCUS_LAST: - focus_first_last (plug, GTK_DIR_TAB_BACKWARD); - break; - case XEMBED_FOCUS_CURRENT: - break; - } - break; - - case XEMBED_FOCUS_OUT: - _gtk_window_set_has_toplevel_focus (window, FALSE); - break; - - case XEMBED_GRAB_KEY: - case XEMBED_UNGRAB_KEY: - case XEMBED_GTK_GRAB_KEY: - case XEMBED_GTK_UNGRAB_KEY: - case XEMBED_REQUEST_FOCUS: - case XEMBED_FOCUS_NEXT: - case XEMBED_FOCUS_PREV: - g_warning ("GtkPlug: Invalid _XEMBED message of type %d received", message); - break; - - default: - GTK_NOTE(PLUGSOCKET, - g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %d", message)); - break; - } -} - -static GdkFilterReturn -gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) -{ - GdkScreen *screen = gdk_drawable_get_screen (event->any.window); - GdkDisplay *display = gdk_screen_get_display (screen); - GtkPlug *plug = GTK_PLUG (data); - XEvent *xevent = (XEvent *)gdk_xevent; - - GdkFilterReturn return_val; - - return_val = GDK_FILTER_CONTINUE; - - switch (xevent->type) - { - case ClientMessage: - if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED")) - { - _gtk_xembed_push_message (xevent); - handle_xembed_message (plug, - xevent->xclient.data.l[1], - xevent->xclient.data.l[2], - xevent->xclient.data.l[3], - xevent->xclient.data.l[4], - xevent->xclient.data.l[0]); - _gtk_xembed_pop_message (); - - return GDK_FILTER_REMOVE; - } - else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW")) - { - /* 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); - g_object_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_WINDOW_XWINDOW (gdk_screen_get_root_window (screen))) - send_delete_event (widget); - } - else - goto done; - } - - if (xre->parent != GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen))) - { - /* Start of embedding protocol */ - - plug->socket_window = gdk_window_lookup_for_display (display, 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_for_display (display, xre->parent); - if (!plug->socket_window) /* Already gone */ - break; - } - - if (plug->grabbed_keys) - g_hash_table_foreach (plug->grabbed_keys, add_grabbed_key_always, plug); - - if (!was_embedded) - g_signal_emit (plug, plug_signals[EMBEDDED], 0); - } - - done: - g_object_unref (plug); - - break; - } - } - - return GDK_FILTER_CONTINUE; -} - -#define __GTK_PLUG_C__ -#include "gtkaliasdef.c"