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