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