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 (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,
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 GtkWindowClass *window_class = (GtkWindowClass *)class;
99 parent_class = gtk_type_class (GTK_TYPE_WINDOW);
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;
105 widget_class->focus = gtk_plug_focus;
107 window_class->set_focus = gtk_plug_set_focus;
109 window_class->accel_entries_changed = gtk_plug_accel_entries_changed;
114 gtk_plug_init (GtkPlug *plug)
118 window = GTK_WINDOW (plug);
120 window->type = GTK_WINDOW_TOPLEVEL;
121 window->auto_shrink = TRUE;
124 gtk_window_set_grab_group (window, window);
129 gtk_plug_construct (GtkPlug *plug, GdkNativeWindow socket_id)
133 plug->socket_window = gdk_window_lookup (socket_id);
134 plug->same_app = TRUE;
136 if (plug->socket_window == NULL)
138 plug->socket_window = gdk_window_foreign_new (socket_id);
139 plug->same_app = FALSE;
145 gtk_plug_new (GdkNativeWindow socket_id)
149 plug = GTK_PLUG (gtk_type_new (GTK_TYPE_PLUG));
150 gtk_plug_construct (plug, socket_id);
151 return GTK_WIDGET (plug);
155 gtk_plug_unrealize (GtkWidget *widget)
159 g_return_if_fail (widget != NULL);
160 g_return_if_fail (GTK_IS_PLUG (widget));
162 plug = GTK_PLUG (widget);
164 if (plug->socket_window != NULL)
166 gdk_window_set_user_data (plug->socket_window, NULL);
167 gdk_window_unref (plug->socket_window);
168 plug->socket_window = NULL;
172 if (plug->modality_window)
173 handle_modality_off (plug);
176 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
177 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
181 gtk_plug_realize (GtkWidget *widget)
185 GdkWindowAttr attributes;
186 gint attributes_mask;
188 g_return_if_fail (widget != NULL);
189 g_return_if_fail (GTK_IS_PLUG (widget));
191 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
192 window = GTK_WINDOW (widget);
193 plug = GTK_PLUG (widget);
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;
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 |
210 GDK_ENTER_NOTIFY_MASK |
211 GDK_LEAVE_NOTIFY_MASK |
212 GDK_FOCUS_CHANGE_MASK |
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);
219 gdk_error_trap_push ();
220 widget->window = gdk_window_new (plug->socket_window,
221 &attributes, attributes_mask);
223 if (gdk_error_trap_pop ()) /* Uh-oh */
225 gdk_error_trap_push ();
226 gdk_window_destroy (widget->window);
228 gdk_error_trap_pop ();
229 widget->window = gdk_window_new (NULL, &attributes, attributes_mask);
232 GDK_WINDOW_TYPE (widget->window) = GDK_WINDOW_TOPLEVEL;
233 gdk_window_set_user_data (widget->window, window);
235 widget->style = gtk_style_attach (widget->style, widget->window);
236 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
238 gdk_window_add_filter (widget->window, gtk_plug_filter_func, widget);
242 gtk_plug_key_press_event (GtkWidget *widget,
245 if (!GTK_WINDOW (widget)->has_focus)
247 gtk_plug_forward_key_press (GTK_PLUG (widget), event);
251 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event);
255 gtk_plug_forward_key_press (GtkPlug *plug, GdkEventKey *event)
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
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(),
273 xevent.xkey.same_screen = TRUE; /* FIXME ? */
275 gdk_error_trap_push ();
276 XSendEvent (gdk_display,
277 GDK_WINDOW_XWINDOW (plug->socket_window),
278 False, NoEventMask, &xevent);
280 gdk_error_trap_pop ();
284 gtk_plug_set_focus (GtkWindow *window,
287 GtkPlug *plug = GTK_PLUG (window);
289 GTK_WINDOW_CLASS (parent_class)->set_focus (window, focus);
291 /* Ask for focus from embedder
294 if (focus && !window->has_focus)
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 */
305 gdk_error_trap_push ();
306 XSendEvent (gdk_display,
307 GDK_WINDOW_XWINDOW (plug->socket_window),
308 False, NoEventMask, &xevent);
310 gdk_error_trap_pop ();
313 send_xembed_message (plug, XEMBED_REQUEST_FOCUS, 0, 0, 0,
314 gtk_get_current_event_time ());
322 guint accelerator_key;
323 GdkModifierType accelerator_mods;
327 grabbed_key_hash (gconstpointer a)
329 const GrabbedKey *key = a;
332 h = key->accelerator_key << 16;
333 h ^= key->accelerator_key >> 16;
334 h ^= key->accelerator_mods;
340 grabbed_key_equal (gconstpointer a, gconstpointer b)
342 const GrabbedKey *keya = a;
343 const GrabbedKey *keyb = b;
345 return (keya->accelerator_key == keyb->accelerator_key &&
346 keya->accelerator_mods == keyb->accelerator_mods);
350 add_grabbed_keys (gpointer key, gpointer val, gpointer data)
352 GrabbedKey *grabbed_key = key;
353 GtkPlug *plug = data;
355 if (!plug->grabbed_keys ||
356 !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
358 send_xembed_message (plug, XEMBED_GRAB_KEY, 0,
359 grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
360 gtk_get_current_event_time ());
365 remove_grabbed_keys (gpointer key, gpointer val, gpointer data)
367 GrabbedKey *grabbed_key = key;
368 GtkPlug *plug = data;
370 if (!plug->grabbed_keys ||
371 !g_hash_table_lookup (plug->grabbed_keys, grabbed_key))
373 send_xembed_message (plug, XEMBED_UNGRAB_KEY, 0,
374 grabbed_key->accelerator_key, grabbed_key->accelerator_mods,
375 gtk_get_current_event_time ());
380 gtk_plug_free_grabbed_keys (GHashTable *key_table)
382 g_hash_table_foreach (key_table, (GHFunc)g_free, NULL);
383 g_hash_table_destroy (key_table);
387 gtk_plug_accel_entries_changed (GtkWindow *window)
389 GHashTable *new_grabbed_keys, *old_grabbed_keys;
390 GSList *accel_groups, *tmp_list;
391 GtkPlug *plug = GTK_PLUG (window);
393 new_grabbed_keys = g_hash_table_new (grabbed_key_hash, grabbed_key_equal);
395 accel_groups = gtk_accel_groups_from_object (GTK_OBJECT (window));
397 tmp_list = accel_groups;
401 GtkAccelGroup *accel_group = tmp_list->data;
403 GtkAccelEntry *entries;
405 gtk_accel_group_get_entries (accel_group, &entries, &n_entries);
407 for (i = 0; i < n_entries; i++)
412 if (gdk_keymap_get_entries_for_keyval (NULL, entries[i].accelerator_key, &keys, &n_keys))
414 GrabbedKey *key = g_new (GrabbedKey, 1);
416 key->accelerator_key = keys[0].keycode;
417 key->accelerator_mods = entries[i].accelerator_mods;
419 g_hash_table_insert (new_grabbed_keys, key, key);
425 tmp_list = tmp_list->next;
428 g_hash_table_foreach (new_grabbed_keys, add_grabbed_keys, plug);
430 old_grabbed_keys = plug->grabbed_keys;
431 plug->grabbed_keys = new_grabbed_keys;
433 if (old_grabbed_keys)
435 g_hash_table_foreach (old_grabbed_keys, remove_grabbed_keys, plug);
436 gtk_plug_free_grabbed_keys (old_grabbed_keys);
443 gtk_plug_focus (GtkWidget *widget,
444 GtkDirectionType direction)
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;
453 /* We override GtkWindow's behavior, since we don't want wrapping here.
457 if (gtk_widget_child_focus (old_focus_child, direction))
460 if (window->focus_widget)
462 /* Wrapped off the end, clear the focus setting for the toplevel */
463 parent = window->focus_widget->parent;
466 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
467 parent = GTK_WIDGET (parent)->parent;
470 gtk_window_set_focus (GTK_WINDOW (container), NULL);
472 if (!GTK_CONTAINER (window)->focus_child)
480 case GTK_DIR_TAB_BACKWARD:
481 message = XEMBED_FOCUS_PREV;
485 case GTK_DIR_TAB_FORWARD:
486 message = XEMBED_FOCUS_NEXT;
490 send_xembed_message (plug, message, 0, 0, 0,
491 gtk_get_current_event_time ());
494 gtk_window_set_focus (GTK_WINDOW (widget), NULL);
496 gdk_error_trap_push ();
497 XSetInputFocus (GDK_DISPLAY (),
498 GDK_WINDOW_XWINDOW (plug->socket_window),
499 RevertToParent, event->time);
501 gdk_error_trap_pop ();
503 gtk_plug_forward_key_press (plug, event);
512 /* Try to focus the first widget in the window */
514 if (gtk_widget_child_focus (bin->child, direction))
522 send_xembed_message (GtkPlug *plug,
529 if (plug->socket_window)
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;
543 gdk_error_trap_push ();
544 XSendEvent (gdk_display,
545 GDK_WINDOW_XWINDOW (plug->socket_window),
546 False, NoEventMask, &xevent);
548 gdk_error_trap_pop ();
553 focus_first_last (GtkPlug *plug,
554 GtkDirectionType direction)
556 GtkWindow *window = GTK_WINDOW (plug);
559 if (window->focus_widget)
561 parent = window->focus_widget->parent;
564 gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL);
565 parent = GTK_WIDGET (parent)->parent;
568 gtk_window_set_focus (GTK_WINDOW (plug), NULL);
571 gtk_widget_child_focus (GTK_WIDGET (plug), direction);
575 handle_modality_on (GtkPlug *plug)
578 if (!plug->modality_window)
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);
588 handle_modality_off (GtkPlug *plug)
591 if (plug->modality_window)
593 gtk_grab_remove (plug->modality_window);
594 gtk_widget_destroy (plug->modality_window);
595 plug->modality_window = NULL;
601 handle_xembed_message (GtkPlug *plug,
608 GTK_NOTE (PLUGSOCKET,
609 g_message ("Message of type %ld received", message));
613 case XEMBED_EMBEDDED_NOTIFY:
615 case XEMBED_WINDOW_ACTIVATE:
617 g_message ("GtkPlug: ACTIVATE received"));
619 case XEMBED_WINDOW_DEACTIVATE:
621 g_message ("GtkPlug: DEACTIVATE received"));
624 case XEMBED_MODALITY_ON:
625 handle_modality_on (plug);
627 case XEMBED_MODALITY_OFF:
628 handle_modality_off (plug);
631 case XEMBED_FOCUS_IN:
634 case XEMBED_FOCUS_FIRST:
635 focus_first_last (plug, GTK_DIR_TAB_FORWARD);
637 case XEMBED_FOCUS_LAST:
638 focus_first_last (plug, GTK_DIR_TAB_BACKWARD);
640 case XEMBED_FOCUS_CURRENT:
644 case XEMBED_FOCUS_OUT:
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);
653 gtk_widget_event (GTK_WIDGET (plug), &event);
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);
668 g_message ("GtkPlug: Ignoring unknown _XEMBED message of type %ld", message));
673 static GdkFilterReturn
674 gtk_plug_filter_func (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
676 GtkPlug *plug = GTK_PLUG (data);
677 XEvent *xevent = (XEvent *)gdk_xevent;
679 GdkFilterReturn return_val;
681 return_val = GDK_FILTER_CONTINUE;
683 switch (xevent->type)
686 if (xevent->xclient.message_type == gdk_atom_intern ("_XEMBED", FALSE))
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]);
696 return GDK_FILTER_REMOVE;
701 return GDK_FILTER_CONTINUE;