1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
19 /* By Owen Taylor <otaylor@gtk.org> 98/4/4 */
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/.
31 #include "gdk/gdkkeysyms.h"
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,
42 static void gtk_plug_forward_key_press (GtkPlug *plug,
44 static void gtk_plug_set_focus (GtkWindow *window,
46 static gboolean gtk_plug_focus (GtkContainer *container,
47 GtkDirectionType direction);
48 static void gtk_plug_accel_entries_changed (GtkWindow *window);
49 static GdkFilterReturn gtk_plug_filter_func (GdkXEvent *gdk_xevent,
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,
63 #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20
65 static GtkWindowClass *parent_class = NULL;
70 static GtkType plug_type = 0;
74 static const GTypeInfo plug_info =
76 sizeof (GtkPlugClass),
78 NULL, /* base_finalize */
79 (GClassInitFunc) gtk_plug_class_init,
80 NULL, /* class_finalize */
81 NULL, /* class_data */
84 (GInstanceInitFunc) gtk_plug_init,
87 plug_type = g_type_register_static (GTK_TYPE_WINDOW, "GtkPlug", &plug_info, 0);
94 gtk_plug_class_init (GtkPlugClass *class)
96 GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
97 GtkContainerClass *container_class = (GtkContainerClass *)class;
98 GtkWindowClass *window_class = (GtkWindowClass *)class;
100 parent_class = gtk_type_class (GTK_TYPE_WINDOW);
102 widget_class->realize = gtk_plug_realize;
103 widget_class->unrealize = gtk_plug_unrealize;
104 widget_class->key_press_event = gtk_plug_key_press_event;
106 container_class->focus = gtk_plug_focus;
108 window_class->set_focus = gtk_plug_set_focus;
110 window_class->accel_entries_changed = gtk_plug_accel_entries_changed;
115 gtk_plug_init (GtkPlug *plug)
119 window = GTK_WINDOW (plug);
121 window->type = GTK_WINDOW_TOPLEVEL;
122 window->auto_shrink = TRUE;
125 gtk_window_set_grab_group (window, window);
130 gtk_plug_construct (GtkPlug *plug, GdkNativeWindow socket_id)
134 plug->socket_window = gdk_window_lookup (socket_id);
135 plug->same_app = TRUE;
137 if (plug->socket_window == NULL)
139 plug->socket_window = gdk_window_foreign_new (socket_id);
140 plug->same_app = FALSE;
146 gtk_plug_new (GdkNativeWindow socket_id)
150 plug = GTK_PLUG (gtk_type_new (GTK_TYPE_PLUG));
151 gtk_plug_construct (plug, socket_id);
152 return GTK_WIDGET (plug);
156 gtk_plug_unrealize (GtkWidget *widget)
160 g_return_if_fail (widget != NULL);
161 g_return_if_fail (GTK_IS_PLUG (widget));
163 plug = GTK_PLUG (widget);
165 if (plug->socket_window != NULL)
167 gdk_window_set_user_data (plug->socket_window, NULL);
168 gdk_window_unref (plug->socket_window);
169 plug->socket_window = NULL;
173 if (plug->modality_window)
174 handle_modality_off (plug);
177 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
178 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
182 gtk_plug_realize (GtkWidget *widget)
186 GdkWindowAttr attributes;
187 gint attributes_mask;
189 g_return_if_fail (widget != NULL);
190 g_return_if_fail (GTK_IS_PLUG (widget));
192 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
193 window = GTK_WINDOW (widget);
194 plug = GTK_PLUG (widget);
196 attributes.window_type = GDK_WINDOW_CHILD; /* XXX GDK_WINDOW_PLUG ? */
197 attributes.title = window->title;
198 attributes.wmclass_name = window->wmclass_name;
199 attributes.wmclass_class = window->wmclass_class;
200 attributes.width = widget->allocation.width;
201 attributes.height = widget->allocation.height;
202 attributes.wclass = GDK_INPUT_OUTPUT;
204 /* this isn't right - we should match our parent's visual/colormap.
205 * though that will require handling "foreign" colormaps */
206 attributes.visual = gtk_widget_get_visual (widget);
207 attributes.colormap = gtk_widget_get_colormap (widget);
208 attributes.event_mask = gtk_widget_get_events (widget);
209 attributes.event_mask |= (GDK_EXPOSURE_MASK |
211 GDK_ENTER_NOTIFY_MASK |
212 GDK_LEAVE_NOTIFY_MASK |
213 GDK_FOCUS_CHANGE_MASK |
216 attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP;
217 attributes_mask |= (window->title ? GDK_WA_TITLE : 0);
218 attributes_mask |= (window->wmclass_name ? GDK_WA_WMCLASS : 0);
220 gdk_error_trap_push ();
221 widget->window = gdk_window_new (plug->socket_window,
222 &attributes, attributes_mask);
224 if (gdk_error_trap_pop ()) /* Uh-oh */
226 gdk_error_trap_push ();
227 gdk_window_destroy (widget->window);
229 gdk_error_trap_pop ();
230 widget->window = gdk_window_new (NULL, &attributes, attributes_mask);
233 GDK_WINDOW_TYPE (widget->window) = GDK_WINDOW_TOPLEVEL;
234 gdk_window_set_user_data (widget->window, window);
236 widget->style = gtk_style_attach (widget->style, widget->window);
237 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
239 gdk_window_add_filter (widget->window, gtk_plug_filter_func, widget);
243 gtk_plug_key_press_event (GtkWidget *widget,
246 if (!GTK_WINDOW (widget)->has_focus)
248 gtk_plug_forward_key_press (GTK_PLUG (widget), event);
252 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
256 gtk_plug_forward_key_press (GtkPlug *plug, GdkEventKey *event)
260 xevent.xkey.type = KeyPress;
261 xevent.xkey.display = GDK_WINDOW_XDISPLAY (GTK_WIDGET(plug)->window);
262 xevent.xkey.window = GDK_WINDOW_XWINDOW (plug->socket_window);
263 xevent.xkey.root = GDK_ROOT_WINDOW (); /* FIXME */
264 xevent.xkey.time = event->time;
265 /* FIXME, the following might cause big problems for
269 xevent.xkey.x_root = 0;
270 xevent.xkey.y_root = 0;
271 xevent.xkey.state = event->state;
272 xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(),
274 xevent.xkey.same_screen = TRUE; /* FIXME ? */
276 gdk_error_trap_push ();
277 XSendEvent (gdk_display,
278 GDK_WINDOW_XWINDOW (plug->socket_window),
279 False, NoEventMask, &xevent);
281 gdk_error_trap_pop ();
285 gtk_plug_set_focus (GtkWindow *window,
288 GtkPlug *plug = GTK_PLUG (window);
290 GTK_WINDOW_CLASS (parent_class)->set_focus (window, focus);
292 /* Ask for focus from embedder
295 if (focus && !window->has_focus)
300 xevent.xfocus.type = FocusIn;
301 xevent.xfocus.display = GDK_WINDOW_XDISPLAY (GTK_WIDGET(plug)->window);
302 xevent.xfocus.window = GDK_WINDOW_XWINDOW (plug->socket_window);
303 xevent.xfocus.mode = EMBEDDED_APP_WANTS_FOCUS;
304 xevent.xfocus.detail = FALSE; /* Don't force */
306 gdk_error_trap_push ();
307 XSendEvent (gdk_display,
308 GDK_WINDOW_XWINDOW (plug->socket_window),
309 False, NoEventMask, &xevent);
311 gdk_error_trap_pop ();
314 send_xembed_message (plug, XEMBED_REQUEST_FOCUS, 0, 0, 0,
315 gtk_get_current_event_time ());
323 guint accelerator_key;
324 GdkModifierType accelerator_mods;
328 grabbed_key_hash (gconstpointer a)
330 const GrabbedKey *key = a;
333 h = key->accelerator_key << 16;
334 h ^= key->accelerator_key >> 16;
335 h ^= key->accelerator_mods;
341 grabbed_key_equal (gconstpointer a, gconstpointer b)
343 const GrabbedKey *keya = a;
344 const GrabbedKey *keyb = b;
346 return (keya->accelerator_key == keyb->accelerator_key &&
347 keya->accelerator_mods == keyb->accelerator_mods);
351 add_grabbed_keys (gpointer key, gpointer val, gpointer data)
353 GrabbedKey *grabbed_key = key;
354 GtkPlug *plug = data;
356 if (!plug->grabbed_keys ||
357 !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
359 send_xembed_message (plug, XEMBED_GRAB_KEY, 0,
360 grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
361 gtk_get_current_event_time ());
366 remove_grabbed_keys (gpointer key, gpointer val, gpointer data)
368 GrabbedKey *grabbed_key = key;
369 GtkPlug *plug = data;
371 if (!plug->grabbed_keys ||
372 !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
374 send_xembed_message (plug, XEMBED_UNGRAB_KEY, 0,
375 grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
376 gtk_get_current_event_time ());
381 gtk_plug_free_grabbed_keys (GHashTable *key_table)
383 g_hash_table_foreach (key_table, (GHFunc)g_free, NULL);
384 g_hash_table_destroy (key_table);
388 gtk_plug_accel_entries_changed (GtkWindow *window)
390 GHashTable *new_grabbed_keys, *old_grabbed_keys;
391 GSList *accel_groups, *tmp_list;
392 GtkPlug *plug = GTK_PLUG (window);
394 new_grabbed_keys = g_hash_table_new (grabbed_key_hash, grabbed_key_equal);
396 accel_groups = gtk_accel_groups_from_object (GTK_OBJECT (window));
398 tmp_list = accel_groups;
402 GtkAccelGroup *accel_group = tmp_list->data;
404 GtkAccelEntry *entries;
406 gtk_accel_group_get_entries (accel_group, &entries, &n_entries);
408 for (i = 0; i < n_entries; i++)
413 if (gdk_keymap_get_entries_for_keyval (NULL, entries[i].accelerator_key, &keys, &n_keys))
415 GrabbedKey *key = g_new (GrabbedKey, 1);
417 key->accelerator_key = keys[0].keycode;
418 key->accelerator_mods = entries[i].accelerator_mods;
420 g_hash_table_insert (new_grabbed_keys, key, key);
426 tmp_list = tmp_list->next;
429 g_hash_table_foreach (new_grabbed_keys, add_grabbed_keys, plug);
431 old_grabbed_keys = plug->grabbed_keys;
432 plug->grabbed_keys = new_grabbed_keys;
434 if (old_grabbed_keys)
436 g_hash_table_foreach (old_grabbed_keys, remove_grabbed_keys, plug);
437 gtk_plug_free_grabbed_keys (old_grabbed_keys);
444 gtk_plug_focus (GtkContainer *container,
445 GtkDirectionType direction)
447 GtkBin *bin = GTK_BIN (container);
448 GtkPlug *plug = GTK_PLUG (container);
449 GtkWindow *window = GTK_WINDOW (container);
450 GtkWidget *old_focus_child = container->focus_child;
453 /* We override GtkWindow's behavior, since we don't want wrapping here.
457 if (GTK_IS_CONTAINER (old_focus_child) &&
458 GTK_WIDGET_DRAWABLE (old_focus_child) &&
459 GTK_WIDGET_IS_SENSITIVE (old_focus_child) &&
460 gtk_container_focus (GTK_CONTAINER (old_focus_child), direction))
463 if (window->focus_widget)
465 /* Wrapped off the end, clear the focus setting for the toplevel */
466 parent = window->focus_widget->parent;
469 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
470 parent = GTK_WIDGET (parent)->parent;
473 gtk_window_set_focus (GTK_WINDOW (container), NULL);
475 if (!GTK_CONTAINER (window)->focus_child)
483 case GTK_DIR_TAB_BACKWARD:
484 message = XEMBED_FOCUS_PREV;
488 case GTK_DIR_TAB_FORWARD:
489 message = XEMBED_FOCUS_NEXT;
493 send_xembed_message (plug, message, 0, 0, 0,
494 gtk_get_current_event_time ());
497 gtk_window_set_focus (GTK_WINDOW (widget), NULL);
499 gdk_error_trap_push ();
500 XSetInputFocus (GDK_DISPLAY (),
501 GDK_WINDOW_XWINDOW (plug->socket_window),
502 RevertToParent, event->time);
504 gdk_error_trap_pop ();
506 gtk_plug_forward_key_press (plug, event);
515 /* Try to focus the first widget in the window */
516 if (GTK_WIDGET_DRAWABLE (bin->child) &&
517 GTK_WIDGET_IS_SENSITIVE (bin->child))
519 if (GTK_IS_CONTAINER (bin->child))
521 if (gtk_container_focus (GTK_CONTAINER (bin->child), direction))
524 else if (GTK_WIDGET_CAN_FOCUS (bin->child))
526 gtk_widget_grab_focus (bin->child);
536 send_xembed_message (GtkPlug *plug,
543 if (plug->socket_window)
547 xevent.xclient.window = GDK_WINDOW_XWINDOW (plug->socket_window);
548 xevent.xclient.type = ClientMessage;
549 xevent.xclient.message_type = gdk_atom_intern ("_XEMBED", FALSE);
550 xevent.xclient.format = 32;
551 xevent.xclient.data.l[0] = time;
552 xevent.xclient.data.l[1] = message;
553 xevent.xclient.data.l[2] = detail;
554 xevent.xclient.data.l[3] = data1;
555 xevent.xclient.data.l[4] = data2;
557 gdk_error_trap_push ();
558 XSendEvent (gdk_display,
559 GDK_WINDOW_XWINDOW (plug->socket_window),
560 False, NoEventMask, &xevent);
562 gdk_error_trap_pop ();
567 focus_first_last (GtkPlug *plug,
568 GtkDirectionType direction)
570 GtkWindow *window = GTK_WINDOW (plug);
573 if (window->focus_widget)
575 parent = window->focus_widget->parent;
578 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
579 parent = GTK_WIDGET (parent)->parent;
582 gtk_window_set_focus (GTK_WINDOW (plug), NULL);
585 gtk_container_focus (GTK_CONTAINER (plug), direction);
589 handle_modality_on (GtkPlug *plug)
592 if (!plug->modality_window)
594 plug->modality_window = gtk_window_new (GTK_WINDOW_POPUP);
595 gtk_window_set_grab_group (GTK_WINDOW (plug->modality_window), GTK_WINDOW (plug));
596 gtk_grab_add (plug->modality_window);
602 handle_modality_off (GtkPlug *plug)
605 if (plug->modality_window)
607 gtk_grab_remove (plug->modality_window);
608 gtk_widget_destroy (plug->modality_window);
609 plug->modality_window = NULL;
615 handle_xembed_message (GtkPlug *plug,
622 GTK_NOTE (PLUGSOCKET,
623 g_message ("Message of type %ld received", message));
627 case XEMBED_EMBEDDED_NOTIFY:
629 case XEMBED_WINDOW_ACTIVATE:
631 g_message ("GtkPlug: ACTIVATE received"));
633 case XEMBED_WINDOW_DEACTIVATE:
635 g_message ("GtkPlug: DEACTIVATE received"));
638 case XEMBED_MODALITY_ON:
639 handle_modality_on (plug);
641 case XEMBED_MODALITY_OFF:
642 handle_modality_off (plug);
645 case XEMBED_FOCUS_IN:
648 case XEMBED_FOCUS_FIRST:
649 focus_first_last (plug, GTK_DIR_TAB_FORWARD);
651 case XEMBED_FOCUS_LAST:
652 focus_first_last (plug, GTK_DIR_TAB_BACKWARD);
654 case XEMBED_FOCUS_CURRENT:
658 case XEMBED_FOCUS_OUT:
662 event.focus_change.type = GDK_FOCUS_CHANGE;
663 event.focus_change.window = GTK_WIDGET (plug)->window;
664 event.focus_change.send_event = TRUE;
665 event.focus_change.in = (message == XEMBED_FOCUS_IN);
667 gtk_widget_event (GTK_WIDGET (plug), &event);
672 case XEMBED_REQUEST_FOCUS:
673 case XEMBED_FOCUS_NEXT:
674 case XEMBED_FOCUS_PREV:
675 case XEMBED_GRAB_KEY:
676 case XEMBED_UNGRAB_KEY:
677 g_warning ("GtkPlug: Invalid _XEMBED message of type %ld received", message);
682 g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %ld", message));
687 static GdkFilterReturn
688 gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
690 GtkPlug *plug = GTK_PLUG (data);
691 XEvent *xevent = (XEvent *)gdk_xevent;
693 GdkFilterReturn return_val;
695 return_val = GDK_FILTER_CONTINUE;
697 switch (xevent->type)
700 if (xevent->xclient.message_type == gdk_atom_intern ("_XEMBED", FALSE))
702 handle_xembed_message (plug,
703 xevent->xclient.data.l[1],
704 xevent->xclient.data.l[2],
705 xevent->xclient.data.l[3],
706 xevent->xclient.data.l[4],
707 xevent->xclient.data.l[0]);
710 return GDK_FILTER_REMOVE;
715 return GDK_FILTER_CONTINUE;