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