]> Pileus Git - ~andy/gtk/blob - gtk/gtkplug.c
geez, don't call g_list funcs on GSList
[~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 "gtkplug.h"
30
31 #include "gdk/gdkkeysyms.h"
32 #include "x11/gdkx.h"
33
34 #include "xembed.h"
35
36 static void            gtk_plug_class_init            (GtkPlugClass     *klass);
37 static void            gtk_plug_init                  (GtkPlug          *plug);
38 static void            gtk_plug_realize               (GtkWidget        *widget);
39 static void            gtk_plug_unrealize             (GtkWidget        *widget);
40 static gboolean        gtk_plug_key_press_event       (GtkWidget        *widget,
41                                                        GdkEventKey      *event);
42 static void            gtk_plug_forward_key_press     (GtkPlug          *plug,
43                                                        GdkEventKey      *event);
44 static void            gtk_plug_set_focus             (GtkWindow        *window,
45                                                        GtkWidget        *focus);
46 static gboolean        gtk_plug_focus                 (GtkWidget        *widget,
47                                                        GtkDirectionType  direction);
48 static void            gtk_plug_accel_entries_changed (GtkWindow        *window);
49 static GdkFilterReturn gtk_plug_filter_func           (GdkXEvent        *gdk_xevent,
50                                                        GdkEvent         *event,
51                                                        gpointer          data);
52
53 static void gtk_plug_free_grabbed_keys (GHashTable *key_table);
54 static void handle_modality_off        (GtkPlug    *plug);
55 static void send_xembed_message        (GtkPlug    *plug,
56                                         glong       message,
57                                         glong       detail,
58                                         glong       data1,
59                                         glong       data2,
60                                         guint32     time);
61
62 /* From Tk */
63 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
64   
65 static GtkWindowClass *parent_class = NULL;
66
67 GtkType
68 gtk_plug_get_type ()
69 {
70   static GtkType plug_type = 0;
71
72   if (!plug_type)
73     {
74       static const GTypeInfo plug_info =
75       {
76         sizeof (GtkPlugClass),
77         NULL,           /* base_init */
78         NULL,           /* base_finalize */
79         (GClassInitFunc) gtk_plug_class_init,
80         NULL,           /* class_finalize */
81         NULL,           /* class_data */
82         sizeof (GtkPlug),
83         16,             /* n_preallocs */
84         (GInstanceInitFunc) gtk_plug_init,
85       };
86
87       plug_type = g_type_register_static (GTK_TYPE_WINDOW, "GtkPlug", &plug_info, 0);
88     }
89
90   return plug_type;
91 }
92
93 static void
94 gtk_plug_class_init (GtkPlugClass *class)
95 {
96   GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
97   GtkWindowClass *window_class = (GtkWindowClass *)class;
98
99   parent_class = gtk_type_class (GTK_TYPE_WINDOW);
100
101   widget_class->realize = gtk_plug_realize;
102   widget_class->unrealize = gtk_plug_unrealize;
103   widget_class->key_press_event = gtk_plug_key_press_event;
104
105  widget_class->focus = gtk_plug_focus;
106
107   window_class->set_focus = gtk_plug_set_focus;
108 #if 0  
109   window_class->accel_entries_changed = gtk_plug_accel_entries_changed;
110 #endif
111 }
112
113 static void
114 gtk_plug_init (GtkPlug *plug)
115 {
116   GtkWindow *window;
117
118   window = GTK_WINDOW (plug);
119
120   window->type = GTK_WINDOW_TOPLEVEL;
121   window->auto_shrink = TRUE;
122
123 #if 0  
124   gtk_window_set_grab_group (window, window);
125 #endif  
126 }
127
128 void
129 gtk_plug_construct (GtkPlug *plug, GdkNativeWindow socket_id)
130 {
131   if (socket_id)
132     {
133       plug->socket_window = gdk_window_lookup (socket_id);
134       plug->same_app = TRUE;
135
136       if (plug->socket_window == NULL)
137         {
138           plug->socket_window = gdk_window_foreign_new (socket_id);
139           plug->same_app = FALSE;
140         }
141     }
142 }
143
144 GtkWidget*
145 gtk_plug_new (GdkNativeWindow socket_id)
146 {
147   GtkPlug *plug;
148
149   plug = GTK_PLUG (gtk_type_new (GTK_TYPE_PLUG));
150   gtk_plug_construct (plug, socket_id);
151   return GTK_WIDGET (plug);
152 }
153
154 static void
155 gtk_plug_unrealize (GtkWidget *widget)
156 {
157   GtkPlug *plug;
158
159   g_return_if_fail (widget != NULL);
160   g_return_if_fail (GTK_IS_PLUG (widget));
161
162   plug = GTK_PLUG (widget);
163
164   if (plug->socket_window != NULL)
165     {
166       gdk_window_set_user_data (plug->socket_window, NULL);
167       gdk_window_unref (plug->socket_window);
168       plug->socket_window = NULL;
169     }
170
171 #if 0  
172   if (plug->modality_window)
173     handle_modality_off (plug);
174 #endif  
175   
176   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
177     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
178 }
179
180 static void
181 gtk_plug_realize (GtkWidget *widget)
182 {
183   GtkWindow *window;
184   GtkPlug *plug;
185   GdkWindowAttr attributes;
186   gint attributes_mask;
187
188   g_return_if_fail (widget != NULL);
189   g_return_if_fail (GTK_IS_PLUG (widget));
190
191   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
192   window = GTK_WINDOW (widget);
193   plug = GTK_PLUG (widget);
194
195   attributes.window_type = GDK_WINDOW_CHILD;    /* XXX GDK_WINDOW_PLUG ? */
196   attributes.title = window->title;
197   attributes.wmclass_name = window->wmclass_name;
198   attributes.wmclass_class = window->wmclass_class;
199   attributes.width = widget->allocation.width;
200   attributes.height = widget->allocation.height;
201   attributes.wclass = GDK_INPUT_OUTPUT;
202
203   /* this isn't right - we should match our parent's visual/colormap.
204    * though that will require handling "foreign" colormaps */
205   attributes.visual = gtk_widget_get_visual (widget);
206   attributes.colormap = gtk_widget_get_colormap (widget);
207   attributes.event_mask = gtk_widget_get_events (widget);
208   attributes.event_mask |= (GDK_EXPOSURE_MASK |
209                             GDK_KEY_PRESS_MASK |
210                             GDK_ENTER_NOTIFY_MASK |
211                             GDK_LEAVE_NOTIFY_MASK |
212                             GDK_FOCUS_CHANGE_MASK |
213                             GDK_STRUCTURE_MASK);
214
215   attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP;
216   attributes_mask |= (window->title ? GDK_WA_TITLE : 0);
217   attributes_mask |= (window->wmclass_name ? GDK_WA_WMCLASS : 0);
218
219   gdk_error_trap_push ();
220   widget->window = gdk_window_new (plug->socket_window, 
221                                    &attributes, attributes_mask);
222   gdk_flush ();
223   if (gdk_error_trap_pop ()) /* Uh-oh */
224     {
225       gdk_error_trap_push ();
226       gdk_window_destroy (widget->window);
227       gdk_flush ();
228       gdk_error_trap_pop ();
229       widget->window = gdk_window_new (NULL, &attributes, attributes_mask);
230     }
231   
232   GDK_WINDOW_TYPE (widget->window) = GDK_WINDOW_TOPLEVEL;
233   gdk_window_set_user_data (widget->window, window);
234
235   widget->style = gtk_style_attach (widget->style, widget->window);
236   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
237
238   gdk_window_add_filter (widget->window, gtk_plug_filter_func, widget);
239 }
240
241 static gboolean
242 gtk_plug_key_press_event (GtkWidget   *widget,
243                           GdkEventKey *event)
244 {
245   if (!GTK_WINDOW (widget)->has_focus)
246     {
247       gtk_plug_forward_key_press (GTK_PLUG (widget), event);
248       return TRUE;
249     }
250   else
251     return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
252 }
253
254 static void
255 gtk_plug_forward_key_press (GtkPlug *plug, GdkEventKey *event)
256 {
257   XEvent xevent;
258   
259   xevent.xkey.type = KeyPress;
260   xevent.xkey.display = GDK_WINDOW_XDISPLAY (GTK_WIDGET(plug)->window);
261   xevent.xkey.window = GDK_WINDOW_XWINDOW (plug->socket_window);
262   xevent.xkey.root = GDK_ROOT_WINDOW (); /* FIXME */
263   xevent.xkey.time = event->time;
264   /* FIXME, the following might cause big problems for
265    * non-GTK apps */
266   xevent.xkey.x = 0;
267   xevent.xkey.y = 0;
268   xevent.xkey.x_root = 0;
269   xevent.xkey.y_root = 0;
270   xevent.xkey.state = event->state;
271   xevent.xkey.keycode =  XKeysymToKeycode(GDK_DISPLAY(), 
272                                           event->keyval);
273   xevent.xkey.same_screen = TRUE; /* FIXME ? */
274
275   gdk_error_trap_push ();
276   XSendEvent (gdk_display,
277               GDK_WINDOW_XWINDOW (plug->socket_window),
278               False, NoEventMask, &xevent);
279   gdk_flush ();
280   gdk_error_trap_pop ();
281 }
282
283 static void
284 gtk_plug_set_focus (GtkWindow *window,
285                     GtkWidget *focus)
286 {
287   GtkPlug *plug = GTK_PLUG (window);
288
289   GTK_WINDOW_CLASS (parent_class)->set_focus (window, focus);
290   
291   /* Ask for focus from embedder
292    */
293
294   if (focus && !window->has_focus)
295     {
296 #if 0      
297       XEvent xevent;
298
299       xevent.xfocus.type = FocusIn;
300       xevent.xfocus.display = GDK_WINDOW_XDISPLAY (GTK_WIDGET(plug)->window);
301       xevent.xfocus.window = GDK_WINDOW_XWINDOW (plug->socket_window);
302       xevent.xfocus.mode = EMBEDDED_APP_WANTS_FOCUS;
303       xevent.xfocus.detail = FALSE; /* Don't force */
304
305       gdk_error_trap_push ();
306       XSendEvent (gdk_display,
307                   GDK_WINDOW_XWINDOW (plug->socket_window),
308                   False, NoEventMask, &xevent);
309       gdk_flush ();
310       gdk_error_trap_pop ();
311 #endif
312
313       send_xembed_message (plug, XEMBED_REQUEST_FOCUS, 0, 0, 0,
314                            gtk_get_current_event_time ());
315     }
316 }
317
318 #if 0
319
320 typedef struct
321 {
322   guint                  accelerator_key;
323   GdkModifierType        accelerator_mods;
324 } GrabbedKey;
325
326 static guint
327 grabbed_key_hash (gconstpointer a)
328 {
329   const GrabbedKey *key = a;
330   guint h;
331   
332   h = key->accelerator_key << 16;
333   h ^= key->accelerator_key >> 16;
334   h ^= key->accelerator_mods;
335
336   return h;
337 }
338
339 static gboolean
340 grabbed_key_equal (gconstpointer a, gconstpointer b)
341 {
342   const GrabbedKey *keya = a;
343   const GrabbedKey *keyb = b;
344
345   return (keya->accelerator_key == keyb->accelerator_key &&
346           keya->accelerator_mods == keyb->accelerator_mods);
347 }
348
349 static void
350 add_grabbed_keys (gpointer key, gpointer val, gpointer data)
351 {
352   GrabbedKey *grabbed_key = key;
353   GtkPlug *plug = data;
354
355   if (!plug->grabbed_keys ||
356       !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
357     {
358       send_xembed_message (plug, XEMBED_GRAB_KEY, 0, 
359                            grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
360                            gtk_get_current_event_time ());
361     }
362 }
363
364 static void
365 remove_grabbed_keys (gpointer key, gpointer val, gpointer data)
366 {
367   GrabbedKey *grabbed_key = key;
368   GtkPlug *plug = data;
369
370   if (!plug->grabbed_keys ||
371       !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
372     {
373       send_xembed_message (plug, XEMBED_UNGRAB_KEY, 0, 
374                            grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
375                            gtk_get_current_event_time ());
376     }
377 }
378
379 static void
380 gtk_plug_free_grabbed_keys (GHashTable *key_table)
381 {
382   g_hash_table_foreach (key_table, (GHFunc)g_free, NULL);
383   g_hash_table_destroy (key_table);
384 }
385
386 static void
387 gtk_plug_accel_entries_changed (GtkWindow *window)
388 {
389   GHashTable *new_grabbed_keys, *old_grabbed_keys;
390   GSList *accel_groups, *tmp_list;
391   GtkPlug *plug = GTK_PLUG (window);
392
393   new_grabbed_keys = g_hash_table_new (grabbed_key_hash, grabbed_key_equal);
394
395   accel_groups = gtk_accel_groups_from_object (GTK_OBJECT (window));
396   
397   tmp_list = accel_groups;
398
399   while (tmp_list)
400     {
401       GtkAccelGroup *accel_group = tmp_list->data;
402       gint i, n_entries;
403       GtkAccelEntry *entries;
404
405       gtk_accel_group_get_entries (accel_group, &entries, &n_entries);
406
407       for (i = 0; i < n_entries; i++)
408         {
409           GdkKeymapKey *keys;
410           gint n_keys;
411           
412           if (gdk_keymap_get_entries_for_keyval (NULL, entries[i].accelerator_key, &keys, &n_keys))
413             {
414               GrabbedKey *key = g_new (GrabbedKey, 1);
415               
416               key->accelerator_key = keys[0].keycode;
417               key->accelerator_mods = entries[i].accelerator_mods;
418               
419               g_hash_table_insert (new_grabbed_keys, key, key);
420
421               g_free (keys);
422             }
423         }
424       
425       tmp_list = tmp_list->next;
426     }
427
428   g_hash_table_foreach (new_grabbed_keys, add_grabbed_keys, plug);
429
430   old_grabbed_keys = plug->grabbed_keys;
431   plug->grabbed_keys = new_grabbed_keys;
432
433   if (old_grabbed_keys)
434     {
435       g_hash_table_foreach (old_grabbed_keys, remove_grabbed_keys, plug);
436       gtk_plug_free_grabbed_keys (old_grabbed_keys);
437     }
438
439 }
440 #endif
441
442 static gboolean
443 gtk_plug_focus (GtkWidget        *widget,
444                 GtkDirectionType  direction)
445 {
446   GtkBin *bin = GTK_BIN (widget);
447   GtkPlug *plug = GTK_PLUG (widget);
448   GtkWindow *window = GTK_WINDOW (widget);
449   GtkContainer *container = GTK_CONTAINER (widget);
450   GtkWidget *old_focus_child = container->focus_child;
451   GtkWidget *parent;
452   
453   /* We override GtkWindow's behavior, since we don't want wrapping here.
454    */
455   if (old_focus_child)
456     {
457       if (gtk_widget_child_focus (old_focus_child, direction))
458         return TRUE;
459
460       if (window->focus_widget)
461         {
462           /* Wrapped off the end, clear the focus setting for the toplevel */
463           parent = window->focus_widget->parent;
464           while (parent)
465             {
466               gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
467               parent = GTK_WIDGET (parent)->parent;
468             }
469           
470           gtk_window_set_focus (GTK_WINDOW (container), NULL);
471
472           if (!GTK_CONTAINER (window)->focus_child)
473             {
474               gint message = -1;
475
476               switch (direction)
477                 {
478                 case GTK_DIR_UP:
479                 case GTK_DIR_LEFT:
480                 case GTK_DIR_TAB_BACKWARD:
481                   message = XEMBED_FOCUS_PREV;
482                   break;
483                 case GTK_DIR_DOWN:
484                 case GTK_DIR_RIGHT:
485                 case GTK_DIR_TAB_FORWARD:
486                   message = XEMBED_FOCUS_NEXT;
487                   break;
488                 }
489               
490               send_xembed_message (plug, message, 0, 0, 0,
491                                    gtk_get_current_event_time ());
492               
493 #if 0         
494               gtk_window_set_focus (GTK_WINDOW (widget), NULL);
495
496               gdk_error_trap_push ();
497               XSetInputFocus (GDK_DISPLAY (),
498                               GDK_WINDOW_XWINDOW (plug->socket_window),
499                               RevertToParent, event->time);
500               gdk_flush ();
501               gdk_error_trap_pop ();
502
503               gtk_plug_forward_key_press (plug, event);
504 #endif        
505             }
506         }
507
508       return FALSE;
509     }
510   else
511     {
512       /* Try to focus the first widget in the window */
513       
514       if (gtk_widget_child_focus (bin->child, direction))
515         return TRUE;
516     }
517
518   return FALSE;
519 }
520
521 static void
522 send_xembed_message (GtkPlug *plug,
523                      glong      message,
524                      glong      detail,
525                      glong      data1,
526                      glong      data2,
527                      guint32    time)
528 {
529   if (plug->socket_window)
530     {
531       XEvent xevent;
532
533       xevent.xclient.window = GDK_WINDOW_XWINDOW (plug->socket_window);
534       xevent.xclient.type = ClientMessage;
535       xevent.xclient.message_type = gdk_atom_intern ("_XEMBED", FALSE);
536       xevent.xclient.format = 32;
537       xevent.xclient.data.l[0] = time;
538       xevent.xclient.data.l[1] = message;
539       xevent.xclient.data.l[2] = detail;
540       xevent.xclient.data.l[3] = data1;
541       xevent.xclient.data.l[4] = data2;
542
543       gdk_error_trap_push ();
544       XSendEvent (gdk_display,
545                   GDK_WINDOW_XWINDOW (plug->socket_window),
546                   False, NoEventMask, &xevent);
547       gdk_flush ();
548       gdk_error_trap_pop ();
549     }
550 }
551
552 static void
553 focus_first_last (GtkPlug          *plug,
554                   GtkDirectionType  direction)
555 {
556   GtkWindow *window = GTK_WINDOW (plug);
557   GtkWidget *parent;
558   
559   if (window->focus_widget)
560     {
561       parent = window->focus_widget->parent;
562       while (parent)
563         {
564           gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
565           parent = GTK_WIDGET (parent)->parent;
566         }
567       
568       gtk_window_set_focus (GTK_WINDOW (plug), NULL);
569     }
570
571   gtk_widget_child_focus (GTK_WIDGET (plug), direction);
572 }
573
574 static void
575 handle_modality_on (GtkPlug *plug)
576 {
577 #if 0
578   if (!plug->modality_window)
579     {
580       plug->modality_window = gtk_window_new (GTK_WINDOW_POPUP);
581       gtk_window_set_grab_group (GTK_WINDOW (plug->modality_window), GTK_WINDOW (plug));
582       gtk_grab_add (plug->modality_window);
583     }
584 #endif  
585 }
586
587 static void
588 handle_modality_off (GtkPlug *plug)
589 {
590 #if 0  
591   if (plug->modality_window)
592     {
593       gtk_grab_remove (plug->modality_window);
594       gtk_widget_destroy (plug->modality_window);
595       plug->modality_window = NULL;
596     }
597 #endif  
598 }
599
600 static void
601 handle_xembed_message (GtkPlug   *plug,
602                        glong      message,
603                        glong      detail,
604                        glong      data1,
605                        glong      data2,
606                        guint32    time)
607 {
608   GTK_NOTE (PLUGSOCKET,
609             g_message ("Message of type %ld received", message));
610   
611   switch (message)
612     {
613     case XEMBED_EMBEDDED_NOTIFY:
614       break;
615     case XEMBED_WINDOW_ACTIVATE:
616       GTK_NOTE(PLUGSOCKET,
617                g_message ("GtkPlug: ACTIVATE received"));
618       break;
619     case XEMBED_WINDOW_DEACTIVATE:
620       GTK_NOTE(PLUGSOCKET,
621                g_message ("GtkPlug: DEACTIVATE received"));
622       break;
623       
624     case XEMBED_MODALITY_ON:
625       handle_modality_on (plug);
626       break;
627     case XEMBED_MODALITY_OFF:
628       handle_modality_off (plug);
629       break;
630
631     case XEMBED_FOCUS_IN:
632       switch (detail)
633         {
634         case XEMBED_FOCUS_FIRST:
635           focus_first_last (plug, GTK_DIR_TAB_FORWARD);
636           break;
637         case XEMBED_FOCUS_LAST:
638           focus_first_last (plug, GTK_DIR_TAB_BACKWARD);
639           break;
640         case XEMBED_FOCUS_CURRENT:
641           /* fall through */;
642         }
643       
644     case XEMBED_FOCUS_OUT:
645       {
646         GdkEvent event;
647
648         event.focus_change.type = GDK_FOCUS_CHANGE;
649         event.focus_change.window = GTK_WIDGET (plug)->window;
650         event.focus_change.send_event = TRUE;
651         event.focus_change.in = (message == XEMBED_FOCUS_IN);
652
653         gtk_widget_event (GTK_WIDGET (plug), &event);
654
655         break;
656       }
657       
658     case XEMBED_REQUEST_FOCUS:
659     case XEMBED_FOCUS_NEXT:
660     case XEMBED_FOCUS_PREV:
661     case XEMBED_GRAB_KEY:
662     case XEMBED_UNGRAB_KEY:
663       g_warning ("GtkPlug: Invalid _XEMBED message of type %ld received", message);
664       break;
665       
666     default:
667       GTK_NOTE(PLUGSOCKET,
668                g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %ld", message));
669       break;
670     }
671 }
672
673 static GdkFilterReturn
674 gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
675 {
676   GtkPlug *plug = GTK_PLUG (data);
677   XEvent *xevent = (XEvent *)gdk_xevent;
678
679   GdkFilterReturn return_val;
680   
681   return_val = GDK_FILTER_CONTINUE;
682
683   switch (xevent->type)
684     {
685     case ClientMessage:
686       if (xevent->xclient.message_type == gdk_atom_intern ("_XEMBED", FALSE))
687         {
688           handle_xembed_message (plug,
689                                  xevent->xclient.data.l[1],
690                                  xevent->xclient.data.l[2],
691                                  xevent->xclient.data.l[3],
692                                  xevent->xclient.data.l[4],
693                                  xevent->xclient.data.l[0]);
694                                  
695
696           return GDK_FILTER_REMOVE;
697         }
698       break;
699     }
700
701   return GDK_FILTER_CONTINUE;
702 }