1 | /* Bonobo-Plug.c: A private version of GtkPlug |
---|
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 Library 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 | * Library General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU Library 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-1999. 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 <stdio.h> |
---|
29 | #include "gdk/gdkx.h" |
---|
30 | #include "gdk/gdkkeysyms.h" |
---|
31 | #include "bonobo/bonobo-plug.h" |
---|
32 | |
---|
33 | |
---|
34 | |
---|
35 | /* Private part of the BonoboPlug structure */ |
---|
36 | struct _BonoboPlugPrivate { |
---|
37 | GdkWindow *socket_window; |
---|
38 | gint same_app; |
---|
39 | |
---|
40 | /* The control interface that holds us */ |
---|
41 | BonoboControl *control; |
---|
42 | |
---|
43 | /* |
---|
44 | * Whether we have the focus. We cannot use the GTK_HAS_FOCUS flag |
---|
45 | * because we are not a GTK_CAN_FOCUS widget and it would make |
---|
46 | * GtkContainer::focus() skip us immediately when trying to change the |
---|
47 | * focus within the plug's children. |
---|
48 | */ |
---|
49 | guint has_focus : 1; |
---|
50 | }; |
---|
51 | |
---|
52 | /* From Tk */ |
---|
53 | #define EMBEDDED_APP_WANTS_FOCUS NotifyNormal+20 |
---|
54 | |
---|
55 | /* Sometimes we have to forward keyboard events to our parent socket because |
---|
56 | * they get received on the plug side when the plug does not have the focus, or |
---|
57 | * because no widget within the plug handled the event and we must propagate it |
---|
58 | * upwards. However, since we are using XSendEvent(), we have to use the |
---|
59 | * original keycode and state values for the synthetic event. GDK translates |
---|
60 | * these and we cannot recover them from a GTK+ widget keyboard handler. So we |
---|
61 | * install an event filter for all keyboard events, ignore those that are not |
---|
62 | * for plug windows, and store the remaining ones in this table, which is a |
---|
63 | * circular buffer. The table is keyed by the timestamps of the events. When |
---|
64 | * we have to forward a key event, we fetch the original values from this table. |
---|
65 | */ |
---|
66 | #define KEY_EVENT_TABLE_SIZE 20 |
---|
67 | |
---|
68 | struct KeyEvent { |
---|
69 | guint32 time; |
---|
70 | guint keycode; |
---|
71 | guint state; |
---|
72 | }; |
---|
73 | |
---|
74 | struct KeyEvent key_event_table[KEY_EVENT_TABLE_SIZE]; |
---|
75 | |
---|
76 | /* The table is a circular buffer. Events get inserted at the tail, get removed |
---|
77 | * from the head. |
---|
78 | */ |
---|
79 | static int key_event_table_head; |
---|
80 | static int key_event_table_tail; |
---|
81 | static int key_event_table_nelements; |
---|
82 | |
---|
83 | |
---|
84 | static GtkWindowClass *parent_class = NULL; |
---|
85 | |
---|
86 | static void install_filter (GdkWindow *window); |
---|
87 | |
---|
88 | |
---|
89 | |
---|
90 | /** |
---|
91 | * bonobo_plug_construct: |
---|
92 | * @plug: The #BonoboPlug. |
---|
93 | * @socket_id: the XID of the socket's window. |
---|
94 | * |
---|
95 | * Finish the creation of a #BonoboPlug widget. This function |
---|
96 | * will generally only be used by classes deriving |
---|
97 | * from #BonoboPlug. |
---|
98 | */ |
---|
99 | void |
---|
100 | bonobo_plug_construct (BonoboPlug *plug, guint32 socket_id) |
---|
101 | { |
---|
102 | BonoboPlugPrivate *priv; |
---|
103 | |
---|
104 | g_return_if_fail (plug != NULL); |
---|
105 | g_return_if_fail (BONOBO_IS_PLUG (plug)); |
---|
106 | |
---|
107 | priv = plug->priv; |
---|
108 | |
---|
109 | priv->socket_window = gdk_window_lookup (socket_id); |
---|
110 | priv->same_app = TRUE; |
---|
111 | |
---|
112 | if (priv->socket_window == NULL) { |
---|
113 | priv->socket_window = gdk_window_foreign_new (socket_id); |
---|
114 | priv->same_app = FALSE; |
---|
115 | } |
---|
116 | } |
---|
117 | |
---|
118 | /** |
---|
119 | * bonobo_plug_new: |
---|
120 | * @socket_id: the XID of the socket's window. |
---|
121 | * |
---|
122 | * Create a new plug widget inside the #GtkSocket identified |
---|
123 | * by @socket_id. |
---|
124 | * |
---|
125 | * Returns: the new #BonoboPlug widget. |
---|
126 | */ |
---|
127 | GtkWidget* |
---|
128 | bonobo_plug_new (guint32 socket_id) |
---|
129 | { |
---|
130 | BonoboPlug *plug; |
---|
131 | |
---|
132 | plug = BONOBO_PLUG (gtk_type_new (bonobo_plug_get_type ())); |
---|
133 | bonobo_plug_construct (plug, socket_id); |
---|
134 | return GTK_WIDGET (plug); |
---|
135 | } |
---|
136 | |
---|
137 | /* Destroy handler for the plug widget */ |
---|
138 | static void |
---|
139 | bonobo_plug_destroy (GtkObject *object) |
---|
140 | { |
---|
141 | BonoboPlug *plug; |
---|
142 | BonoboPlugPrivate *priv; |
---|
143 | |
---|
144 | g_return_if_fail (object != NULL); |
---|
145 | g_return_if_fail (BONOBO_IS_PLUG (object)); |
---|
146 | |
---|
147 | plug = BONOBO_PLUG (object); |
---|
148 | priv = plug->priv; |
---|
149 | |
---|
150 | g_free (priv); |
---|
151 | plug->priv = NULL; |
---|
152 | |
---|
153 | if (GTK_OBJECT_CLASS (parent_class)->destroy) |
---|
154 | (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); |
---|
155 | } |
---|
156 | |
---|
157 | static void |
---|
158 | bonobo_plug_unrealize (GtkWidget *widget) |
---|
159 | { |
---|
160 | BonoboPlug *plug; |
---|
161 | BonoboPlugPrivate *priv; |
---|
162 | |
---|
163 | g_return_if_fail (widget != NULL); |
---|
164 | g_return_if_fail (BONOBO_IS_PLUG (widget)); |
---|
165 | |
---|
166 | plug = BONOBO_PLUG (widget); |
---|
167 | priv = plug->priv; |
---|
168 | |
---|
169 | if (GTK_WIDGET_CLASS (parent_class)->unrealize) |
---|
170 | (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget); |
---|
171 | |
---|
172 | if (priv->socket_window != NULL) { |
---|
173 | gdk_window_set_user_data (priv->socket_window, NULL); |
---|
174 | gdk_window_unref (priv->socket_window); |
---|
175 | priv->socket_window = NULL; |
---|
176 | } |
---|
177 | } |
---|
178 | |
---|
179 | /* Checks whether a window is a descendant of a plug window. */ |
---|
180 | static gboolean |
---|
181 | is_descendant_window_of_plug (GdkWindow *window) |
---|
182 | { |
---|
183 | while (window) { |
---|
184 | GtkWidget *widget; |
---|
185 | |
---|
186 | widget = window->user_data; |
---|
187 | if (widget && BONOBO_IS_PLUG (widget)) |
---|
188 | return TRUE; |
---|
189 | |
---|
190 | window = gdk_window_get_parent (window); |
---|
191 | } |
---|
192 | |
---|
193 | return FALSE; |
---|
194 | } |
---|
195 | |
---|
196 | /* Filter function for all events. See the comments for key_event_table above. |
---|
197 | * We filter key events and log them in the key_event_table, and we take care |
---|
198 | * of child window creations. |
---|
199 | */ |
---|
200 | static GdkFilterReturn |
---|
201 | event_filter_cb (GdkXEvent *xevent, GdkEvent *event, gpointer data) |
---|
202 | { |
---|
203 | XEvent *xev; |
---|
204 | struct KeyEvent *ke; |
---|
205 | GdkWindow *w; |
---|
206 | |
---|
207 | xev = (XEvent *) xevent; |
---|
208 | |
---|
209 | /* Ignore non-keyboard events */ |
---|
210 | if (!(xev->type == KeyPress || xev->type == KeyRelease || xev->type == CreateNotify)) |
---|
211 | return GDK_FILTER_CONTINUE; |
---|
212 | |
---|
213 | if (!is_descendant_window_of_plug (gdk_window_lookup (xev->xany.window))) |
---|
214 | return GDK_FILTER_CONTINUE; |
---|
215 | |
---|
216 | switch (xev->type) { |
---|
217 | case KeyPress: |
---|
218 | case KeyRelease: |
---|
219 | ke = key_event_table + key_event_table_tail; |
---|
220 | |
---|
221 | ke->time = xev->xkey.time; |
---|
222 | ke->keycode = xev->xkey.keycode; |
---|
223 | ke->state = xev->xkey.state; |
---|
224 | |
---|
225 | /* We just overwrite the oldest entries if the table becomes full */ |
---|
226 | |
---|
227 | if (key_event_table_tail == key_event_table_head) { |
---|
228 | if (key_event_table_nelements == 0) |
---|
229 | key_event_table_nelements = 1; |
---|
230 | else { |
---|
231 | g_assert (key_event_table_nelements == KEY_EVENT_TABLE_SIZE); |
---|
232 | |
---|
233 | key_event_table_head++; |
---|
234 | if (key_event_table_head == KEY_EVENT_TABLE_SIZE) |
---|
235 | key_event_table_head = 0; |
---|
236 | } |
---|
237 | |
---|
238 | key_event_table_tail++; |
---|
239 | if (key_event_table_tail == KEY_EVENT_TABLE_SIZE) |
---|
240 | key_event_table_tail = 0; |
---|
241 | } else { |
---|
242 | key_event_table_tail++; |
---|
243 | if (key_event_table_tail == KEY_EVENT_TABLE_SIZE) |
---|
244 | key_event_table_tail = 0; |
---|
245 | |
---|
246 | key_event_table_nelements++; |
---|
247 | g_assert (key_event_table_nelements <= KEY_EVENT_TABLE_SIZE); |
---|
248 | } |
---|
249 | |
---|
250 | return GDK_FILTER_CONTINUE; |
---|
251 | |
---|
252 | case CreateNotify: |
---|
253 | w = gdk_window_lookup (xev->xcreatewindow.window); |
---|
254 | if (w) |
---|
255 | install_filter (w); |
---|
256 | |
---|
257 | return GDK_FILTER_CONTINUE; |
---|
258 | |
---|
259 | default: |
---|
260 | g_assert_not_reached (); |
---|
261 | return GDK_FILTER_CONTINUE; |
---|
262 | } |
---|
263 | } |
---|
264 | |
---|
265 | /* Recursively sets up the key event filter for the specified window and all its |
---|
266 | * children. |
---|
267 | */ |
---|
268 | static void |
---|
269 | install_filter (GdkWindow *window) |
---|
270 | { |
---|
271 | GList *children; |
---|
272 | GList *l; |
---|
273 | Window xwindow; |
---|
274 | XWindowAttributes attr; |
---|
275 | |
---|
276 | gdk_window_add_filter (window, event_filter_cb, NULL); |
---|
277 | |
---|
278 | /* Add SubstructureNotifyMask so that we can monitor for child window creations */ |
---|
279 | |
---|
280 | xwindow = GDK_WINDOW_XWINDOW (window); |
---|
281 | if (XGetWindowAttributes (GDK_DISPLAY (), xwindow, &attr) == Success) |
---|
282 | XSelectInput (GDK_DISPLAY (), xwindow, attr.your_event_mask | SubstructureNotifyMask); |
---|
283 | |
---|
284 | /* Add filters for all the children */ |
---|
285 | |
---|
286 | children = gdk_window_get_children (window); |
---|
287 | |
---|
288 | for (l = children; l; l = l->next) { |
---|
289 | GdkWindow *w; |
---|
290 | |
---|
291 | w = l->data; |
---|
292 | install_filter (w); |
---|
293 | } |
---|
294 | |
---|
295 | g_list_free (children); |
---|
296 | } |
---|
297 | |
---|
298 | /* map handler for the plug widget. We install the key event filter for all of |
---|
299 | * our child windows here, ugh. |
---|
300 | */ |
---|
301 | static void |
---|
302 | bonobo_plug_map (GtkWidget *widget) |
---|
303 | { |
---|
304 | if (GTK_WIDGET_CLASS (parent_class)->map) |
---|
305 | (* GTK_WIDGET_CLASS (parent_class)->map) (widget); |
---|
306 | |
---|
307 | install_filter (widget->window); |
---|
308 | } |
---|
309 | |
---|
310 | static void |
---|
311 | bonobo_plug_realize (GtkWidget *widget) |
---|
312 | { |
---|
313 | BonoboPlug *plug; |
---|
314 | BonoboPlugPrivate *priv; |
---|
315 | GtkWindow *window; |
---|
316 | GdkWindowAttr attributes; |
---|
317 | gint attributes_mask; |
---|
318 | |
---|
319 | g_return_if_fail (widget != NULL); |
---|
320 | g_return_if_fail (BONOBO_IS_PLUG (widget)); |
---|
321 | |
---|
322 | plug = BONOBO_PLUG (widget); |
---|
323 | priv = plug->priv; |
---|
324 | |
---|
325 | GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); |
---|
326 | window = GTK_WINDOW (widget); |
---|
327 | |
---|
328 | attributes.window_type = GDK_WINDOW_CHILD; /* XXX GDK_WINDOW_PLUG ? */ |
---|
329 | attributes.title = window->title; |
---|
330 | attributes.wmclass_name = window->wmclass_name; |
---|
331 | attributes.wmclass_class = window->wmclass_class; |
---|
332 | attributes.width = widget->allocation.width; |
---|
333 | attributes.height = widget->allocation.height; |
---|
334 | attributes.wclass = GDK_INPUT_OUTPUT; |
---|
335 | |
---|
336 | /* |
---|
337 | * this isn't right - we should match our parent's visual/colormap. |
---|
338 | * though that will require handling "foreign" colormaps |
---|
339 | */ |
---|
340 | attributes.visual = gtk_widget_get_visual (widget); |
---|
341 | attributes.colormap = gtk_widget_get_colormap (widget); |
---|
342 | attributes.event_mask = gtk_widget_get_events (widget); |
---|
343 | attributes.event_mask |= (GDK_EXPOSURE_MASK | |
---|
344 | GDK_KEY_PRESS_MASK | |
---|
345 | GDK_ENTER_NOTIFY_MASK | |
---|
346 | GDK_LEAVE_NOTIFY_MASK | |
---|
347 | GDK_FOCUS_CHANGE_MASK | |
---|
348 | GDK_STRUCTURE_MASK); |
---|
349 | |
---|
350 | attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP; |
---|
351 | attributes_mask |= (window->title ? GDK_WA_TITLE : 0); |
---|
352 | attributes_mask |= (window->wmclass_name ? GDK_WA_WMCLASS : 0); |
---|
353 | |
---|
354 | gdk_error_trap_push (); |
---|
355 | widget->window = gdk_window_new (priv->socket_window, |
---|
356 | &attributes, attributes_mask); |
---|
357 | gdk_flush (); |
---|
358 | if (gdk_error_trap_pop ()) { |
---|
359 | /* Uh-oh */ |
---|
360 | gdk_error_trap_push (); |
---|
361 | gdk_window_destroy (widget->window); |
---|
362 | gdk_flush (); |
---|
363 | gdk_error_trap_pop (); |
---|
364 | widget->window = gdk_window_new (NULL, &attributes, attributes_mask); |
---|
365 | } |
---|
366 | |
---|
367 | ((GdkWindowPrivate *)widget->window)->window_type = GDK_WINDOW_TOPLEVEL; |
---|
368 | gdk_window_set_user_data (widget->window, window); |
---|
369 | |
---|
370 | widget->style = gtk_style_attach (widget->style, widget->window); |
---|
371 | gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); |
---|
372 | } |
---|
373 | |
---|
374 | /* Looks up a KeyEvent structure in the key event table based on the timestamp |
---|
375 | * of the specified event. If it is not found, returns NULL. |
---|
376 | */ |
---|
377 | static struct KeyEvent * |
---|
378 | lookup_key_event (GdkEventKey *event) |
---|
379 | { |
---|
380 | int i; |
---|
381 | |
---|
382 | if (key_event_table_nelements == 0) |
---|
383 | return NULL; |
---|
384 | |
---|
385 | i = key_event_table_head; |
---|
386 | |
---|
387 | while (key_event_table_nelements > 0) { |
---|
388 | key_event_table_head++; |
---|
389 | if (key_event_table_head == KEY_EVENT_TABLE_SIZE) |
---|
390 | key_event_table_head = 0; |
---|
391 | |
---|
392 | key_event_table_nelements--; |
---|
393 | |
---|
394 | if (key_event_table[i].time == event->time) |
---|
395 | return key_event_table + i; |
---|
396 | } |
---|
397 | |
---|
398 | return NULL; |
---|
399 | } |
---|
400 | |
---|
401 | /* Extracts a key event from the key event table and forwards it to our parent |
---|
402 | * socket. |
---|
403 | */ |
---|
404 | static void |
---|
405 | bonobo_plug_forward_key_event (BonoboPlug *plug, GdkEventKey *event) |
---|
406 | { |
---|
407 | BonoboPlugPrivate *priv; |
---|
408 | XEvent xevent; |
---|
409 | struct KeyEvent *ke; |
---|
410 | |
---|
411 | priv = plug->priv; |
---|
412 | |
---|
413 | if (event->type == GDK_KEY_PRESS) |
---|
414 | xevent.xkey.type = KeyPress; |
---|
415 | else if (event->type == GDK_KEY_RELEASE) |
---|
416 | xevent.xkey.type = KeyRelease; |
---|
417 | else |
---|
418 | g_assert_not_reached (); |
---|
419 | |
---|
420 | xevent.xkey.display = GDK_WINDOW_XDISPLAY (GTK_WIDGET(plug)->window); |
---|
421 | xevent.xkey.window = GDK_WINDOW_XWINDOW (priv->socket_window); |
---|
422 | xevent.xkey.root = GDK_ROOT_WINDOW (); /* FIXME */ |
---|
423 | xevent.xkey.time = event->time; |
---|
424 | xevent.xkey.x = 0; |
---|
425 | xevent.xkey.y = 0; |
---|
426 | xevent.xkey.x_root = 0; |
---|
427 | xevent.xkey.y_root = 0; |
---|
428 | xevent.xkey.same_screen = TRUE; /* FIXME ? */ |
---|
429 | |
---|
430 | ke = lookup_key_event (event); |
---|
431 | if (ke) { |
---|
432 | xevent.xkey.keycode = ke->keycode; |
---|
433 | xevent.xkey.state = ke->state; |
---|
434 | } else { |
---|
435 | /* Do the best we can do at this point, even if we may lose |
---|
436 | * information from the original event. |
---|
437 | */ |
---|
438 | xevent.xkey.state = event->state; |
---|
439 | xevent.xkey.keycode = XKeysymToKeycode(GDK_DISPLAY(), event->keyval); |
---|
440 | } |
---|
441 | |
---|
442 | gdk_error_trap_push (); |
---|
443 | XSendEvent (gdk_display, |
---|
444 | GDK_WINDOW_XWINDOW (priv->socket_window), |
---|
445 | False, NoEventMask, &xevent); |
---|
446 | gdk_flush (); |
---|
447 | gdk_error_trap_pop (); |
---|
448 | } |
---|
449 | |
---|
450 | /* Key_press_event handler for the plug widget */ |
---|
451 | static gint |
---|
452 | bonobo_plug_key_press_event (GtkWidget *widget, GdkEventKey *event) |
---|
453 | { |
---|
454 | BonoboPlug *plug; |
---|
455 | BonoboPlugPrivate *priv; |
---|
456 | GtkContainer *container; |
---|
457 | GtkWindow *window; |
---|
458 | GtkDirectionType direction = 0; |
---|
459 | gint return_val; |
---|
460 | |
---|
461 | g_return_val_if_fail (widget != NULL, FALSE); |
---|
462 | g_return_val_if_fail (BONOBO_IS_PLUG (widget), FALSE); |
---|
463 | g_return_val_if_fail (event != NULL, FALSE); |
---|
464 | |
---|
465 | plug = BONOBO_PLUG (widget); |
---|
466 | priv = plug->priv; |
---|
467 | |
---|
468 | container = GTK_CONTAINER (widget); |
---|
469 | window = GTK_WINDOW (widget); |
---|
470 | |
---|
471 | if (!GTK_WIDGET_HAS_FOCUS (widget)) { |
---|
472 | bonobo_plug_forward_key_event (plug, event); |
---|
473 | return FALSE; |
---|
474 | } |
---|
475 | |
---|
476 | return_val = FALSE; |
---|
477 | if (window->focus_widget |
---|
478 | && window->focus_widget != widget |
---|
479 | && GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) |
---|
480 | return_val = gtk_widget_event (window->focus_widget, (GdkEvent*) event); |
---|
481 | |
---|
482 | if (!return_val) |
---|
483 | switch (event->keyval) { |
---|
484 | case GDK_space: |
---|
485 | if (window->focus_widget) { |
---|
486 | if (GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) |
---|
487 | return_val = gtk_widget_activate (window->focus_widget); |
---|
488 | } |
---|
489 | break; |
---|
490 | |
---|
491 | case GDK_Return: |
---|
492 | case GDK_KP_Enter: |
---|
493 | if (window->default_widget && GTK_WIDGET_IS_SENSITIVE (window->default_widget) |
---|
494 | && (!window->focus_widget |
---|
495 | || !GTK_WIDGET_RECEIVES_DEFAULT (window->focus_widget))) |
---|
496 | return_val = gtk_widget_activate (window->default_widget); |
---|
497 | else if (window->focus_widget) { |
---|
498 | if (GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) |
---|
499 | return_val = gtk_widget_activate (window->focus_widget); |
---|
500 | } |
---|
501 | break; |
---|
502 | |
---|
503 | case GDK_Up: |
---|
504 | case GDK_Down: |
---|
505 | case GDK_Left: |
---|
506 | case GDK_Right: |
---|
507 | case GDK_KP_Up: |
---|
508 | case GDK_KP_Down: |
---|
509 | case GDK_KP_Left: |
---|
510 | case GDK_KP_Right: |
---|
511 | case GDK_Tab: |
---|
512 | case GDK_ISO_Left_Tab: |
---|
513 | switch (event->keyval) { |
---|
514 | case GDK_Up: |
---|
515 | case GDK_KP_Up: |
---|
516 | direction = GTK_DIR_UP; |
---|
517 | break; |
---|
518 | case GDK_Down: |
---|
519 | case GDK_KP_Down: |
---|
520 | direction = GTK_DIR_DOWN; |
---|
521 | break; |
---|
522 | case GDK_Left: |
---|
523 | case GDK_KP_Left: |
---|
524 | direction = GTK_DIR_LEFT; |
---|
525 | break; |
---|
526 | case GDK_Right: |
---|
527 | case GDK_KP_Right: |
---|
528 | direction = GTK_DIR_RIGHT; |
---|
529 | break; |
---|
530 | case GDK_Tab: |
---|
531 | case GDK_ISO_Left_Tab: |
---|
532 | if (event->state & GDK_SHIFT_MASK) |
---|
533 | direction = GTK_DIR_TAB_BACKWARD; |
---|
534 | else |
---|
535 | direction = GTK_DIR_TAB_FORWARD; |
---|
536 | break; |
---|
537 | default: |
---|
538 | direction = GTK_DIR_UP; /* never reached, but makes compiler happy */ |
---|
539 | } |
---|
540 | |
---|
541 | gtk_container_focus (container, direction); |
---|
542 | |
---|
543 | if (!container->focus_child) { |
---|
544 | gtk_window_set_focus (window, NULL); |
---|
545 | |
---|
546 | gdk_error_trap_push (); |
---|
547 | XSetInputFocus (GDK_DISPLAY (), |
---|
548 | GDK_WINDOW_XWINDOW (priv->socket_window), |
---|
549 | RevertToParent, event->time); |
---|
550 | gdk_flush (); |
---|
551 | gdk_error_trap_pop (); |
---|
552 | |
---|
553 | bonobo_plug_forward_key_event (plug, event); |
---|
554 | } |
---|
555 | |
---|
556 | return_val = TRUE; |
---|
557 | break; |
---|
558 | } |
---|
559 | |
---|
560 | /* |
---|
561 | * If we havn't handled it pass it on to our socket, since it might be a |
---|
562 | * keybinding or something interesting. |
---|
563 | */ |
---|
564 | if (!return_val) |
---|
565 | bonobo_plug_forward_key_event (plug, event); |
---|
566 | |
---|
567 | return return_val; |
---|
568 | } |
---|
569 | |
---|
570 | /* Key_release_event handler for the plug widget */ |
---|
571 | static gint |
---|
572 | bonobo_plug_key_release_event (GtkWidget *widget, GdkEventKey *event) |
---|
573 | { |
---|
574 | BonoboPlug *plug; |
---|
575 | GtkWindow *window; |
---|
576 | gint return_val; |
---|
577 | |
---|
578 | g_return_val_if_fail (widget != NULL, FALSE); |
---|
579 | g_return_val_if_fail (BONOBO_IS_PLUG (widget), FALSE); |
---|
580 | g_return_val_if_fail (event != NULL, FALSE); |
---|
581 | |
---|
582 | plug = BONOBO_PLUG (widget); |
---|
583 | |
---|
584 | if (!GTK_WIDGET_HAS_FOCUS (widget)) { |
---|
585 | bonobo_plug_forward_key_event (plug, event); |
---|
586 | return FALSE; |
---|
587 | } |
---|
588 | |
---|
589 | window = GTK_WINDOW (widget); |
---|
590 | |
---|
591 | return_val = FALSE; |
---|
592 | if (window->focus_widget |
---|
593 | && window->focus_widget != widget |
---|
594 | && GTK_WIDGET_IS_SENSITIVE (window->focus_widget)) |
---|
595 | return_val = gtk_widget_event (window->focus_widget, (GdkEvent*) event); |
---|
596 | |
---|
597 | /* |
---|
598 | * If we havn't handled it pass it on to our socket, since it might be a |
---|
599 | * keybinding or something interesting. |
---|
600 | */ |
---|
601 | if (!return_val) |
---|
602 | bonobo_plug_forward_key_event (plug, event); |
---|
603 | |
---|
604 | return return_val; |
---|
605 | } |
---|
606 | |
---|
607 | /* Focus_in_event handler for the plug widget */ |
---|
608 | static gint |
---|
609 | bonobo_plug_focus_in_event (GtkWidget *widget, GdkEventFocus *event) |
---|
610 | { |
---|
611 | GtkWindow *window; |
---|
612 | GdkEventFocus fevent; |
---|
613 | |
---|
614 | g_return_val_if_fail (widget != NULL, FALSE); |
---|
615 | g_return_val_if_fail (BONOBO_IS_PLUG (widget), FALSE); |
---|
616 | g_return_val_if_fail (event != NULL, FALSE); |
---|
617 | |
---|
618 | /* |
---|
619 | * It appears spurious focus in events can occur when the window is |
---|
620 | * hidden. So we'll just check to see if the window is visible before |
---|
621 | * actually handling the event. |
---|
622 | */ |
---|
623 | if (GTK_WIDGET_VISIBLE (widget)) { |
---|
624 | GTK_OBJECT_SET_FLAGS (widget, GTK_HAS_FOCUS); |
---|
625 | |
---|
626 | window = GTK_WINDOW (widget); |
---|
627 | if (window->focus_widget && !GTK_WIDGET_HAS_FOCUS (window->focus_widget)) { |
---|
628 | fevent.type = GDK_FOCUS_CHANGE; |
---|
629 | fevent.window = window->focus_widget->window; |
---|
630 | fevent.in = TRUE; |
---|
631 | |
---|
632 | gtk_widget_event (window->focus_widget, (GdkEvent*) &fevent); |
---|
633 | } |
---|
634 | } |
---|
635 | |
---|
636 | return FALSE; |
---|
637 | } |
---|
638 | |
---|
639 | /* Focus_out_event handler for the plug widget */ |
---|
640 | static gint |
---|
641 | bonobo_plug_focus_out_event (GtkWidget *widget, GdkEventFocus *event) |
---|
642 | { |
---|
643 | GtkWindow *window; |
---|
644 | GdkEventFocus fevent; |
---|
645 | |
---|
646 | g_return_val_if_fail (widget != NULL, FALSE); |
---|
647 | g_return_val_if_fail (BONOBO_IS_PLUG (widget), FALSE); |
---|
648 | g_return_val_if_fail (event != NULL, FALSE); |
---|
649 | |
---|
650 | window = GTK_WINDOW (widget); |
---|
651 | |
---|
652 | GTK_OBJECT_UNSET_FLAGS (widget, GTK_HAS_FOCUS); |
---|
653 | |
---|
654 | if (window->focus_widget && GTK_WIDGET_HAS_FOCUS (window->focus_widget)) { |
---|
655 | fevent.type = GDK_FOCUS_CHANGE; |
---|
656 | fevent.window = window->focus_widget->window; |
---|
657 | fevent.in = FALSE; |
---|
658 | |
---|
659 | gtk_widget_event (window->focus_widget, (GdkEvent*) &fevent); |
---|
660 | } |
---|
661 | |
---|
662 | return FALSE; |
---|
663 | } |
---|
664 | |
---|
665 | /* |
---|
666 | * Focus handler for the plug widget. It should be the same as GtkWindow's, but |
---|
667 | * it should not wrap around if we reach the `last' widget so that we can pass |
---|
668 | * on the focus request to the parent. |
---|
669 | */ |
---|
670 | static gint |
---|
671 | bonobo_plug_focus (GtkContainer *container, GtkDirectionType direction) |
---|
672 | { |
---|
673 | BonoboPlug *plug; |
---|
674 | BonoboPlugPrivate *priv; |
---|
675 | GtkWindow *window; |
---|
676 | GtkBin *bin; |
---|
677 | GtkWidget *old_focus_child; |
---|
678 | |
---|
679 | plug = BONOBO_PLUG (container); |
---|
680 | priv = plug->priv; |
---|
681 | |
---|
682 | window = GTK_WINDOW (container); |
---|
683 | bin = GTK_BIN (container); |
---|
684 | |
---|
685 | old_focus_child = container->focus_child; |
---|
686 | |
---|
687 | if (old_focus_child) { |
---|
688 | if (GTK_IS_CONTAINER (old_focus_child) |
---|
689 | && GTK_WIDGET_DRAWABLE (old_focus_child) |
---|
690 | && GTK_WIDGET_IS_SENSITIVE (old_focus_child) |
---|
691 | && gtk_container_focus (GTK_CONTAINER (old_focus_child), direction)) |
---|
692 | return TRUE; |
---|
693 | } |
---|
694 | |
---|
695 | if (window->focus_widget) { |
---|
696 | GtkWidget *parent; |
---|
697 | |
---|
698 | parent = window->focus_widget->parent; |
---|
699 | while (parent) { |
---|
700 | gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL); |
---|
701 | parent = parent->parent; |
---|
702 | } |
---|
703 | |
---|
704 | gtk_window_set_focus (window, NULL); |
---|
705 | return FALSE; |
---|
706 | } |
---|
707 | |
---|
708 | if (GTK_WIDGET_DRAWABLE (bin->child) && GTK_WIDGET_IS_SENSITIVE (bin->child)) { |
---|
709 | if (GTK_IS_CONTAINER (bin->child)) |
---|
710 | return gtk_container_focus (GTK_CONTAINER (bin->child), direction); |
---|
711 | else if (GTK_WIDGET_CAN_FOCUS (bin->child)) { |
---|
712 | gtk_widget_grab_focus (bin->child); |
---|
713 | return TRUE; |
---|
714 | } else |
---|
715 | return FALSE; |
---|
716 | } else |
---|
717 | return FALSE; |
---|
718 | } |
---|
719 | |
---|
720 | /* Set_focus handler for the plug widget */ |
---|
721 | static void |
---|
722 | bonobo_plug_set_focus (GtkWindow *window, GtkWidget *focus) |
---|
723 | { |
---|
724 | BonoboPlug *plug; |
---|
725 | BonoboPlugPrivate *priv; |
---|
726 | |
---|
727 | plug = BONOBO_PLUG (window); |
---|
728 | priv = plug->priv; |
---|
729 | |
---|
730 | (* GTK_WINDOW_CLASS (parent_class)->set_focus) (window, focus); |
---|
731 | |
---|
732 | if (focus && GTK_WIDGET_CAN_FOCUS (focus) && !GTK_WIDGET_HAS_FOCUS (window)) { |
---|
733 | XEvent xevent; |
---|
734 | |
---|
735 | xevent.xfocus.type = FocusIn; |
---|
736 | xevent.xfocus.display = GDK_WINDOW_XDISPLAY (GTK_WIDGET (plug)->window); |
---|
737 | xevent.xfocus.window = GDK_WINDOW_XWINDOW (priv->socket_window); |
---|
738 | xevent.xfocus.mode = EMBEDDED_APP_WANTS_FOCUS; |
---|
739 | xevent.xfocus.detail = FALSE; /* Don't force */ |
---|
740 | |
---|
741 | gdk_error_trap_push (); |
---|
742 | XSendEvent (gdk_display, |
---|
743 | GDK_WINDOW_XWINDOW (priv->socket_window), |
---|
744 | False, NoEventMask, &xevent); |
---|
745 | gdk_flush (); |
---|
746 | gdk_error_trap_pop (); |
---|
747 | } |
---|
748 | } |
---|
749 | |
---|
750 | static void |
---|
751 | bonobo_plug_init (BonoboPlug *plug) |
---|
752 | { |
---|
753 | BonoboPlugPrivate *priv; |
---|
754 | GtkWindow *window; |
---|
755 | |
---|
756 | priv = g_new0 (BonoboPlugPrivate, 1); |
---|
757 | plug->priv = priv; |
---|
758 | |
---|
759 | window = GTK_WINDOW (plug); |
---|
760 | |
---|
761 | window->type = GTK_WINDOW_TOPLEVEL; |
---|
762 | window->auto_shrink = TRUE; |
---|
763 | |
---|
764 | priv->control = NULL; |
---|
765 | priv->has_focus = FALSE; |
---|
766 | } |
---|
767 | |
---|
768 | /* Sets up the key event filter and key event table; see the comments for |
---|
769 | * key_event_table above. |
---|
770 | */ |
---|
771 | static void |
---|
772 | setup_event_filter (void) |
---|
773 | { |
---|
774 | key_event_table_head = key_event_table_tail = key_event_table_nelements = 0; |
---|
775 | |
---|
776 | /* This is a default event filter, so we pass NULL for the window field */ |
---|
777 | gdk_window_add_filter (NULL, event_filter_cb, NULL); |
---|
778 | } |
---|
779 | |
---|
780 | static void |
---|
781 | bonobo_plug_class_init (BonoboPlugClass *class) |
---|
782 | { |
---|
783 | GtkObjectClass *object_class; |
---|
784 | GtkWidgetClass *widget_class; |
---|
785 | GtkContainerClass *container_class; |
---|
786 | GtkWindowClass *window_class; |
---|
787 | |
---|
788 | object_class = (GtkObjectClass *) class; |
---|
789 | widget_class = (GtkWidgetClass *) class; |
---|
790 | container_class = (GtkContainerClass *) class; |
---|
791 | window_class = (GtkWindowClass *) class; |
---|
792 | |
---|
793 | parent_class = gtk_type_class (gtk_window_get_type ()); |
---|
794 | |
---|
795 | object_class->destroy = bonobo_plug_destroy; |
---|
796 | |
---|
797 | widget_class->map = bonobo_plug_map; |
---|
798 | widget_class->realize = bonobo_plug_realize; |
---|
799 | widget_class->unrealize = bonobo_plug_unrealize; |
---|
800 | widget_class->key_press_event = bonobo_plug_key_press_event; |
---|
801 | widget_class->key_release_event = bonobo_plug_key_release_event; |
---|
802 | widget_class->focus_in_event = bonobo_plug_focus_in_event; |
---|
803 | widget_class->focus_out_event = bonobo_plug_focus_out_event; |
---|
804 | |
---|
805 | container_class->focus = bonobo_plug_focus; |
---|
806 | |
---|
807 | window_class->set_focus = bonobo_plug_set_focus; |
---|
808 | |
---|
809 | setup_event_filter (); |
---|
810 | } |
---|
811 | |
---|
812 | guint |
---|
813 | bonobo_plug_get_type () |
---|
814 | { |
---|
815 | static guint plug_type = 0; |
---|
816 | |
---|
817 | if (!plug_type) |
---|
818 | { |
---|
819 | static const GtkTypeInfo plug_info = |
---|
820 | { |
---|
821 | "BonoboPlug", |
---|
822 | sizeof (BonoboPlug), |
---|
823 | sizeof (BonoboPlugClass), |
---|
824 | (GtkClassInitFunc) bonobo_plug_class_init, |
---|
825 | (GtkObjectInitFunc) bonobo_plug_init, |
---|
826 | (GtkArgSetFunc) NULL, |
---|
827 | (GtkArgGetFunc) NULL |
---|
828 | }; |
---|
829 | |
---|
830 | plug_type = gtk_type_unique (gtk_window_get_type (), &plug_info); |
---|
831 | } |
---|
832 | |
---|
833 | return plug_type; |
---|
834 | } |
---|
835 | |
---|
836 | /** |
---|
837 | * bonobo_plug_set_control: |
---|
838 | * @plug: A plug. |
---|
839 | * @control: Control that wraps the plug widget. |
---|
840 | * |
---|
841 | * Sets the #BonoboControl that the plug will use to proxy requests to the |
---|
842 | * parent container. |
---|
843 | **/ |
---|
844 | void |
---|
845 | bonobo_plug_set_control (BonoboPlug *plug, BonoboControl *control) |
---|
846 | { |
---|
847 | BonoboPlugPrivate *priv; |
---|
848 | |
---|
849 | g_return_if_fail (plug != NULL); |
---|
850 | g_return_if_fail (BONOBO_IS_PLUG (plug)); |
---|
851 | |
---|
852 | priv = plug->priv; |
---|
853 | g_return_if_fail (priv->control == NULL); |
---|
854 | |
---|
855 | g_return_if_fail (control != NULL); |
---|
856 | g_return_if_fail (BONOBO_IS_CONTROL (control)); |
---|
857 | |
---|
858 | priv->control = control; |
---|
859 | } |
---|
860 | |
---|
861 | /** |
---|
862 | * bonobo_plug_clear_focus_chain: |
---|
863 | * @plug: A plug. |
---|
864 | * |
---|
865 | * Clears the focus children from the container hierarchy inside a plug. This |
---|
866 | * should be used only by the #BonoboControl implementation. |
---|
867 | **/ |
---|
868 | void |
---|
869 | bonobo_plug_clear_focus_chain (BonoboPlug *plug) |
---|
870 | { |
---|
871 | BonoboPlugPrivate *priv; |
---|
872 | |
---|
873 | g_return_if_fail (plug != NULL); |
---|
874 | g_return_if_fail (BONOBO_IS_PLUG (plug)); |
---|
875 | |
---|
876 | priv = plug->priv; |
---|
877 | |
---|
878 | if (GTK_WINDOW (plug)->focus_widget) { |
---|
879 | GtkWidget *parent; |
---|
880 | |
---|
881 | parent = GTK_WINDOW (plug)->focus_widget->parent; |
---|
882 | while (parent) { |
---|
883 | gtk_container_set_focus_child (GTK_CONTAINER (parent), NULL); |
---|
884 | parent = parent->parent; |
---|
885 | } |
---|
886 | |
---|
887 | gtk_window_set_focus (GTK_WINDOW (plug), NULL); |
---|
888 | } |
---|
889 | } |
---|