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