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