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