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