]> Pileus Git - ~andy/gtk/blob - modules/other/gail/gail.c
Remove relocations from the atk factories. Remove unused gail_foo_new()
[~andy/gtk] / modules / other / gail / 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 #include <atk/atk.h>
25 #include <gtk/gtk.h>
26 #include "gail.h"
27 #include "gailfactory.h"
28
29 #define GNOME_ACCESSIBILITY_ENV "GNOME_ACCESSIBILITY"
30
31 static gboolean gail_focus_watcher      (GSignalInvocationHint *ihint,
32                                          guint                  n_param_values,
33                                          const GValue          *param_values,
34                                          gpointer               data);
35 static gboolean gail_select_watcher     (GSignalInvocationHint *ihint,
36                                          guint                  n_param_values,
37                                          const GValue          *param_values,
38                                          gpointer               data);
39 static gboolean gail_deselect_watcher   (GSignalInvocationHint *ihint,
40                                          guint                  n_param_values,
41                                          const GValue          *param_values,
42                                          gpointer               data);
43 static gboolean gail_switch_page_watcher(GSignalInvocationHint *ihint,
44                                          guint                  n_param_values,
45                                          const GValue          *param_values,
46                                          gpointer               data);
47 static AtkObject* gail_get_accessible_for_widget (GtkWidget    *widget,
48                                                   gboolean     *transient);
49 static void     gail_finish_select       (GtkWidget            *widget);
50 static void     gail_map_cb              (GtkWidget            *widget);
51 static void     gail_map_submenu_cb      (GtkWidget            *widget);
52 static gint     gail_focus_idle_handler  (gpointer             data);
53 static void     gail_focus_notify        (GtkWidget            *widget);
54 static void     gail_focus_notify_when_idle (GtkWidget            *widget);
55
56 static void     gail_focus_tracker_init (void);
57 static void     gail_focus_object_destroyed (gpointer data);
58 static void     gail_focus_tracker (AtkObject *object);
59 static void     gail_set_focus_widget (GtkWidget *focus_widget,
60                                        GtkWidget *widget); 
61 static void     gail_set_focus_object (AtkObject *focus_obj,
62                                        AtkObject *obj);
63
64 GtkWidget* focus_widget = NULL;
65 static GtkWidget* next_focus_widget = NULL;
66 static gboolean was_deselect = FALSE;
67 static GtkWidget* subsequent_focus_widget = NULL;
68 static GtkWidget* focus_before_menu = NULL;
69 static guint focus_notify_handler = 0;    
70 static guint focus_tracker_id = 0;
71 static GQuark quark_focus_object = 0;
72
73 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_OBJECT, GailObject, gail_object, GTK_TYPE_OBJECT)
74 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_WIDGET, GailWidget, gail_widget, GTK_TYPE_WIDGET)
75 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_CONTAINER, GailContainer, gail_container, GTK_TYPE_CONTAINER)
76 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_BUTTON, GailButton, gail_button, GTK_TYPE_BUTTON)
77 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_ITEM, GailItem, gail_item, GTK_TYPE_ITEM)
78 GAIL_IMPLEMENT_FACTORY_WITH_FUNC (GAIL_TYPE_MENU_ITEM, GailMenuItem, gail_menu_item, gail_menu_item_new)
79 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_TOGGLE_BUTTON, GailToggleButton, gail_toggle_button, GTK_TYPE_TOGGLE_BUTTON)
80 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_IMAGE, GailImage, gail_image, GTK_TYPE_IMAGE)
81 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_TEXT_VIEW, GailTextView, gail_text_view, GTK_TYPE_TEXT_VIEW)
82 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_COMBO, GailCombo, gail_combo, GTK_TYPE_COMBO)
83 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_COMBO_BOX, GailComboBox, gail_combo_box, GTK_TYPE_COMBO_BOX)
84 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_ENTRY, GailEntry, gail_entry, GTK_TYPE_ENTRY)
85 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_MENU_SHELL, GailMenuShell, gail_menu_shell, GTK_TYPE_MENU_SHELL)
86 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_MENU, GailMenu, gail_menu, GTK_TYPE_MENU)
87 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_WINDOW, GailWindow, gail_window, GTK_TYPE_BIN)
88 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_RANGE, GailRange, gail_range, GTK_TYPE_RANGE)
89 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_SCALE, GailScale, gail_scale, GTK_TYPE_SCALE)
90 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_CLIST, GailCList, gail_clist, GTK_TYPE_CLIST)
91 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_LABEL, GailLabel, gail_label, GTK_TYPE_LABEL)
92 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_STATUSBAR, GailStatusbar, gail_statusbar, GTK_TYPE_STATUSBAR)
93 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_NOTEBOOK, GailNotebook, gail_notebook, GTK_TYPE_NOTEBOOK)
94 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_CALENDAR, GailCalendar, gail_calendar, GTK_TYPE_CALENDAR)
95 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_PROGRESS_BAR, GailProgressBar, gail_progress_bar, GTK_TYPE_PROGRESS_BAR)
96 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_SPIN_BUTTON, GailSpinButton, gail_spin_button, GTK_TYPE_SPIN_BUTTON)
97 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_TREE_VIEW, GailTreeView, gail_tree_view, GTK_TYPE_TREE_VIEW)
98 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_FRAME, GailFrame, gail_frame, GTK_TYPE_FRAME)
99 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_RADIO_BUTTON, GailRadioButton, gail_radio_button, GTK_TYPE_RADIO_BUTTON)
100 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_ARROW, GailArrow, gail_arrow, GTK_TYPE_ARROW)
101 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_PIXMAP, GailPixmap, gail_pixmap, GTK_TYPE_PIXMAP)
102 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_SEPARATOR, GailSeparator, gail_separator, GTK_TYPE_SEPARATOR)
103 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_BOX, GailBox, gail_box, GTK_TYPE_BOX)
104 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_SCROLLED_WINDOW, GailScrolledWindow, gail_scrolled_window, GTK_TYPE_SCROLLED_WINDOW)
105 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_LIST, GailList, gail_list, GTK_TYPE_LIST)
106 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_PANED, GailPaned, gail_paned, GTK_TYPE_PANED)
107 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_SCROLLBAR, GailScrollbar, gail_scrollbar, GTK_TYPE_SCROLLBAR)
108 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_OPTION_MENU, GailOptionMenu, gail_option_menu, GTK_TYPE_OPTION_MENU)
109 GAIL_IMPLEMENT_FACTORY_WITH_FUNC (GAIL_TYPE_CHECK_MENU_ITEM, GailCheckMenuItem, gail_check_menu_item, gail_check_menu_item_new)
110 GAIL_IMPLEMENT_FACTORY_WITH_FUNC (GAIL_TYPE_RADIO_MENU_ITEM, GailRadioMenuItem, gail_radio_menu_item, gail_radio_menu_item_new)
111 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_EXPANDER, GailExpander, gail_expander, GTK_TYPE_EXPANDER)
112 GAIL_IMPLEMENT_FACTORY_WITH_FUNC_DUMMY (GAIL_TYPE_RENDERER_CELL, GailRendererCell, gail_renderer_cell, GTK_TYPE_CELL_RENDERER, gail_renderer_cell_new)
113 GAIL_IMPLEMENT_FACTORY_WITH_FUNC_DUMMY (GAIL_TYPE_BOOLEAN_CELL, GailBooleanCell, gail_boolean_cell, GTK_TYPE_CELL_RENDERER_TOGGLE, gail_boolean_cell_new)
114 GAIL_IMPLEMENT_FACTORY_WITH_FUNC_DUMMY (GAIL_TYPE_IMAGE_CELL, GailImageCell, gail_image_cell, GTK_TYPE_CELL_RENDERER_PIXBUF, gail_image_cell_new)
115 GAIL_IMPLEMENT_FACTORY_WITH_FUNC_DUMMY (GAIL_TYPE_TEXT_CELL, GailTextCell, gail_text_cell, GTK_TYPE_CELL_RENDERER_TEXT, gail_text_cell_new)
116
117 static AtkObject*
118 gail_get_accessible_for_widget (GtkWidget *widget,
119                                 gboolean  *transient)
120 {
121   AtkObject *obj = NULL;
122   GType gnome_canvas;
123
124   gnome_canvas = g_type_from_name ("GnomeCanvas");
125
126   *transient = FALSE;
127   if (!widget)
128     return NULL;
129
130   if (GTK_IS_ENTRY (widget))
131     {
132       GtkWidget *other_widget = widget->parent;
133       if (GTK_IS_COMBO (other_widget))
134         {
135           gail_set_focus_widget (other_widget, widget);
136           widget = other_widget;
137         }
138     } 
139   else if (GTK_IS_NOTEBOOK (widget)) 
140     {
141       GtkNotebook *notebook;
142       gint page_num = -1;
143
144       notebook = GTK_NOTEBOOK (widget);
145       /*
146        * Report the currently focused tab rather than the currently selected tab
147        */
148       if (notebook->focus_tab)
149         {
150           page_num = g_list_index (notebook->children, notebook->focus_tab->data);
151         }
152       if (page_num != -1)
153         {
154           obj = gtk_widget_get_accessible (widget);
155           obj = atk_object_ref_accessible_child (obj, page_num);
156           g_object_unref (obj);
157         }
158     }
159   else if (GTK_CHECK_TYPE ((widget), gnome_canvas))
160     {
161       GObject *focused_item;
162       GValue value = {0, };
163
164       g_value_init (&value, G_TYPE_OBJECT);
165       g_object_get_property (G_OBJECT (widget), "focused_item", &value);
166       focused_item = g_value_get_object (&value);
167
168       if (focused_item)
169         {
170           AtkObject *tmp;
171
172           obj = atk_gobject_accessible_for_object (G_OBJECT (focused_item));
173           tmp = g_object_get_qdata (G_OBJECT (obj), quark_focus_object);
174           if (tmp != NULL)
175             obj = tmp;
176         }
177     }
178   else if (GTK_IS_TOGGLE_BUTTON (widget))
179     {
180       GtkWidget *other_widget = widget->parent;
181       if (GTK_IS_COMBO_BOX (other_widget))
182         {
183           gail_set_focus_widget (other_widget, widget);
184           widget = other_widget;
185         }
186     }
187   if (obj == NULL)
188     {
189       AtkObject *focus_object;
190
191       obj = gtk_widget_get_accessible (widget);
192       focus_object = g_object_get_qdata (G_OBJECT (obj), quark_focus_object);
193       /*
194        * We check whether the object for this focus_object has been deleted.
195        * This can happen when navigating to an empty directory in nautilus. 
196        * See bug #141907.
197        */
198       if (ATK_IS_GOBJECT_ACCESSIBLE (focus_object))
199         {
200           if (!atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (focus_object)))
201             focus_object = NULL;
202         }
203       if (focus_object)
204         obj = focus_object;
205     }
206
207   return obj;
208 }
209
210 static gboolean
211 gail_focus_watcher (GSignalInvocationHint *ihint,
212                     guint                  n_param_values,
213                     const GValue          *param_values,
214                     gpointer               data)
215 {
216   GObject *object;
217   GtkWidget *widget;
218   GdkEvent *event;
219
220   object = g_value_get_object (param_values + 0);
221   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
222
223   event = g_value_get_boxed (param_values + 1);
224   widget = GTK_WIDGET (object);
225
226   if (event->type == GDK_FOCUS_CHANGE) 
227     {
228       if (event->focus_change.in)
229         {
230           if (GTK_IS_WINDOW (widget))
231             {
232               GtkWindow *window;
233
234               window = GTK_WINDOW (widget);
235               if (window->focus_widget)
236                 {
237                   /*
238                    * If we already have a potential focus widget set this
239                    * windows's focus widget to focus_before_menu so that 
240                    * it will be reported when menu item is unset.
241                    */
242                   if (next_focus_widget)
243                     {
244                       if (GTK_IS_MENU_ITEM (next_focus_widget) &&
245                           !focus_before_menu)
246                         {
247                           void *vp_focus_before_menu = &focus_before_menu;
248                           focus_before_menu = window->focus_widget;
249                           g_object_add_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
250                         }
251
252                       return TRUE;
253                     }
254                   widget = window->focus_widget;
255                 }
256               else if (window->type == GTK_WINDOW_POPUP) 
257                 {
258                   if (GTK_IS_BIN (widget))
259                     {
260                       GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
261
262                       if (GTK_IS_WIDGET (child) && GTK_WIDGET_HAS_GRAB (child))
263                         {
264                           if (GTK_IS_MENU_SHELL (child))
265                             {
266                               if (GTK_MENU_SHELL (child)->active_menu_item)
267                                 {
268                                   /*
269                                    * We have a menu which has a menu item selected
270                                    * so we do not report focus on the menu.
271                                    */ 
272                                   return TRUE; 
273                                 }
274                             }
275                           widget = child;
276                         } 
277                     }
278                   else /* popup window has no children; this edge case occurs in some custom code (OOo for instance) */
279                     {
280                       return TRUE;
281                     }
282                 }
283               else /* Widget is a non-popup toplevel with no focus children; 
284                       don't emit for this case either, as it's useless */
285                 {
286                   return TRUE;
287                 }
288             }
289         }
290       else
291         {
292           if (next_focus_widget)
293             {
294                GtkWidget *toplevel;
295
296                toplevel = gtk_widget_get_toplevel (next_focus_widget);
297                if (toplevel == widget)
298                  next_focus_widget = NULL; 
299             }
300           /* focus out */
301           widget = NULL;
302         }
303     }
304   else
305     {
306       if (event->type == GDK_MOTION_NOTIFY && GTK_WIDGET_HAS_FOCUS (widget))
307         {
308           if (widget == focus_widget)
309             {
310               return TRUE;
311             }
312         }
313       else
314         {
315           return TRUE;
316         }
317     }
318   /*
319    * If the focus widget is a GtkSocket without a plug
320    * then ignore the focus notification as the embedded
321    * plug will report a focus notification.
322    */
323   if (GTK_IS_SOCKET (widget) &&
324       GTK_SOCKET (widget)->plug_widget == NULL)
325     return TRUE;
326   /*
327    * The widget may not yet be visible on the screen so we wait until it is.
328    */
329   gail_focus_notify_when_idle (widget);
330   return TRUE; 
331 }
332
333 static gboolean
334 gail_select_watcher (GSignalInvocationHint *ihint,
335                      guint                  n_param_values,
336                      const GValue          *param_values,
337                      gpointer               data)
338 {
339   GObject *object;
340   GtkWidget *widget;
341
342   object = g_value_get_object (param_values + 0);
343   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
344
345   widget = GTK_WIDGET (object);
346
347   if (!GTK_WIDGET_MAPPED (widget))
348     {
349       g_signal_connect (widget, "map",
350                         G_CALLBACK (gail_map_cb),
351                         NULL);
352     }
353   else
354     gail_finish_select (widget);
355
356   return TRUE;
357 }
358
359 static void
360 gail_finish_select (GtkWidget *widget)
361 {
362   if (GTK_IS_MENU_ITEM (widget))
363     {
364       GtkMenuItem* menu_item;
365
366       menu_item = GTK_MENU_ITEM (widget);
367       if (menu_item->submenu &&
368           !GTK_WIDGET_MAPPED (menu_item->submenu))
369         {
370           /*
371            * If the submenu is not visble, wait until it is before
372            * reporting focus on the menu item.
373            */
374           gulong handler_id;
375
376           handler_id = g_signal_handler_find (menu_item->submenu,
377                                               G_SIGNAL_MATCH_FUNC,
378                                               g_signal_lookup ("map",
379                                                                GTK_TYPE_WINDOW),
380                                               0,
381                                               NULL,
382                                               (gpointer) gail_map_submenu_cb,
383                                               NULL); 
384           if (!handler_id)
385             g_signal_connect (menu_item->submenu, "map",
386                               G_CALLBACK (gail_map_submenu_cb),
387                               NULL);
388             return;
389
390         }
391       /*
392        * If we are waiting to report focus on a menubar or a menu item
393        * because of a previous deselect, cancel it.
394        */
395       if (was_deselect &&
396           focus_notify_handler &&
397           next_focus_widget &&
398           (GTK_IS_MENU_BAR (next_focus_widget) ||
399            GTK_IS_MENU_ITEM (next_focus_widget)))
400         {
401           void *vp_next_focus_widget = &next_focus_widget;
402           g_source_remove (focus_notify_handler);
403           g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
404           next_focus_widget = NULL;
405           focus_notify_handler = 0;
406           was_deselect = FALSE;
407         }
408     } 
409   /*
410    * If previously focused widget is not a GtkMenuItem or a GtkMenu,
411    * keep track of it so we can return to it after menubar is deactivated
412    */
413   if (focus_widget && 
414       !GTK_IS_MENU_ITEM (focus_widget) && 
415       !GTK_IS_MENU (focus_widget))
416     {
417       void *vp_focus_before_menu = &focus_before_menu;
418       focus_before_menu = focus_widget;
419       g_object_add_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
420
421     } 
422   gail_focus_notify_when_idle (widget);
423
424   return; 
425 }
426
427 static void
428 gail_map_cb (GtkWidget *widget)
429 {
430   gail_finish_select (widget);
431 }
432
433 static void
434 gail_map_submenu_cb (GtkWidget *widget)
435 {
436   if (GTK_IS_MENU (widget))
437     {
438       if (GTK_MENU (widget)->parent_menu_item)
439         gail_finish_select (GTK_MENU (widget)->parent_menu_item);
440     }
441 }
442
443
444 static gboolean
445 gail_deselect_watcher (GSignalInvocationHint *ihint,
446                        guint                  n_param_values,
447                        const GValue          *param_values,
448                        gpointer               data)
449 {
450   GObject *object;
451   GtkWidget *widget;
452   GtkWidget *menu_shell;
453
454   object = g_value_get_object (param_values + 0);
455   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
456
457   widget = GTK_WIDGET (object);
458
459   if (!GTK_IS_MENU_ITEM (widget))
460     return TRUE;
461
462   if (subsequent_focus_widget == widget)
463     subsequent_focus_widget = NULL;
464
465   menu_shell = gtk_widget_get_parent (widget);
466   if (GTK_IS_MENU_SHELL (menu_shell))
467     {
468       GtkWidget *parent_menu_shell;
469
470       parent_menu_shell = GTK_MENU_SHELL (menu_shell)->parent_menu_shell;
471       if (parent_menu_shell)
472         {
473           GtkWidget *active_menu_item;
474
475           active_menu_item = GTK_MENU_SHELL (parent_menu_shell)->active_menu_item;
476           if (active_menu_item)
477             {
478               gail_focus_notify_when_idle (active_menu_item);
479             }
480         }
481       else
482         {
483           if (!GTK_IS_MENU_BAR (menu_shell))
484             {
485               gail_focus_notify_when_idle (menu_shell);
486             }
487         }
488     }
489   was_deselect = TRUE;
490   return TRUE; 
491 }
492
493 static gboolean 
494 gail_switch_page_watcher (GSignalInvocationHint *ihint,
495                           guint                  n_param_values,
496                           const GValue          *param_values,
497                           gpointer               data)
498 {
499   GObject *object;
500   GtkWidget *widget;
501   GtkNotebook *notebook;
502
503   object = g_value_get_object (param_values + 0);
504   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
505
506   widget = GTK_WIDGET (object);
507
508   if (!GTK_IS_NOTEBOOK (widget))
509     return TRUE;
510
511   notebook = GTK_NOTEBOOK (widget);
512   if (!notebook->focus_tab)
513     return TRUE;
514
515   gail_focus_notify_when_idle (widget);
516   return TRUE;
517 }
518
519 static gboolean
520 gail_focus_idle_handler (gpointer data)
521 {
522   focus_notify_handler = 0;
523   /*
524    * The widget which was to receive focus may have been removed
525    */
526   if (!next_focus_widget)
527     {
528       if (next_focus_widget != data)
529         return FALSE;
530     }
531   else
532     {
533       void *vp_next_focus_widget = &next_focus_widget;
534       g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
535       next_focus_widget = NULL;
536     }
537     
538   gail_focus_notify (data);
539
540   return FALSE;
541 }
542
543 static void
544 gail_focus_notify (GtkWidget *widget)
545 {
546   AtkObject *atk_obj;
547   gboolean transient;
548
549   if (widget != focus_widget)
550     {
551       if (focus_widget)
552         {
553           void *vp_focus_widget = &focus_widget;
554           g_object_remove_weak_pointer (G_OBJECT (focus_widget), vp_focus_widget);
555         }
556       focus_widget = widget;
557       if (focus_widget)
558         {
559           void *vp_focus_widget = &focus_widget;
560           g_object_add_weak_pointer (G_OBJECT (focus_widget), vp_focus_widget);
561           /*
562            * The UI may not have been updated yet; e.g. in gtkhtml2
563            * html_view_layout() is called in a idle handler
564            */
565           if (focus_widget == focus_before_menu)
566             {
567               void *vp_focus_before_menu = &focus_before_menu;
568               g_object_remove_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
569               focus_before_menu = NULL;
570             }
571         }
572       gail_focus_notify_when_idle (focus_widget);
573     }
574   else
575     {
576       if (focus_widget)
577         atk_obj  = gail_get_accessible_for_widget (focus_widget, &transient);
578       else
579         atk_obj = NULL;
580       /*
581        * Do not report focus on redundant object
582        */
583       if (atk_obj && 
584           (atk_object_get_role(atk_obj) != ATK_ROLE_REDUNDANT_OBJECT))
585           atk_focus_tracker_notify (atk_obj);
586       if (atk_obj && transient)
587         g_object_unref (atk_obj);
588       if (subsequent_focus_widget)
589         {
590           GtkWidget *tmp_widget = subsequent_focus_widget;
591           subsequent_focus_widget = NULL;
592           gail_focus_notify_when_idle (tmp_widget);
593         }
594     }
595 }
596
597 static void
598 gail_focus_notify_when_idle (GtkWidget *widget)
599 {
600   if (focus_notify_handler)
601     {
602       if (widget)
603         {
604           /*
605            * Ignore focus request when menu item is going to be focused.
606            * See bug #124232.
607            */
608           if (GTK_IS_MENU_ITEM (next_focus_widget) && !GTK_IS_MENU_ITEM (widget))
609             return;
610
611           if (next_focus_widget)
612             {
613               if (GTK_IS_MENU_ITEM (next_focus_widget) && GTK_IS_MENU_ITEM (widget))
614                 {
615                   if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (next_focus_widget)) == gtk_widget_get_parent (widget))
616                     {
617                       if (subsequent_focus_widget)
618                         g_assert_not_reached ();
619                       subsequent_focus_widget = widget;
620                       return;
621                     } 
622                 }
623             }
624           g_source_remove (focus_notify_handler);
625           if (next_focus_widget)
626             {
627               void *vp_next_focus_widget = &next_focus_widget;
628               g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
629               next_focus_widget = NULL;
630             }
631         }
632       else
633         /*
634          * Ignore if focus is being set to NULL and we are waiting to set focus
635          */
636         return;
637     }
638
639   if (widget)
640     {
641       void *vp_next_focus_widget = &next_focus_widget;
642       next_focus_widget = widget;
643       g_object_add_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
644     }
645   else
646     {
647       /*
648        * We are about to report focus as NULL so remove the weak pointer
649        * for the widget we were waiting to report focus on.
650        */ 
651       if (next_focus_widget)
652         {
653           void *vp_next_focus_widget = &next_focus_widget;
654           g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
655           next_focus_widget = NULL;
656         }
657     }
658
659   focus_notify_handler = gdk_threads_add_idle (gail_focus_idle_handler, widget);
660 }
661
662 static gboolean
663 gail_deactivate_watcher (GSignalInvocationHint *ihint,
664                          guint                  n_param_values,
665                          const GValue          *param_values,
666                          gpointer               data)
667 {
668   GObject *object;
669   GtkWidget *widget;
670   GtkMenuShell *shell;
671   GtkWidget *focus = NULL;
672
673   object = g_value_get_object (param_values + 0);
674   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
675   widget = GTK_WIDGET (object);
676
677   g_return_val_if_fail (GTK_IS_MENU_SHELL(widget), TRUE);
678   shell = GTK_MENU_SHELL(widget);
679   if (!shell->parent_menu_shell)
680     focus = focus_before_menu;
681       
682   /*
683    * If we are waiting to report focus on a menubar or a menu item
684    * because of a previous deselect, cancel it.
685    */
686   if (was_deselect &&
687       focus_notify_handler &&
688       next_focus_widget &&
689       (GTK_IS_MENU_BAR (next_focus_widget) ||
690        GTK_IS_MENU_ITEM (next_focus_widget)))
691     {
692       void *vp_next_focus_widget = &next_focus_widget;
693       g_source_remove (focus_notify_handler);
694       g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
695       next_focus_widget = NULL;
696       focus_notify_handler = 0;
697       was_deselect = FALSE;
698     }
699   gail_focus_notify_when_idle (focus);
700
701   return TRUE; 
702 }
703
704 static void
705 gail_focus_tracker_init (void)
706 {
707   static gboolean  emission_hooks_added = FALSE;
708
709   if (!emission_hooks_added)
710     {
711       /*
712        * We cannot be sure that the classes exist so we make sure that they do.
713        */
714       gtk_type_class (GTK_TYPE_WIDGET);
715       gtk_type_class (GTK_TYPE_ITEM);
716       gtk_type_class (GTK_TYPE_MENU_SHELL);
717       gtk_type_class (GTK_TYPE_NOTEBOOK);
718
719       /*
720        * We listen for event_after signal and then check that the
721        * event was a focus in event so we get called after the event.
722        */
723       g_signal_add_emission_hook (
724              g_signal_lookup ("event-after", GTK_TYPE_WIDGET), 0,
725              gail_focus_watcher, NULL, (GDestroyNotify) NULL);
726       /*
727        * A "select" signal is emitted when arrow key is used to
728        * move to a list item in the popup window of a GtkCombo or
729        * a menu item in a menu.
730        */
731       g_signal_add_emission_hook (
732              g_signal_lookup ("select", GTK_TYPE_ITEM), 0,
733              gail_select_watcher, NULL, (GDestroyNotify) NULL);
734
735       /*
736        * A "deselect" signal is emitted when arrow key is used to
737        * move from a menu item in a menu to the parent menu.
738        */
739       g_signal_add_emission_hook (
740              g_signal_lookup ("deselect", GTK_TYPE_ITEM), 0,
741              gail_deselect_watcher, NULL, (GDestroyNotify) NULL);
742
743       /*
744        * We listen for deactivate signals on menushells to determine
745        * when the "focus" has left the menus.
746        */
747       g_signal_add_emission_hook (
748              g_signal_lookup ("deactivate", GTK_TYPE_MENU_SHELL), 0,
749              gail_deactivate_watcher, NULL, (GDestroyNotify) NULL);
750
751       /*
752        * We listen for "switch-page" signal on a GtkNotebook to notify
753        * when page has changed because of clicking on a notebook tab.
754        */
755       g_signal_add_emission_hook (
756              g_signal_lookup ("switch-page", GTK_TYPE_NOTEBOOK), 0,
757              gail_switch_page_watcher, NULL, (GDestroyNotify) NULL);
758       emission_hooks_added = TRUE;
759     }
760 }
761
762 static void
763 gail_focus_object_destroyed (gpointer data)
764 {
765   GObject *obj;
766
767   obj = G_OBJECT (data);
768   g_object_set_qdata (obj, quark_focus_object, NULL);
769   g_object_unref (obj); 
770 }
771
772 static void
773 gail_focus_tracker (AtkObject *focus_object)
774 {
775   /*
776    * Do not report focus on redundant object
777    */
778   if (focus_object && 
779       (atk_object_get_role(focus_object) != ATK_ROLE_REDUNDANT_OBJECT))
780     {
781       AtkObject *old_focus_object;
782
783       if (!GTK_IS_ACCESSIBLE (focus_object))
784         {
785           AtkObject *parent;
786
787           parent = focus_object;
788           while (1)
789             {
790               parent = atk_object_get_parent (parent);
791               if (parent == NULL)
792                 break;
793               if (GTK_IS_ACCESSIBLE (parent))
794                 break;
795             }
796
797           if (parent)
798             {
799               gail_set_focus_object (focus_object, parent);
800             }
801         }
802       else
803         {
804           old_focus_object = g_object_get_qdata (G_OBJECT (focus_object), quark_focus_object);
805           if (old_focus_object)
806             {
807               g_object_weak_unref (G_OBJECT (old_focus_object),
808                                    (GWeakNotify) gail_focus_object_destroyed,
809                                    focus_object);
810               g_object_set_qdata (G_OBJECT (focus_object), quark_focus_object, NULL);
811               g_object_unref (G_OBJECT (focus_object));
812             }
813         }
814     }
815 }
816
817 static void 
818 gail_set_focus_widget (GtkWidget *focus_widget,
819                        GtkWidget *widget)
820 {
821   AtkObject *focus_obj;
822   AtkObject *obj;
823
824   focus_obj = gtk_widget_get_accessible (focus_widget);
825   obj = gtk_widget_get_accessible (widget);
826   gail_set_focus_object (focus_obj, obj);
827 }
828
829 static void 
830 gail_set_focus_object (AtkObject *focus_obj,
831                        AtkObject *obj)
832 {
833   AtkObject *old_focus_obj;
834
835   old_focus_obj = g_object_get_qdata (G_OBJECT (obj), quark_focus_object);
836   if (old_focus_obj != obj)
837     {
838       if (old_focus_obj)
839         g_object_weak_unref (G_OBJECT (old_focus_obj),
840                              (GWeakNotify) gail_focus_object_destroyed,
841                              obj);
842       else
843         /*
844          * We call g_object_ref as if obj is destroyed 
845          * while the weak reference exists then destroying the 
846          * focus_obj would cause gail_focus_object_destroyed to be 
847          * called when obj is not a valid GObject.
848          */
849         g_object_ref (obj);
850
851       g_object_weak_ref (G_OBJECT (focus_obj),
852                          (GWeakNotify) gail_focus_object_destroyed,
853                          obj);
854       g_object_set_qdata (G_OBJECT (obj), quark_focus_object, focus_obj);
855     }
856 }
857
858 /*
859  *   These exported symbols are hooked by gnome-program
860  * to provide automatic module initialization and shutdown.
861  */
862 extern void gnome_accessibility_module_init     (void);
863 extern void gnome_accessibility_module_shutdown (void);
864
865 static int gail_initialized = FALSE;
866
867 static void
868 gail_accessibility_module_init (void)
869 {
870   const char *env_a_t_support;
871   gboolean a_t_support = FALSE;
872
873   if (gail_initialized)
874     {
875       return;
876     }
877   gail_initialized = TRUE;
878   quark_focus_object = g_quark_from_static_string ("gail-focus-object");
879   
880   env_a_t_support = g_getenv (GNOME_ACCESSIBILITY_ENV);
881
882   if (env_a_t_support)
883     a_t_support = atoi (env_a_t_support);
884   if (a_t_support)
885     fprintf (stderr, "GTK Accessibility Module initialized\n");
886
887   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_WIDGET, gail_widget);
888   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CONTAINER, gail_container);
889   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_BUTTON, gail_button);
890   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_ITEM, gail_item);
891   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_MENU_ITEM, gail_menu_item);
892   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_TOGGLE_BUTTON, gail_toggle_button);
893   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_IMAGE, gail_image);
894   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_TEXT_VIEW, gail_text_view);
895   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_COMBO, gail_combo);
896   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_COMBO_BOX, gail_combo_box);
897   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_ENTRY, gail_entry);
898   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_MENU_BAR, gail_menu_shell);
899   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_MENU, gail_menu);
900   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_WINDOW, gail_window);
901   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_RANGE, gail_range);
902   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SCALE, gail_scale);
903   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CLIST, gail_clist);
904   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_LABEL, gail_label);
905   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_STATUSBAR, gail_statusbar);
906   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_NOTEBOOK, gail_notebook);
907   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CALENDAR, gail_calendar);
908   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_PROGRESS_BAR, gail_progress_bar);
909   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SPIN_BUTTON, gail_spin_button);
910   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_TREE_VIEW, gail_tree_view);
911   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_FRAME, gail_frame);
912   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CELL_RENDERER_TEXT, gail_text_cell);
913   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CELL_RENDERER_TOGGLE, gail_boolean_cell);
914   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CELL_RENDERER_PIXBUF, gail_image_cell);
915   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CELL_RENDERER, gail_renderer_cell);
916   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_RADIO_BUTTON, gail_radio_button);
917   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_ARROW, gail_arrow);
918   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_PIXMAP, gail_pixmap);
919   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SEPARATOR, gail_separator);
920   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_BOX, gail_box);
921   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SCROLLED_WINDOW, gail_scrolled_window);
922   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_LIST, gail_list);
923   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_PANED, gail_paned);
924   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SCROLLBAR, gail_scrollbar);
925   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_OPTION_MENU, gail_option_menu);
926   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_CHECK_MENU_ITEM, gail_check_menu_item);
927   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_RADIO_MENU_ITEM, gail_radio_menu_item);
928   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_EXPANDER, gail_expander);
929
930   /* LIBGNOMECANVAS SUPPORT */
931   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_OBJECT, gail_object);
932
933   atk_focus_tracker_init (gail_focus_tracker_init);
934   focus_tracker_id = atk_add_focus_tracker (gail_focus_tracker);
935
936   /* Initialize the GailUtility class */
937   g_type_class_unref (g_type_class_ref (GAIL_TYPE_UTIL));
938   g_type_class_unref (g_type_class_ref (GAIL_TYPE_MISC));
939 }
940
941 /**
942  * gnome_accessibility_module_init:
943  * @void: 
944  * 
945  *   This method is invoked by name from libgnome's
946  * gnome-program.c to activate accessibility support.
947  **/
948 void
949 gnome_accessibility_module_init (void)
950 {
951   gail_accessibility_module_init ();
952 }
953
954 /**
955  * gnome_accessibility_module_shutdown:
956  * @void: 
957  * 
958  *   This method is invoked by name from libgnome's
959  * gnome-program.c to de-activate accessibility support.
960  **/
961 void
962 gnome_accessibility_module_shutdown (void)
963 {
964   if (!gail_initialized)
965     {
966       return;
967     }
968   gail_initialized = FALSE;
969   atk_remove_focus_tracker (focus_tracker_id);
970
971   fprintf (stderr, "GTK Accessibility Module shutdown\n");
972
973   /* FIXME: de-register the factory types so we can unload ? */
974 }
975
976 int
977 gtk_module_init (gint *argc, char** argv[])
978 {
979   gail_accessibility_module_init ();
980
981   return 0;
982 }