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