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