]> Pileus Git - ~andy/gtk/blob - gtk/gtkplug.c
Fix ZWJ => ZWN typo. (#83092, Tino Meinen)
[~andy/gtk] / gtk / gtkplug.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /* By Owen Taylor <otaylor@gtk.org>              98/4/4 */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 #include "gtkmain.h"
29 #include "gtkmarshalers.h"
30 #include "gtkplug.h"
31 #include "gtkprivate.h"
32
33 #include "gdk/gdkkeysyms.h"
34 #include "x11/gdkx.h"
35
36 #include "xembed.h"
37
38 static void            gtk_plug_class_init            (GtkPlugClass     *klass);
39 static void            gtk_plug_init                  (GtkPlug          *plug);
40 static void            gtk_plug_finalize              (GObject          *object);
41 static void            gtk_plug_realize               (GtkWidget        *widget);
42 static void            gtk_plug_unrealize             (GtkWidget        *widget);
43 static void            gtk_plug_show                  (GtkWidget        *widget);
44 static void            gtk_plug_hide                  (GtkWidget        *widget);
45 static void            gtk_plug_map                   (GtkWidget        *widget);
46 static void            gtk_plug_unmap                 (GtkWidget        *widget);
47 static void            gtk_plug_size_allocate         (GtkWidget        *widget,
48                                                        GtkAllocation    *allocation);
49 static gboolean        gtk_plug_key_press_event       (GtkWidget        *widget,
50                                                        GdkEventKey      *event);
51 static gboolean        gtk_plug_focus_event           (GtkWidget        *widget,
52                                                        GdkEventFocus    *event);
53 static void            gtk_plug_set_focus             (GtkWindow        *window,
54                                                        GtkWidget        *focus);
55 static gboolean        gtk_plug_focus                 (GtkWidget        *widget,
56                                                        GtkDirectionType  direction);
57 static void            gtk_plug_check_resize          (GtkContainer     *container);
58 static void            gtk_plug_keys_changed          (GtkWindow        *window);
59 static GdkFilterReturn gtk_plug_filter_func           (GdkXEvent        *gdk_xevent,
60                                                        GdkEvent         *event,
61                                                        gpointer          data);
62
63 static void handle_modality_off        (GtkPlug       *plug);
64 static void send_xembed_message        (GtkPlug       *plug,
65                                         glong          message,
66                                         glong          detail,
67                                         glong          data1,
68                                         glong          data2,
69                                         guint32        time);
70 static void xembed_set_info            (GdkWindow     *window,
71                                         unsigned long  flags);
72
73 /* From Tk */
74 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
75   
76 static GtkWindowClass *parent_class = NULL;
77 static GtkBinClass *bin_class = NULL;
78
79 enum {
80   EMBEDDED,
81   LAST_SIGNAL
82 }; 
83
84 static guint plug_signals[LAST_SIGNAL] = { 0 };
85
86 GtkType
87 gtk_plug_get_type ()
88 {
89   static GtkType plug_type = 0;
90
91   if (!plug_type)
92     {
93       static const GTypeInfo plug_info =
94       {
95         sizeof (GtkPlugClass),
96         NULL,           /* base_init */
97         NULL,           /* base_finalize */
98         (GClassInitFunc) gtk_plug_class_init,
99         NULL,           /* class_finalize */
100         NULL,           /* class_data */
101         sizeof (GtkPlug),
102         16,             /* n_preallocs */
103         (GInstanceInitFunc) gtk_plug_init,
104       };
105
106       plug_type = g_type_register_static (GTK_TYPE_WINDOW, "GtkPlug", &plug_info, 0);
107     }
108
109   return plug_type;
110 }
111
112 static void
113 gtk_plug_class_init (GtkPlugClass *class)
114 {
115   GObjectClass *gobject_class = (GObjectClass *)class;
116   GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
117   GtkWindowClass *window_class = (GtkWindowClass *)class;
118   GtkContainerClass *container_class = (GtkContainerClass *)class;
119
120   parent_class = gtk_type_class (GTK_TYPE_WINDOW);
121   bin_class = gtk_type_class (GTK_TYPE_BIN);
122
123   gobject_class->finalize = gtk_plug_finalize;
124   
125   widget_class->realize = gtk_plug_realize;
126   widget_class->unrealize = gtk_plug_unrealize;
127   widget_class->key_press_event = gtk_plug_key_press_event;
128   widget_class->focus_in_event = gtk_plug_focus_event;
129   widget_class->focus_out_event = gtk_plug_focus_event;
130
131   widget_class->show = gtk_plug_show;
132   widget_class->hide = gtk_plug_hide;
133   widget_class->map = gtk_plug_map;
134   widget_class->unmap = gtk_plug_unmap;
135   widget_class->size_allocate = gtk_plug_size_allocate;
136
137   widget_class->focus = gtk_plug_focus;
138
139   container_class->check_resize = gtk_plug_check_resize;
140
141   window_class->set_focus = gtk_plug_set_focus;
142   window_class->keys_changed = gtk_plug_keys_changed;
143
144   plug_signals[EMBEDDED] =
145     g_signal_new ("embedded",
146                   G_OBJECT_CLASS_TYPE (class),
147                   G_SIGNAL_RUN_LAST,
148                   G_STRUCT_OFFSET (GtkPlugClass, embedded),
149                   NULL, NULL,
150                   _gtk_marshal_VOID__VOID,
151                   GTK_TYPE_NONE, 0);
152 }
153
154 static void
155 gtk_plug_init (GtkPlug *plug)
156 {
157   GtkWindow *window;
158
159   window = GTK_WINDOW (plug);
160
161   window->type = GTK_WINDOW_TOPLEVEL;
162 }
163
164 static void
165 gtk_plug_set_is_child (GtkPlug  *plug,
166                        gboolean  is_child)
167 {
168   g_assert (!GTK_WIDGET (plug)->parent);
169       
170   if (is_child)
171     {
172       if (plug->modality_window)
173         handle_modality_off (plug);
174
175       if (plug->modality_group)
176         {
177           gtk_window_group_remove_window (plug->modality_group, GTK_WINDOW (plug));
178           g_object_unref (plug->modality_group);
179           plug->modality_group = NULL;
180         }
181       
182       /* As a toplevel, the MAPPED flag doesn't correspond
183        * to whether the widget->window is mapped; we unmap
184        * here, but don't bother remapping -- we will get mapped
185        * by gtk_widget_set_parent ().
186        */
187       if (GTK_WIDGET_MAPPED (plug))
188         gtk_widget_unmap (GTK_WIDGET (plug));
189       
190       GTK_WIDGET_UNSET_FLAGS (plug, GTK_TOPLEVEL);
191       gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_PARENT);
192
193       _gtk_widget_propagate_hierarchy_changed (GTK_WIDGET (plug), GTK_WIDGET (plug));
194     }
195   else
196     {
197       if (GTK_WINDOW (plug)->focus_widget)
198         gtk_window_set_focus (GTK_WINDOW (plug), NULL);
199       if (GTK_WINDOW (plug)->default_widget)
200         gtk_window_set_default (GTK_WINDOW (plug), NULL);
201           
202       plug->modality_group = gtk_window_group_new ();
203       gtk_window_group_add_window (plug->modality_group, GTK_WINDOW (plug));
204       
205       GTK_WIDGET_SET_FLAGS (plug, GTK_TOPLEVEL);
206       gtk_container_set_resize_mode (GTK_CONTAINER (plug), GTK_RESIZE_QUEUE);
207
208       _gtk_widget_propagate_hierarchy_changed (GTK_WIDGET (plug), NULL);
209     }
210 }
211
212 /**
213  * _gtk_plug_add_to_socket:
214  * @plug: a #GtkPlug
215  * @socket: a #GtkSocket
216  * 
217  * Adds a plug to a socket within the same application.
218  **/
219 void
220 _gtk_plug_add_to_socket (GtkPlug   *plug,
221                          GtkSocket *socket)
222 {
223   GtkWidget *widget;
224   gint w, h;
225   
226   g_return_if_fail (GTK_IS_PLUG (plug));
227   g_return_if_fail (GTK_IS_SOCKET (socket));
228   g_return_if_fail (GTK_WIDGET_REALIZED (socket));
229
230   widget = GTK_WIDGET (plug);
231
232   gtk_plug_set_is_child (plug, TRUE);
233   plug->same_app = TRUE;
234   socket->same_app = TRUE;
235   socket->plug_widget = widget;
236
237   plug->socket_window = GTK_WIDGET (socket)->window;
238
239   if (GTK_WIDGET_REALIZED (widget))
240     {
241       gdk_drawable_get_size (GDK_DRAWABLE (widget->window), &w, &h);
242       gdk_window_reparent (widget->window, plug->socket_window, -w, -h);
243     }
244
245   gtk_widget_set_parent (widget, GTK_WIDGET (socket));
246
247   g_signal_emit_by_name (G_OBJECT (socket), "plug_added", 0);
248 }
249
250 /**
251  * _gtk_plug_remove_from_socket:
252  * @plug: a #GtkPlug
253  * @socket: a #GtkSocket
254  * 
255  * Removes a plug from a socket within the same application.
256  **/
257 void
258 _gtk_plug_remove_from_socket (GtkPlug   *plug,
259                               GtkSocket *socket)
260 {
261   GtkWidget *widget;
262   GdkEvent event;
263   gboolean result;
264   gboolean widget_was_visible;
265
266   g_return_if_fail (GTK_IS_PLUG (plug));
267   g_return_if_fail (GTK_IS_SOCKET (socket));
268   g_return_if_fail (GTK_WIDGET_REALIZED (plug));
269
270   widget = GTK_WIDGET (plug);
271
272   g_object_ref (plug);
273   g_object_ref (socket);
274
275   widget_was_visible = GTK_WIDGET_VISIBLE (plug);
276   
277   gdk_window_hide (widget->window);
278   gdk_window_reparent (widget->window,
279                        gtk_widget_get_root_window (widget),
280                        0, 0);
281
282   GTK_PRIVATE_SET_FLAG (plug, GTK_IN_REPARENT);
283   gtk_widget_unparent (GTK_WIDGET (plug));
284   GTK_PRIVATE_UNSET_FLAG (plug, GTK_IN_REPARENT);
285   
286   socket->plug_widget = NULL;
287   g_object_unref (socket->plug_window);
288   socket->plug_window = NULL;
289   
290   socket->same_app = FALSE;
291
292   plug->same_app = FALSE;
293   plug->socket_window = NULL;
294
295   gtk_plug_set_is_child (plug, FALSE);
296                     
297   g_signal_emit_by_name (G_OBJECT (socket), "plug_removed", &result);
298   if (!result)
299     gtk_widget_destroy (GTK_WIDGET (socket));
300
301   event.any.type = GDK_DELETE;
302   event.any.window = g_object_ref (widget->window);
303   event.any.send_event = FALSE;
304   
305   if (!gtk_widget_event (widget, &event))
306     gtk_widget_destroy (widget);
307   
308   g_object_unref (event.any.window);
309   g_object_unref (plug);
310
311   if (widget_was_visible && GTK_WIDGET_VISIBLE (socket))
312     gtk_widget_queue_resize (GTK_WIDGET (socket));
313
314   g_object_unref (socket);
315 }
316
317 /**
318  * gtk_plug_construct:
319  * @plug: a #GtkPlug.
320  * @socket_id: the XID of the socket's window.
321  *
322  * Finish the initialization of @plug for a given #GtkSocket identified by
323  * @socket_id. This function will generally only be used by classes deriving from #GtkPlug.
324  **/
325 void
326 gtk_plug_construct (GtkPlug         *plug,
327                     GdkNativeWindow  socket_id)
328 {
329   gtk_plug_construct_for_display (plug, gdk_get_default_display (), socket_id);
330 }
331
332 /**
333  * gtk_plug_construct_for_display:
334  * @plug: a #GtkPlug.
335  * @display: the #GdkDisplay associated with @socket_id's 
336  *           #GtkSocket.
337  * @socket_id: the XID of the socket's window.
338  *
339  * Finish the initialization of @plug for a given #GtkSocket identified by
340  * @socket_id which is currently displayed on @display.
341  * This function will generally only be used by classes deriving from #GtkPlug.
342  **/
343 void
344 gtk_plug_construct_for_display (GtkPlug         *plug,
345                                 GdkDisplay      *display,
346                                 GdkNativeWindow  socket_id)
347 {
348   if (socket_id)
349     {
350       gpointer user_data = NULL;
351
352       plug->socket_window = gdk_window_lookup_for_display (display, socket_id);
353       
354       if (plug->socket_window)
355         gdk_window_get_user_data (plug->socket_window, &user_data);
356       else
357         plug->socket_window = gdk_window_foreign_new_for_display (display, socket_id);
358           
359       if (user_data)
360         {
361           if (GTK_IS_SOCKET (user_data))
362             _gtk_plug_add_to_socket (plug, user_data);
363           else
364             {
365               g_warning (G_STRLOC "Can't create GtkPlug as child of non-GtkSocket");
366               plug->socket_window = NULL;
367             }
368         }
369
370       if (plug->socket_window)
371         g_signal_emit (G_OBJECT (plug), plug_signals[EMBEDDED], 0);
372     }
373 }
374
375 /**
376  * gtk_plug_new:
377  * @socket_id:  the window ID of the socket, or 0.
378  * 
379  * Creates a new plug widget inside the #GtkSocket identified
380  * by @socket_id. If @socket_id is 0, the plug is left "unplugged" and
381  * can later be plugged into a #GtkSocket by  gtk_socket_add_id().
382  * 
383  * Return value: the new #GtkPlug widget.
384  **/
385 GtkWidget*
386 gtk_plug_new (GdkNativeWindow socket_id)
387 {
388   return gtk_plug_new_for_display (gdk_get_default_display (), socket_id);
389 }
390
391 /**
392  * gtk_plug_new_for_display:
393  * @display : the #GdkDisplay on which @socket_id is displayed
394  * @socket_id: the XID of the socket's window.
395  * 
396  * Create a new plug widget inside the #GtkSocket identified by socket_id.
397  *
398  * Return value: the new #GtkPlug widget.
399  */
400 GtkWidget*
401 gtk_plug_new_for_display (GdkDisplay      *display,
402                           GdkNativeWindow  socket_id)
403 {
404   GtkPlug *plug;
405
406   plug = GTK_PLUG (gtk_type_new (GTK_TYPE_PLUG));
407   gtk_plug_construct_for_display (plug, display, socket_id);
408   return GTK_WIDGET (plug);
409 }
410
411 /**
412  * gtk_plug_get_id:
413  * @plug: a #GtkPlug.
414  * 
415  * Gets the window ID of a #GtkPlug widget, which can then
416  * be used to embed this window inside another window, for
417  * instance with gtk_socket_add_id().
418  * 
419  * Return value: the window ID for the plug
420  **/
421 GdkNativeWindow
422 gtk_plug_get_id (GtkPlug *plug)
423 {
424   g_return_val_if_fail (GTK_IS_PLUG (plug), 0);
425
426   if (!GTK_WIDGET_REALIZED (plug))
427     gtk_widget_realize (GTK_WIDGET (plug));
428
429   return GDK_WINDOW_XWINDOW (GTK_WIDGET (plug)->window);
430 }
431
432 static void
433 gtk_plug_finalize (GObject *object)
434 {
435   GtkPlug *plug = GTK_PLUG (object);
436
437   if (plug->grabbed_keys)
438     {
439       g_hash_table_destroy (plug->grabbed_keys);
440       plug->grabbed_keys = NULL;
441     }
442   
443   G_OBJECT_CLASS (parent_class)->finalize (object);
444 }
445
446 static void
447 gtk_plug_unrealize (GtkWidget *widget)
448 {
449   GtkPlug *plug;
450
451   g_return_if_fail (GTK_IS_PLUG (widget));
452
453   plug = GTK_PLUG (widget);
454
455   if (plug->socket_window != NULL)
456     {
457       gdk_window_set_user_data (plug->socket_window, NULL);
458       gdk_window_unref (plug->socket_window);
459       plug->socket_window = NULL;
460     }
461
462   if (!plug->same_app)
463     {
464       if (plug->modality_window)
465         handle_modality_off (plug);
466
467       gtk_window_group_remove_window (plug->modality_group, GTK_WINDOW (plug));
468       g_object_unref (plug->modality_group);
469     }
470   
471   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
472     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
473 }
474
475 static void
476 gtk_plug_realize (GtkWidget *widget)
477 {
478   GtkWindow *window;
479   GtkPlug *plug;
480   GdkWindowAttr attributes;
481   gint attributes_mask;
482
483   g_return_if_fail (GTK_IS_PLUG (widget));
484
485   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
486   window = GTK_WINDOW (widget);
487   plug = GTK_PLUG (widget);
488
489   attributes.window_type = GDK_WINDOW_CHILD;    /* XXX GDK_WINDOW_PLUG ? */
490   attributes.title = window->title;
491   attributes.wmclass_name = window->wmclass_name;
492   attributes.wmclass_class = window->wmclass_class;
493   attributes.width = widget->allocation.width;
494   attributes.height = widget->allocation.height;
495   attributes.wclass = GDK_INPUT_OUTPUT;
496
497   /* this isn't right - we should match our parent's visual/colormap.
498    * though that will require handling "foreign" colormaps */
499   attributes.visual = gtk_widget_get_visual (widget);
500   attributes.colormap = gtk_widget_get_colormap (widget);
501   attributes.event_mask = gtk_widget_get_events (widget);
502   attributes.event_mask |= (GDK_EXPOSURE_MASK |
503                             GDK_KEY_PRESS_MASK |
504                             GDK_KEY_RELEASE_MASK |
505                             GDK_ENTER_NOTIFY_MASK |
506                             GDK_LEAVE_NOTIFY_MASK |
507                             GDK_STRUCTURE_MASK);
508
509   attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP;
510   attributes_mask |= (window->title ? GDK_WA_TITLE : 0);
511   attributes_mask |= (window->wmclass_name ? GDK_WA_WMCLASS : 0);
512
513   if (GTK_WIDGET_TOPLEVEL (widget))
514     {
515       attributes.window_type = GDK_WINDOW_TOPLEVEL;
516
517       gdk_error_trap_push ();
518       if (plug->socket_window)
519         widget->window = gdk_window_new (plug->socket_window, 
520                                          &attributes, attributes_mask);
521       else /* If it's a passive plug, we use the root window */
522         widget->window = gdk_window_new (gtk_widget_get_root_window (widget),
523                                          &attributes, attributes_mask);
524
525       gdk_display_sync (gtk_widget_get_display (widget));
526       if (gdk_error_trap_pop ()) /* Uh-oh */
527         {
528           gdk_error_trap_push ();
529           gdk_window_destroy (widget->window);
530           gdk_flush ();
531           gdk_error_trap_pop ();
532           widget->window = gdk_window_new (gtk_widget_get_root_window (widget),
533                                            &attributes, attributes_mask);
534         }
535       
536       gdk_window_add_filter (widget->window, gtk_plug_filter_func, widget);
537
538       plug->modality_group = gtk_window_group_new ();
539       gtk_window_group_add_window (plug->modality_group, window);
540       
541       xembed_set_info (widget->window, 0);
542     }
543   else
544     widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), 
545                                      &attributes, attributes_mask);      
546   
547   gdk_window_set_user_data (widget->window, window);
548
549   widget->style = gtk_style_attach (widget->style, widget->window);
550   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
551 }
552
553 static void
554 gtk_plug_show (GtkWidget *widget)
555 {
556   if (GTK_WIDGET_TOPLEVEL (widget))
557     GTK_WIDGET_CLASS (parent_class)->show (widget);
558   else
559     GTK_WIDGET_CLASS (bin_class)->show (widget);
560 }
561
562 static void
563 gtk_plug_hide (GtkWidget *widget)
564 {
565   if (GTK_WIDGET_TOPLEVEL (widget))
566     GTK_WIDGET_CLASS (parent_class)->hide (widget);
567   else
568     GTK_WIDGET_CLASS (bin_class)->hide (widget);
569 }
570
571 /* From gdkinternals.h */
572 void gdk_synthesize_window_state (GdkWindow     *window,
573                                   GdkWindowState unset_flags,
574                                   GdkWindowState set_flags);
575
576 static void
577 gtk_plug_map (GtkWidget *widget)
578 {
579   if (GTK_WIDGET_TOPLEVEL (widget))
580     {
581       GtkBin *bin = GTK_BIN (widget);
582       
583       GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
584
585       if (bin->child &&
586           GTK_WIDGET_VISIBLE (bin->child) &&
587           !GTK_WIDGET_MAPPED (bin->child))
588         gtk_widget_map (bin->child);
589
590       xembed_set_info (widget->window, XEMBED_MAPPED);
591       
592       gdk_synthesize_window_state (widget->window,
593                                    GDK_WINDOW_STATE_WITHDRAWN,
594                                    0);
595     }
596   else
597     GTK_WIDGET_CLASS (bin_class)->map (widget);
598 }
599
600 static void
601 gtk_plug_unmap (GtkWidget *widget)
602 {
603   if (GTK_WIDGET_TOPLEVEL (widget))
604     {
605       GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
606
607       gdk_window_hide (widget->window);
608       xembed_set_info (widget->window, 0);
609       
610       gdk_synthesize_window_state (widget->window,
611                                    0,
612                                    GDK_WINDOW_STATE_WITHDRAWN);
613     }
614   else
615     GTK_WIDGET_CLASS (bin_class)->unmap (widget);
616 }
617
618 static void
619 gtk_plug_size_allocate (GtkWidget     *widget,
620                         GtkAllocation *allocation)
621 {
622   if (GTK_WIDGET_TOPLEVEL (widget))
623     GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
624   else
625     {
626       GtkBin *bin = GTK_BIN (widget);
627
628       widget->allocation = *allocation;
629
630       if (GTK_WIDGET_REALIZED (widget))
631         gdk_window_move_resize (widget->window,
632                                 allocation->x, allocation->y,
633                                 allocation->width, allocation->height);
634
635       if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
636         {
637           GtkAllocation child_allocation;
638           
639           child_allocation.x = child_allocation.y = GTK_CONTAINER (widget)->border_width;
640           child_allocation.width =
641             MAX (1, (gint)allocation->width - child_allocation.x * 2);
642           child_allocation.height =
643             MAX (1, (gint)allocation->height - child_allocation.y * 2);
644           
645           gtk_widget_size_allocate (bin->child, &child_allocation);
646         }
647       
648     }
649 }
650
651 static gboolean
652 gtk_plug_key_press_event (GtkWidget   *widget,
653                           GdkEventKey *event)
654 {
655   if (GTK_WIDGET_TOPLEVEL (widget))
656     return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
657   else
658     return FALSE;
659 }
660
661 static gboolean
662 gtk_plug_focus_event (GtkWidget      *widget,
663                       GdkEventFocus  *event)
664 {
665   /* We eat focus-in events and focus-out events, since they
666    * can be generated by something like a keyboard grab on
667    * a child of the plug.
668    */
669   return FALSE;
670 }
671
672 static void
673 gtk_plug_set_focus (GtkWindow *window,
674                     GtkWidget *focus)
675 {
676   GtkPlug *plug = GTK_PLUG (window);
677
678   GTK_WINDOW_CLASS (parent_class)->set_focus (window, focus);
679   
680   /* Ask for focus from embedder
681    */
682
683   if (focus && !window->has_toplevel_focus)
684     {
685       send_xembed_message (plug, XEMBED_REQUEST_FOCUS, 0, 0, 0,
686                            gtk_get_current_event_time ());
687     }
688 }
689
690 typedef struct
691 {
692   guint                  accelerator_key;
693   GdkModifierType        accelerator_mods;
694 } GrabbedKey;
695
696 static guint
697 grabbed_key_hash (gconstpointer a)
698 {
699   const GrabbedKey *key = a;
700   guint h;
701   
702   h = key->accelerator_key << 16;
703   h ^= key->accelerator_key >> 16;
704   h ^= key->accelerator_mods;
705
706   return h;
707 }
708
709 static gboolean
710 grabbed_key_equal (gconstpointer a, gconstpointer b)
711 {
712   const GrabbedKey *keya = a;
713   const GrabbedKey *keyb = b;
714
715   return (keya->accelerator_key == keyb->accelerator_key &&
716           keya->accelerator_mods == keyb->accelerator_mods);
717 }
718
719 static void
720 add_grabbed_key (gpointer key, gpointer val, gpointer data)
721 {
722   GrabbedKey *grabbed_key = key;
723   GtkPlug *plug = data;
724
725   if (!plug->grabbed_keys ||
726       !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
727     {
728       send_xembed_message (plug, XEMBED_GTK_GRAB_KEY, 0, 
729                            grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
730                            gtk_get_current_event_time ());
731     }
732 }
733
734 static void
735 add_grabbed_key_always (gpointer key, gpointer val, gpointer data)
736 {
737   GrabbedKey *grabbed_key = key;
738   GtkPlug *plug = data;
739
740   send_xembed_message (plug, XEMBED_GTK_GRAB_KEY, 0, 
741                        grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
742                        gtk_get_current_event_time ());
743 }
744
745 static void
746 remove_grabbed_key (gpointer key, gpointer val, gpointer data)
747 {
748   GrabbedKey *grabbed_key = key;
749   GtkPlug *plug = data;
750
751   if (!plug->grabbed_keys ||
752       !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
753     {
754       send_xembed_message (plug, XEMBED_GTK_UNGRAB_KEY, 0, 
755                            grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
756                            gtk_get_current_event_time ());
757     }
758 }
759
760 static void
761 keys_foreach (GtkWindow      *window,
762               guint           keyval,
763               GdkModifierType modifiers,
764               gboolean        is_mnemonic,
765               gpointer        data)
766 {
767   GHashTable *new_grabbed_keys = data;
768   GrabbedKey *key = g_new (GrabbedKey, 1);
769
770   key->accelerator_key = keyval;
771   key->accelerator_mods = modifiers;
772   
773   g_hash_table_replace (new_grabbed_keys, key, key);
774 }
775
776 static void
777 gtk_plug_keys_changed (GtkWindow *window)
778 {
779   GHashTable *new_grabbed_keys, *old_grabbed_keys;
780   GtkPlug *plug = GTK_PLUG (window);
781
782   new_grabbed_keys = g_hash_table_new_full (grabbed_key_hash, grabbed_key_equal, (GDestroyNotify)g_free, NULL);
783   _gtk_window_keys_foreach (window, keys_foreach, new_grabbed_keys);
784
785   if (plug->socket_window)
786     g_hash_table_foreach (new_grabbed_keys, add_grabbed_key, plug);
787
788   old_grabbed_keys = plug->grabbed_keys;
789   plug->grabbed_keys = new_grabbed_keys;
790
791   if (old_grabbed_keys)
792     {
793       if (plug->socket_window)
794         g_hash_table_foreach (old_grabbed_keys, remove_grabbed_key, plug);
795       g_hash_table_destroy (old_grabbed_keys);
796     }
797 }
798
799 static gboolean
800 gtk_plug_focus (GtkWidget        *widget,
801                 GtkDirectionType  direction)
802 {
803   GtkBin *bin = GTK_BIN (widget);
804   GtkPlug *plug = GTK_PLUG (widget);
805   GtkWindow *window = GTK_WINDOW (widget);
806   GtkContainer *container = GTK_CONTAINER (widget);
807   GtkWidget *old_focus_child = container->focus_child;
808   GtkWidget *parent;
809   
810   /* We override GtkWindow's behavior, since we don't want wrapping here.
811    */
812   if (old_focus_child)
813     {
814       if (gtk_widget_child_focus (old_focus_child, direction))
815         return TRUE;
816
817       if (window->focus_widget)
818         {
819           /* Wrapped off the end, clear the focus setting for the toplevel */
820           parent = window->focus_widget->parent;
821           while (parent)
822             {
823               gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
824               parent = GTK_WIDGET (parent)->parent;
825             }
826           
827           gtk_window_set_focus (GTK_WINDOW (container), NULL);
828
829           if (!GTK_CONTAINER (window)->focus_child)
830             {
831               gint message = -1;
832
833               switch (direction)
834                 {
835                 case GTK_DIR_UP:
836                 case GTK_DIR_LEFT:
837                 case GTK_DIR_TAB_BACKWARD:
838                   message = XEMBED_FOCUS_PREV;
839                   break;
840                 case GTK_DIR_DOWN:
841                 case GTK_DIR_RIGHT:
842                 case GTK_DIR_TAB_FORWARD:
843                   message = XEMBED_FOCUS_NEXT;
844                   break;
845                 }
846               
847               send_xembed_message (plug, message, 0, 0, 0,
848                                    gtk_get_current_event_time ());
849             }
850         }
851
852       return FALSE;
853     }
854   else
855     {
856       /* Try to focus the first widget in the window */
857       
858       if (bin->child && gtk_widget_child_focus (bin->child, direction))
859         return TRUE;
860     }
861
862   return FALSE;
863 }
864
865 static void
866 gtk_plug_check_resize (GtkContainer *container)
867 {
868   if (GTK_WIDGET_TOPLEVEL (container))
869     GTK_CONTAINER_CLASS (parent_class)->check_resize (container);
870   else
871     GTK_CONTAINER_CLASS (bin_class)->check_resize (container);
872 }
873
874 static void
875 send_xembed_message (GtkPlug *plug,
876                      glong      message,
877                      glong      detail,
878                      glong      data1,
879                      glong      data2,
880                      guint32    time)
881 {
882   if (plug->socket_window)
883     {
884       GdkDisplay *display = gdk_drawable_get_display (plug->socket_window);
885       XEvent xevent;
886
887       GTK_NOTE(PLUGSOCKET,
888                g_message ("GtkPlug: Sending XEMBED message of type %ld", message));
889
890       xevent.xclient.window = GDK_WINDOW_XWINDOW (plug->socket_window);
891       xevent.xclient.type = ClientMessage;
892       xevent.xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED");
893       xevent.xclient.format = 32;
894       xevent.xclient.data.l[0] = time;
895       xevent.xclient.data.l[1] = message;
896       xevent.xclient.data.l[2] = detail;
897       xevent.xclient.data.l[3] = data1;
898       xevent.xclient.data.l[4] = data2;
899
900       gdk_error_trap_push ();
901       XSendEvent (GDK_WINDOW_XDISPLAY(plug->socket_window),
902                   GDK_WINDOW_XWINDOW (plug->socket_window),
903                   False, NoEventMask, &xevent);
904       gdk_display_sync (display);
905       gdk_error_trap_pop ();
906     }
907 }
908
909 static void
910 focus_first_last (GtkPlug          *plug,
911                   GtkDirectionType  direction)
912 {
913   GtkWindow *window = GTK_WINDOW (plug);
914   GtkWidget *parent;
915   
916   if (window->focus_widget)
917     {
918       parent = window->focus_widget->parent;
919       while (parent)
920         {
921           gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
922           parent = GTK_WIDGET (parent)->parent;
923         }
924       
925       gtk_window_set_focus (GTK_WINDOW (plug), NULL);
926     }
927
928   gtk_widget_child_focus (GTK_WIDGET (plug), direction);
929 }
930
931 static void
932 handle_modality_on (GtkPlug *plug)
933 {
934   if (!plug->modality_window)
935     {
936       plug->modality_window = gtk_window_new (GTK_WINDOW_POPUP);
937       gtk_window_set_screen (GTK_WINDOW (plug->modality_window),
938                              gtk_widget_get_screen (GTK_WIDGET (plug)));
939       gtk_widget_realize (plug->modality_window);
940       gtk_window_group_add_window (plug->modality_group, GTK_WINDOW (plug->modality_window));
941       gtk_grab_add (plug->modality_window);
942     }
943 }
944
945 static void
946 handle_modality_off (GtkPlug *plug)
947 {
948   if (plug->modality_window)
949     {
950       gtk_widget_destroy (plug->modality_window);
951       plug->modality_window = NULL;
952     }
953 }
954
955 static void
956 xembed_set_info (GdkWindow     *window,
957                  unsigned long  flags)
958 {
959   GdkDisplay *display = gdk_drawable_get_display (window);
960   unsigned long buffer[2];
961
962   Atom xembed_info_atom = gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED_INFO");
963
964   buffer[1] = 0;                /* Protocol version */
965   buffer[1] = flags;
966
967   XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
968                    GDK_WINDOW_XWINDOW (window),
969                    xembed_info_atom, xembed_info_atom, 32,
970                    PropModeReplace,
971                    (unsigned char *)buffer, 2);
972 }
973
974 static void
975 handle_xembed_message (GtkPlug   *plug,
976                        glong      message,
977                        glong      detail,
978                        glong      data1,
979                        glong      data2,
980                        guint32    time)
981 {
982   GtkWindow *window = GTK_WINDOW (plug);
983   
984   GTK_NOTE (PLUGSOCKET,
985             g_message ("GtkPlug: Message of type %ld received", message));
986   
987   switch (message)
988     {
989     case XEMBED_EMBEDDED_NOTIFY:
990       break;
991     case XEMBED_WINDOW_ACTIVATE:
992       _gtk_window_set_is_active (window, TRUE);
993       break;
994     case XEMBED_WINDOW_DEACTIVATE:
995       _gtk_window_set_is_active (window, FALSE);
996       break;
997       
998     case XEMBED_MODALITY_ON:
999       handle_modality_on (plug);
1000       break;
1001     case XEMBED_MODALITY_OFF:
1002       handle_modality_off (plug);
1003       break;
1004
1005     case XEMBED_FOCUS_IN:
1006       _gtk_window_set_has_toplevel_focus (window, TRUE);
1007       switch (detail)
1008         {
1009         case XEMBED_FOCUS_FIRST:
1010           focus_first_last (plug, GTK_DIR_TAB_FORWARD);
1011           break;
1012         case XEMBED_FOCUS_LAST:
1013           focus_first_last (plug, GTK_DIR_TAB_BACKWARD);
1014           break;
1015         case XEMBED_FOCUS_CURRENT:
1016           break;
1017         }
1018       break;
1019
1020     case XEMBED_FOCUS_OUT:
1021       _gtk_window_set_has_toplevel_focus (window, FALSE);
1022       break;
1023       
1024     case XEMBED_GRAB_KEY:
1025     case XEMBED_UNGRAB_KEY:
1026     case XEMBED_GTK_GRAB_KEY:
1027     case XEMBED_GTK_UNGRAB_KEY:
1028     case XEMBED_REQUEST_FOCUS:
1029     case XEMBED_FOCUS_NEXT:
1030     case XEMBED_FOCUS_PREV:
1031       g_warning ("GtkPlug: Invalid _XEMBED message of type %ld received", message);
1032       break;
1033       
1034     default:
1035       GTK_NOTE(PLUGSOCKET,
1036                g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %ld", message));
1037       break;
1038     }
1039 }
1040
1041 static GdkFilterReturn
1042 gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
1043 {
1044   GdkScreen *screen = gdk_drawable_get_screen (event->any.window);
1045   GdkDisplay *display = gdk_screen_get_display (screen);
1046   GtkPlug *plug = GTK_PLUG (data);
1047   XEvent *xevent = (XEvent *)gdk_xevent;
1048
1049   GdkFilterReturn return_val;
1050   
1051   return_val = GDK_FILTER_CONTINUE;
1052
1053   switch (xevent->type)
1054     {
1055     case ClientMessage:
1056       if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "_XEMBED"))
1057         {
1058           handle_xembed_message (plug,
1059                                  xevent->xclient.data.l[1],
1060                                  xevent->xclient.data.l[2],
1061                                  xevent->xclient.data.l[3],
1062                                  xevent->xclient.data.l[4],
1063                                  xevent->xclient.data.l[0]);
1064                                  
1065
1066           return GDK_FILTER_REMOVE;
1067         }
1068       else if (xevent->xclient.message_type == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"))
1069         {
1070           /* We filter these out because we take being reparented back to the
1071            * root window as the reliable end of the embedding protocol
1072            */
1073
1074           return GDK_FILTER_REMOVE;
1075         }
1076       break;
1077     case ReparentNotify:
1078       {
1079         XReparentEvent *xre = &xevent->xreparent;
1080         gboolean was_embedded = plug->socket_window != NULL;
1081
1082         return_val = GDK_FILTER_REMOVE;
1083         
1084         g_object_ref (plug);
1085         
1086         if (was_embedded)
1087           {
1088             /* End of embedding protocol for previous socket */
1089             
1090             /* FIXME: race if we remove from another socket and
1091              * then add to a local window before we get notification
1092              * Probably need check in _gtk_plug_add_to_socket
1093              */
1094             
1095             if (xre->parent != GDK_WINDOW_XWINDOW (plug->socket_window))
1096               {
1097                 GtkWidget *widget = GTK_WIDGET (plug);
1098
1099                 gdk_window_set_user_data (plug->socket_window, NULL);
1100                 gdk_window_unref (plug->socket_window);
1101                 plug->socket_window = NULL;
1102
1103                 /* Emit a delete window, as if the user attempted
1104                  * to close the toplevel. Simple as to how we
1105                  * handle WM_DELETE_WINDOW, if it isn't handled
1106                  * we destroy the widget. BUt only do this if
1107                  * we are being reparented to the root window.
1108                  * Moving from one embedder to another should
1109                  * be invisible to the app.
1110                  */
1111
1112                 if (xre->parent == GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)))
1113                   {
1114                     GdkEvent event;
1115                     
1116                     event.any.type = GDK_DELETE;
1117                     event.any.window = g_object_ref (widget->window);
1118                     event.any.send_event = FALSE;
1119                     
1120                     if (!gtk_widget_event (widget, &event))
1121                       gtk_widget_destroy (widget);
1122                     
1123                     g_object_unref (event.any.window);
1124                   }
1125               }
1126             else
1127               goto done;
1128           }
1129
1130         if (xre->parent != GDK_WINDOW_XWINDOW (gdk_screen_get_root_window (screen)))
1131           {
1132             /* Start of embedding protocol */
1133
1134             plug->socket_window = gdk_window_lookup_for_display (display, xre->parent);
1135             if (plug->socket_window)
1136               {
1137                 gpointer user_data = NULL;
1138                 gdk_window_get_user_data (plug->socket_window, &user_data);
1139
1140                 if (user_data)
1141                   {
1142                     g_warning (G_STRLOC "Plug reparented unexpectedly into window in the same process");
1143                     plug->socket_window = NULL;
1144                     break;
1145                   }
1146
1147                 g_object_ref (plug->socket_window);
1148               }
1149             else
1150               {
1151                 plug->socket_window = gdk_window_foreign_new_for_display (display, xre->parent);
1152                 if (!plug->socket_window) /* Already gone */
1153                   break;
1154               }
1155
1156             if (plug->grabbed_keys)
1157               g_hash_table_foreach (plug->grabbed_keys, add_grabbed_key_always, plug);
1158
1159             if (!was_embedded)
1160               g_signal_emit (G_OBJECT (plug), plug_signals[EMBEDDED], 0);
1161           }
1162
1163       done:
1164         g_object_unref (plug);
1165         
1166         break;
1167       }
1168     }
1169
1170   return GDK_FILTER_CONTINUE;
1171 }