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