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