source: trunk/third/bonobo/bonobo/bonobo-plug.c @ 16855

Revision 16855, 22.7 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16854, which included commits to RCS files with non-trunk default branches.
Line 
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 */
36struct _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
68struct KeyEvent {
69        guint32 time;
70        guint keycode;
71        guint state;
72};
73
74struct 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 */
79static int key_event_table_head;
80static int key_event_table_tail;
81static int key_event_table_nelements;
82
83
84static GtkWindowClass *parent_class = NULL;
85
86static 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 */
99void
100bonobo_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 */
127GtkWidget*
128bonobo_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 */
138static void
139bonobo_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
157static void
158bonobo_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. */
180static gboolean
181is_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 */
200static GdkFilterReturn
201event_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 */
268static void
269install_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 */
301static void
302bonobo_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
310static void
311bonobo_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 */
377static struct KeyEvent *
378lookup_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 */
404static void
405bonobo_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 */
451static gint
452bonobo_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 */
571static gint
572bonobo_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 */
608static gint
609bonobo_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 */
640static gint
641bonobo_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 */
670static gint
671bonobo_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 */
721static void
722bonobo_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
750static void
751bonobo_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 */
771static void
772setup_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
780static void
781bonobo_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
812guint
813bonobo_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 **/
844void
845bonobo_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 **/
868void
869bonobo_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}
Note: See TracBrowser for help on using the repository browser.