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