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