]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gail.c
build: Drop --without-atk-bridge option
[~andy/gtk] / gtk / a11y / gail.c
1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2001 Sun Microsystems Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 #include <gdk/gdk.h>
24 #include <gtk/gtkx.h>
25 #include <gtk/gtkentry.h>
26 #include <gtk/gtknotebook.h>
27 #include <gtk/gtkmenuitem.h>
28 #include <gtk/gtkmenu.h>
29 #include <gtk/gtkmenubar.h>
30 #include <gtk/gtktogglebutton.h>
31 #include <gtk/gtkcombobox.h>
32 #include <gtk/gtkaccessible.h>
33 #include "gailutil.h"
34 #include "gailmisc.h"
35
36 #ifdef GDK_WINDOWING_X11
37 #include <atk-bridge.h>
38 #endif
39
40 static gboolean gail_focus_watcher      (GSignalInvocationHint *ihint,
41                                          guint                  n_param_values,
42                                          const GValue          *param_values,
43                                          gpointer               data);
44 static gboolean gail_select_watcher     (GSignalInvocationHint *ihint,
45                                          guint                  n_param_values,
46                                          const GValue          *param_values,
47                                          gpointer               data);
48 static gboolean gail_deselect_watcher   (GSignalInvocationHint *ihint,
49                                          guint                  n_param_values,
50                                          const GValue          *param_values,
51                                          gpointer               data);
52 static gboolean gail_switch_page_watcher(GSignalInvocationHint *ihint,
53                                          guint                  n_param_values,
54                                          const GValue          *param_values,
55                                          gpointer               data);
56 static void     gail_finish_select       (GtkWidget            *widget);
57 static void     gail_map_cb              (GtkWidget            *widget);
58 static void     gail_map_submenu_cb      (GtkWidget            *widget);
59 static gint     gail_focus_idle_handler  (gpointer             data);
60 static void     gail_focus_notify        (GtkWidget            *widget);
61 static void     gail_focus_notify_when_idle (GtkWidget            *widget);
62
63 static void     gail_focus_tracker_init (void);
64 static void     gail_focus_object_destroyed (gpointer data);
65 static void     gail_focus_tracker (AtkObject *object);
66 static void     gail_set_focus_widget (GtkWidget *focus_widget,
67                                        GtkWidget *widget); 
68 static void     gail_set_focus_object (AtkObject *focus_obj,
69                                        AtkObject *obj);
70
71 GtkWidget* _focus_widget = NULL;
72 static GtkWidget* next_focus_widget = NULL;
73 static gboolean was_deselect = FALSE;
74 static GtkWidget* subsequent_focus_widget = NULL;
75 static GtkWidget* focus_before_menu = NULL;
76 static guint focus_notify_handler = 0;    
77 static guint focus_tracker_id = 0;
78 static GQuark quark_focus_object = 0;
79
80 static AtkObject*
81 gail_get_accessible_for_widget (GtkWidget *widget,
82                                 gboolean  *transient)
83 {
84   AtkObject *obj = NULL;
85
86   *transient = FALSE;
87   if (!widget)
88     return NULL;
89
90   if (GTK_IS_ENTRY (widget))
91     ;
92   else if (GTK_IS_NOTEBOOK (widget)) 
93     {
94       GtkNotebook *notebook;
95       gint page_num = -1;
96
97       notebook = GTK_NOTEBOOK (widget);
98       page_num = gtk_notebook_get_current_page (notebook);
99       if (page_num != -1)
100         {
101           obj = gtk_widget_get_accessible (widget);
102           obj = atk_object_ref_accessible_child (obj, page_num);
103           g_object_unref (obj);
104         }
105     }
106   else if (GTK_IS_TOGGLE_BUTTON (widget))
107     {
108       GtkWidget *other_widget = gtk_widget_get_parent (widget);
109       if (GTK_IS_COMBO_BOX (other_widget))
110         {
111           gail_set_focus_widget (other_widget, widget);
112           widget = other_widget;
113         }
114     }
115   if (obj == NULL)
116     {
117       AtkObject *focus_object;
118
119       obj = gtk_widget_get_accessible (widget);
120       focus_object = g_object_get_qdata (G_OBJECT (obj), quark_focus_object);
121       /*
122        * We check whether the object for this focus_object has been deleted.
123        * This can happen when navigating to an empty directory in nautilus. 
124        * See bug #141907.
125        */
126       if (ATK_IS_GOBJECT_ACCESSIBLE (focus_object))
127         {
128           if (!atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (focus_object)))
129             focus_object = NULL;
130         }
131       if (focus_object)
132         obj = focus_object;
133     }
134
135   return obj;
136 }
137
138 static gboolean
139 gail_focus_watcher (GSignalInvocationHint *ihint,
140                     guint                  n_param_values,
141                     const GValue          *param_values,
142                     gpointer               data)
143 {
144   GObject *object;
145   GtkWidget *widget;
146   GdkEvent *event;
147
148   object = g_value_get_object (param_values + 0);
149   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
150
151   event = g_value_get_boxed (param_values + 1);
152   widget = GTK_WIDGET (object);
153
154   if (event->type == GDK_FOCUS_CHANGE) 
155     {
156       if (event->focus_change.in)
157         {
158           if (GTK_IS_WINDOW (widget))
159             {
160               GtkWidget *focus_widget;
161               GtkWindow *window;
162               GtkWindowType type;
163
164               window = GTK_WINDOW (widget);
165               focus_widget = gtk_window_get_focus (window);
166               g_object_get (window, "type", &type, NULL);
167
168               if (focus_widget)
169                 {
170                   /*
171                    * If we already have a potential focus widget set this
172                    * windows's focus widget to focus_before_menu so that 
173                    * it will be reported when menu item is unset.
174                    */
175                   if (next_focus_widget)
176                     {
177                       if (GTK_IS_MENU_ITEM (next_focus_widget) &&
178                           !focus_before_menu)
179                         {
180                           void *vp_focus_before_menu = &focus_before_menu;
181                           focus_before_menu = focus_widget;
182                           g_object_add_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
183                         }
184
185                       return TRUE;
186                     }
187                   widget = focus_widget;
188                 }
189               else if (type == GTK_WINDOW_POPUP)
190                 {
191                   if (GTK_IS_BIN (widget))
192                     {
193                       GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
194
195                       if (GTK_IS_WIDGET (child) && gtk_widget_has_grab (child))
196                         {
197                           if (GTK_IS_MENU_SHELL (child))
198                             {
199                               if (gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (child)))
200                                 {
201                                   /*
202                                    * We have a menu which has a menu item selected
203                                    * so we do not report focus on the menu.
204                                    */ 
205                                   return TRUE; 
206                                 }
207                             }
208                           widget = child;
209                         } 
210                     }
211                   else /* popup window has no children; this edge case occurs in some custom code (OOo for instance) */
212                     {
213                       return TRUE;
214                     }
215                 }
216               else /* Widget is a non-popup toplevel with no focus children; 
217                       don't emit for this case either, as it's useless */
218                 {
219                   return TRUE;
220                 }
221             }
222         }
223       else
224         {
225           if (next_focus_widget)
226             {
227                GtkWidget *toplevel;
228
229                toplevel = gtk_widget_get_toplevel (next_focus_widget);
230                if (toplevel == widget)
231                  next_focus_widget = NULL; 
232             }
233           /* focus out */
234           widget = NULL;
235         }
236     }
237   else
238     {
239       if (event->type == GDK_MOTION_NOTIFY && gtk_widget_has_focus (widget))
240         {
241           if (widget == _focus_widget)
242             {
243               return TRUE;
244             }
245         }
246       else
247         {
248           return TRUE;
249         }
250     }
251
252 #ifdef GDK_WINDOWING_X11
253   /*
254    * If the focus widget is a GtkSocket without a plug
255    * then ignore the focus notification as the embedded
256    * plug will report a focus notification.
257    */
258   if (GTK_IS_SOCKET (widget) &&
259       gtk_socket_get_plug_window (GTK_SOCKET (widget)) != NULL)
260     return TRUE;
261 #endif
262
263   /*
264    * The widget may not yet be visible on the screen so we wait until it is.
265    */
266   gail_focus_notify_when_idle (widget);
267   return TRUE; 
268 }
269
270 static gboolean
271 gail_select_watcher (GSignalInvocationHint *ihint,
272                      guint                  n_param_values,
273                      const GValue          *param_values,
274                      gpointer               data)
275 {
276   GObject *object;
277   GtkWidget *widget;
278
279   object = g_value_get_object (param_values + 0);
280   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
281
282   widget = GTK_WIDGET (object);
283
284   if (!gtk_widget_get_mapped (widget))
285     {
286       g_signal_connect (widget, "map",
287                         G_CALLBACK (gail_map_cb),
288                         NULL);
289     }
290   else
291     gail_finish_select (widget);
292
293   return TRUE;
294 }
295
296 static void
297 gail_finish_select (GtkWidget *widget)
298 {
299   if (GTK_IS_MENU_ITEM (widget))
300     {
301       GtkMenuItem* menu_item;
302       GtkWidget *submenu;
303
304       menu_item = GTK_MENU_ITEM (widget);
305       submenu = gtk_menu_item_get_submenu (menu_item);
306       if (submenu &&
307           !gtk_widget_get_mapped (submenu))
308         {
309           /*
310            * If the submenu is not visble, wait until it is before
311            * reporting focus on the menu item.
312            */
313           gulong handler_id;
314
315           handler_id = g_signal_handler_find (submenu,
316                                               G_SIGNAL_MATCH_FUNC,
317                                               g_signal_lookup ("map",
318                                                                GTK_TYPE_WINDOW),
319                                               0,
320                                               NULL,
321                                               (gpointer) gail_map_submenu_cb,
322                                               NULL); 
323           if (!handler_id)
324             g_signal_connect (submenu, "map",
325                               G_CALLBACK (gail_map_submenu_cb),
326                               NULL);
327             return;
328         }
329       /*
330        * If we are waiting to report focus on a menubar or a menu item
331        * because of a previous deselect, cancel it.
332        */
333       if (was_deselect &&
334           focus_notify_handler &&
335           next_focus_widget &&
336           (GTK_IS_MENU_BAR (next_focus_widget) ||
337            GTK_IS_MENU_ITEM (next_focus_widget)))
338         {
339           void *vp_next_focus_widget = &next_focus_widget;
340           g_source_remove (focus_notify_handler);
341           g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
342           next_focus_widget = NULL;
343           focus_notify_handler = 0;
344           was_deselect = FALSE;
345         }
346     } 
347   /*
348    * If previously focused widget is not a GtkMenuItem or a GtkMenu,
349    * keep track of it so we can return to it after menubar is deactivated
350    */
351   if (_focus_widget && 
352       !GTK_IS_MENU_ITEM (_focus_widget) && 
353       !GTK_IS_MENU (_focus_widget))
354     {
355       void *vp_focus_before_menu = &focus_before_menu;
356       focus_before_menu = _focus_widget;
357       g_object_add_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
358
359     } 
360   gail_focus_notify_when_idle (widget);
361
362   return; 
363 }
364
365 static void
366 gail_map_cb (GtkWidget *widget)
367 {
368   gail_finish_select (widget);
369 }
370
371 static void
372 gail_map_submenu_cb (GtkWidget *widget)
373 {
374   if (GTK_IS_MENU (widget))
375     {
376       GtkWidget *parent_menu_item;
377
378       parent_menu_item = gtk_menu_get_attach_widget (GTK_MENU (widget));
379       if (parent_menu_item)
380         gail_finish_select (parent_menu_item);
381     }
382 }
383
384
385 static gboolean
386 gail_deselect_watcher (GSignalInvocationHint *ihint,
387                        guint                  n_param_values,
388                        const GValue          *param_values,
389                        gpointer               data)
390 {
391   GObject *object;
392   GtkWidget *widget;
393   GtkWidget *menu_shell;
394
395   object = g_value_get_object (param_values + 0);
396   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
397
398   widget = GTK_WIDGET (object);
399
400   if (!GTK_IS_MENU_ITEM (widget))
401     return TRUE;
402
403   if (subsequent_focus_widget == widget)
404     subsequent_focus_widget = NULL;
405
406   menu_shell = gtk_widget_get_parent (widget);
407   if (GTK_IS_MENU_SHELL (menu_shell))
408     {
409       GtkWidget *parent_menu_shell;
410
411       parent_menu_shell = gtk_menu_shell_get_parent_shell (GTK_MENU_SHELL (menu_shell));
412       if (parent_menu_shell)
413         {
414           GtkWidget *active_menu_item;
415
416           active_menu_item = gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (parent_menu_shell));
417           if (active_menu_item)
418             {
419               gail_focus_notify_when_idle (active_menu_item);
420             }
421         }
422       else
423         {
424           if (!GTK_IS_MENU_BAR (menu_shell))
425             {
426               gail_focus_notify_when_idle (menu_shell);
427             }
428         }
429     }
430   was_deselect = TRUE;
431   return TRUE; 
432 }
433
434 static gboolean 
435 gail_switch_page_watcher (GSignalInvocationHint *ihint,
436                           guint                  n_param_values,
437                           const GValue          *param_values,
438                           gpointer               data)
439 {
440   GObject *object;
441   GtkWidget *widget;
442
443   object = g_value_get_object (param_values + 0);
444   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
445
446   widget = GTK_WIDGET (object);
447
448   if (!GTK_IS_NOTEBOOK (widget))
449     return TRUE;
450
451   if (gtk_notebook_get_current_page (GTK_NOTEBOOK (widget)) == -1)
452     return TRUE;
453
454   gail_focus_notify_when_idle (widget);
455   return TRUE;
456 }
457
458 static gboolean
459 gail_focus_idle_handler (gpointer data)
460 {
461   focus_notify_handler = 0;
462   /*
463    * The widget which was to receive focus may have been removed
464    */
465   if (!next_focus_widget)
466     {
467       if (next_focus_widget != data)
468         return FALSE;
469     }
470   else
471     {
472       void *vp_next_focus_widget = &next_focus_widget;
473       g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
474       next_focus_widget = NULL;
475     }
476     
477   gail_focus_notify (data);
478
479   return FALSE;
480 }
481
482 static void
483 gail_focus_notify (GtkWidget *widget)
484 {
485   AtkObject *atk_obj;
486   gboolean transient;
487
488   if (widget != _focus_widget)
489     {
490       if (_focus_widget)
491         {
492           void *vp_focus_widget = &_focus_widget;
493           g_object_remove_weak_pointer (G_OBJECT (_focus_widget), vp_focus_widget);
494         }
495       _focus_widget = widget;
496       if (_focus_widget)
497         {
498           void *vp_focus_widget = &_focus_widget;
499           g_object_add_weak_pointer (G_OBJECT (_focus_widget), vp_focus_widget);
500           /*
501            * The UI may not have been updated yet; e.g. in gtkhtml2
502            * html_view_layout() is called in a idle handler
503            */
504           if (_focus_widget == focus_before_menu)
505             {
506               void *vp_focus_before_menu = &focus_before_menu;
507               g_object_remove_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
508               focus_before_menu = NULL;
509             }
510         }
511       gail_focus_notify_when_idle (_focus_widget);
512     }
513   else
514     {
515       if (_focus_widget)
516         atk_obj  = gail_get_accessible_for_widget (_focus_widget, &transient);
517       else
518         atk_obj = NULL;
519       /*
520        * Do not report focus on redundant object
521        */
522       if (atk_obj && 
523           (atk_object_get_role(atk_obj) != ATK_ROLE_REDUNDANT_OBJECT))
524           atk_focus_tracker_notify (atk_obj);
525       if (atk_obj && transient)
526         g_object_unref (atk_obj);
527       if (subsequent_focus_widget)
528         {
529           GtkWidget *tmp_widget = subsequent_focus_widget;
530           subsequent_focus_widget = NULL;
531           gail_focus_notify_when_idle (tmp_widget);
532         }
533     }
534 }
535
536 static void
537 gail_focus_notify_when_idle (GtkWidget *widget)
538 {
539   if (focus_notify_handler)
540     {
541       if (widget)
542         {
543           /*
544            * Ignore focus request when menu item is going to be focused.
545            * See bug #124232.
546            */
547           if (GTK_IS_MENU_ITEM (next_focus_widget) && !GTK_IS_MENU_ITEM (widget))
548             return;
549
550           if (next_focus_widget)
551             {
552               if (GTK_IS_MENU_ITEM (next_focus_widget) && GTK_IS_MENU_ITEM (widget))
553                 {
554                   if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (next_focus_widget)) == gtk_widget_get_parent (widget))
555                     {
556                       if (subsequent_focus_widget)
557                         g_assert_not_reached ();
558                       subsequent_focus_widget = widget;
559                       return;
560                     } 
561                 }
562             }
563           g_source_remove (focus_notify_handler);
564           if (next_focus_widget)
565             {
566               void *vp_next_focus_widget = &next_focus_widget;
567               g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
568               next_focus_widget = NULL;
569             }
570         }
571       else
572         /*
573          * Ignore if focus is being set to NULL and we are waiting to set focus
574          */
575         return;
576     }
577
578   if (widget)
579     {
580       void *vp_next_focus_widget = &next_focus_widget;
581       next_focus_widget = widget;
582       g_object_add_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
583     }
584   else
585     {
586       /*
587        * We are about to report focus as NULL so remove the weak pointer
588        * for the widget we were waiting to report focus on.
589        */ 
590       if (next_focus_widget)
591         {
592           void *vp_next_focus_widget = &next_focus_widget;
593           g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
594           next_focus_widget = NULL;
595         }
596     }
597
598   focus_notify_handler = gdk_threads_add_idle (gail_focus_idle_handler, widget);
599 }
600
601 static gboolean
602 gail_deactivate_watcher (GSignalInvocationHint *ihint,
603                          guint                  n_param_values,
604                          const GValue          *param_values,
605                          gpointer               data)
606 {
607   GObject *object;
608   GtkWidget *widget;
609   GtkMenuShell *shell;
610   GtkWidget *focus = NULL;
611
612   object = g_value_get_object (param_values + 0);
613   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
614   widget = GTK_WIDGET (object);
615
616   g_return_val_if_fail (GTK_IS_MENU_SHELL(widget), TRUE);
617   shell = GTK_MENU_SHELL(widget);
618   if (! gtk_menu_shell_get_parent_shell (shell))
619     focus = focus_before_menu;
620       
621   /*
622    * If we are waiting to report focus on a menubar or a menu item
623    * because of a previous deselect, cancel it.
624    */
625   if (was_deselect &&
626       focus_notify_handler &&
627       next_focus_widget &&
628       (GTK_IS_MENU_BAR (next_focus_widget) ||
629        GTK_IS_MENU_ITEM (next_focus_widget)))
630     {
631       void *vp_next_focus_widget = &next_focus_widget;
632       g_source_remove (focus_notify_handler);
633       g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
634       next_focus_widget = NULL;
635       focus_notify_handler = 0;
636       was_deselect = FALSE;
637     }
638   gail_focus_notify_when_idle (focus);
639
640   return TRUE; 
641 }
642
643 static void
644 gail_focus_tracker_init (void)
645 {
646   static gboolean  emission_hooks_added = FALSE;
647
648   if (!emission_hooks_added)
649     {
650       /*
651        * We cannot be sure that the classes exist so we make sure that they do.
652        */
653       g_type_class_ref (GTK_TYPE_WIDGET);
654       g_type_class_ref (GTK_TYPE_MENU_ITEM);
655       g_type_class_ref (GTK_TYPE_MENU_SHELL);
656       g_type_class_ref (GTK_TYPE_NOTEBOOK);
657
658       /*
659        * We listen for event_after signal and then check that the
660        * event was a focus in event so we get called after the event.
661        */
662       g_signal_add_emission_hook (
663              g_signal_lookup ("event-after", GTK_TYPE_WIDGET), 0,
664              gail_focus_watcher, NULL, (GDestroyNotify) NULL);
665       /*
666        * A "select" signal is emitted when arrow key is used to
667        * move to a list item in the popup window of a GtkCombo or
668        * a menu item in a menu.
669        */
670       g_signal_add_emission_hook (
671              g_signal_lookup ("select", GTK_TYPE_MENU_ITEM), 0,
672              gail_select_watcher, NULL, (GDestroyNotify) NULL);
673
674       /*
675        * A "deselect" signal is emitted when arrow key is used to
676        * move from a menu item in a menu to the parent menu.
677        */
678       g_signal_add_emission_hook (
679              g_signal_lookup ("deselect", GTK_TYPE_MENU_ITEM), 0,
680              gail_deselect_watcher, NULL, (GDestroyNotify) NULL);
681
682       /*
683        * We listen for deactivate signals on menushells to determine
684        * when the "focus" has left the menus.
685        */
686       g_signal_add_emission_hook (
687              g_signal_lookup ("deactivate", GTK_TYPE_MENU_SHELL), 0,
688              gail_deactivate_watcher, NULL, (GDestroyNotify) NULL);
689
690       /*
691        * We listen for "switch-page" signal on a GtkNotebook to notify
692        * when page has changed because of clicking on a notebook tab.
693        */
694       g_signal_add_emission_hook (
695              g_signal_lookup ("switch-page", GTK_TYPE_NOTEBOOK), 0,
696              gail_switch_page_watcher, NULL, (GDestroyNotify) NULL);
697       emission_hooks_added = TRUE;
698     }
699 }
700
701 static void
702 gail_focus_object_destroyed (gpointer data)
703 {
704   GObject *obj;
705
706   obj = G_OBJECT (data);
707   g_object_set_qdata (obj, quark_focus_object, NULL);
708   g_object_unref (obj); 
709 }
710
711 static void
712 gail_focus_tracker (AtkObject *focus_object)
713 {
714   /*
715    * Do not report focus on redundant object
716    */
717   if (focus_object && 
718       (atk_object_get_role(focus_object) != ATK_ROLE_REDUNDANT_OBJECT))
719     {
720       AtkObject *old_focus_object;
721
722       if (!GTK_IS_ACCESSIBLE (focus_object))
723         {
724           AtkObject *parent;
725
726           parent = focus_object;
727           while (1)
728             {
729               parent = atk_object_get_parent (parent);
730               if (parent == NULL)
731                 break;
732               if (GTK_IS_ACCESSIBLE (parent))
733                 break;
734             }
735
736           if (parent)
737             {
738               gail_set_focus_object (focus_object, parent);
739             }
740         }
741       else
742         {
743           old_focus_object = g_object_get_qdata (G_OBJECT (focus_object), quark_focus_object);
744           if (old_focus_object)
745             {
746               g_object_weak_unref (G_OBJECT (old_focus_object),
747                                    (GWeakNotify) gail_focus_object_destroyed,
748                                    focus_object);
749               g_object_set_qdata (G_OBJECT (focus_object), quark_focus_object, NULL);
750               g_object_unref (G_OBJECT (focus_object));
751             }
752         }
753     }
754 }
755
756 static void 
757 gail_set_focus_widget (GtkWidget *focus_widget,
758                        GtkWidget *widget)
759 {
760   AtkObject *focus_obj;
761   AtkObject *obj;
762
763   focus_obj = gtk_widget_get_accessible (focus_widget);
764   obj = gtk_widget_get_accessible (widget);
765   gail_set_focus_object (focus_obj, obj);
766 }
767
768 static void 
769 gail_set_focus_object (AtkObject *focus_obj,
770                        AtkObject *obj)
771 {
772   AtkObject *old_focus_obj;
773
774   old_focus_obj = g_object_get_qdata (G_OBJECT (obj), quark_focus_object);
775   if (old_focus_obj != obj)
776     {
777       if (old_focus_obj)
778         g_object_weak_unref (G_OBJECT (old_focus_obj),
779                              (GWeakNotify) gail_focus_object_destroyed,
780                              obj);
781       else
782         /*
783          * We call g_object_ref as if obj is destroyed 
784          * while the weak reference exists then destroying the 
785          * focus_obj would cause gail_focus_object_destroyed to be 
786          * called when obj is not a valid GObject.
787          */
788         g_object_ref (obj);
789
790       g_object_weak_ref (G_OBJECT (focus_obj),
791                          (GWeakNotify) gail_focus_object_destroyed,
792                          obj);
793       g_object_set_qdata (G_OBJECT (obj), quark_focus_object, focus_obj);
794     }
795 }
796
797 void
798 _gtk_accessibility_init (void)
799 {
800   static int initialized = FALSE;
801
802   if (initialized)
803     return;
804
805   initialized = TRUE;
806   quark_focus_object = g_quark_from_static_string ("gail-focus-object");
807
808   atk_focus_tracker_init (gail_focus_tracker_init);
809   focus_tracker_id = atk_add_focus_tracker (gail_focus_tracker);
810
811   _gail_util_install ();
812 #ifdef GDK_WINDOWING_X11
813   atk_bridge_adaptor_init (NULL, NULL);
814 #endif
815
816   atk_misc_instance = g_object_new (GAIL_TYPE_MISC, NULL);
817 }