]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtkaccessibility.c
gtkenums: correct various documentation typos
[~andy/gtk] / gtk / a11y / gtkaccessibility.c
1 /* GTK+ - accessibility implementations
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, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include "gtkaccessibility.h"
21 #include "gtkaccessibilityutil.h"
22 #include "gtkaccessibilitymisc.h"
23
24 #include "gtkwindowaccessible.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28
29 #include <gdk/gdk.h>
30 #include <gtk/gtkx.h>
31 #include <gtk/gtkentry.h>
32 #include <gtk/gtknotebook.h>
33 #include <gtk/gtkmenuitem.h>
34 #include <gtk/gtkmenu.h>
35 #include <gtk/gtkmenubar.h>
36 #include <gtk/gtktogglebutton.h>
37 #include <gtk/gtkcombobox.h>
38 #include <gtk/gtkaccessible.h>
39
40 #ifdef GDK_WINDOWING_X11
41 #include <atk-bridge.h>
42 #endif
43
44 static gboolean gail_focus_watcher      (GSignalInvocationHint *ihint,
45                                          guint                  n_param_values,
46                                          const GValue          *param_values,
47                                          gpointer               data);
48 static gboolean gail_select_watcher     (GSignalInvocationHint *ihint,
49                                          guint                  n_param_values,
50                                          const GValue          *param_values,
51                                          gpointer               data);
52 static gboolean gail_deselect_watcher   (GSignalInvocationHint *ihint,
53                                          guint                  n_param_values,
54                                          const GValue          *param_values,
55                                          gpointer               data);
56 static gboolean gail_switch_page_watcher(GSignalInvocationHint *ihint,
57                                          guint                  n_param_values,
58                                          const GValue          *param_values,
59                                          gpointer               data);
60 static void     gail_finish_select       (GtkWidget            *widget);
61 static void     gail_map_cb              (GtkWidget            *widget);
62 static void     gail_map_submenu_cb      (GtkWidget            *widget);
63 static gint     gail_focus_idle_handler  (gpointer             data);
64 static void     gail_focus_notify        (GtkWidget            *widget);
65 static void     gail_focus_notify_when_idle (GtkWidget            *widget);
66
67 static void     gail_focus_tracker_init (void);
68 static void     gail_focus_object_destroyed (gpointer data);
69 static void     gail_focus_tracker (AtkObject *object);
70 static void     gail_set_focus_widget (GtkWidget *focus_widget,
71                                        GtkWidget *widget);
72 static void     gail_set_focus_object (AtkObject *focus_obj,
73                                        AtkObject *obj);
74
75 GtkWidget* _focus_widget = NULL;
76 static GtkWidget* next_focus_widget = NULL;
77 static gboolean was_deselect = FALSE;
78 static GtkWidget* subsequent_focus_widget = NULL;
79 static GtkWidget* focus_before_menu = NULL;
80 static guint focus_notify_handler = 0;
81 static guint focus_tracker_id = 0;
82 static GQuark quark_focus_object = 0;
83 static int initialized = FALSE;
84
85 static AtkObject*
86 get_accessible_for_widget (GtkWidget *widget,
87                            gboolean  *transient)
88 {
89   AtkObject *obj = NULL;
90
91   *transient = FALSE;
92   if (!widget)
93     return NULL;
94
95   if (GTK_IS_ENTRY (widget))
96     ;
97   else if (GTK_IS_NOTEBOOK (widget)) 
98     {
99       GtkNotebook *notebook;
100       gint page_num = -1;
101
102       notebook = GTK_NOTEBOOK (widget);
103       page_num = gtk_notebook_get_current_page (notebook);
104       if (page_num != -1)
105         {
106           obj = gtk_widget_get_accessible (widget);
107           obj = atk_object_ref_accessible_child (obj, page_num);
108           g_object_unref (obj);
109         }
110     }
111   else if (GTK_IS_TOGGLE_BUTTON (widget))
112     {
113       GtkWidget *other_widget = gtk_widget_get_parent (widget);
114       if (GTK_IS_COMBO_BOX (other_widget))
115         {
116           gail_set_focus_widget (other_widget, widget);
117           widget = other_widget;
118         }
119     }
120   if (obj == NULL)
121     {
122       AtkObject *focus_object;
123
124       obj = gtk_widget_get_accessible (widget);
125       focus_object = g_object_get_qdata (G_OBJECT (obj), quark_focus_object);
126       /*
127        * We check whether the object for this focus_object has been deleted.
128        * This can happen when navigating to an empty directory in nautilus. 
129        * See bug #141907.
130        */
131       if (ATK_IS_GOBJECT_ACCESSIBLE (focus_object))
132         {
133           if (!atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (focus_object)))
134             focus_object = NULL;
135         }
136       if (focus_object)
137         obj = focus_object;
138     }
139
140   return obj;
141 }
142
143 static gboolean
144 gail_focus_watcher (GSignalInvocationHint *ihint,
145                     guint                  n_param_values,
146                     const GValue          *param_values,
147                     gpointer               data)
148 {
149   GObject *object;
150   GtkWidget *widget;
151   GdkEvent *event;
152
153   object = g_value_get_object (param_values + 0);
154   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
155
156   event = g_value_get_boxed (param_values + 1);
157   widget = GTK_WIDGET (object);
158
159   if (event->type == GDK_FOCUS_CHANGE) 
160     {
161       if (event->focus_change.in)
162         {
163           if (GTK_IS_WINDOW (widget))
164             {
165               GtkWidget *focus_widget;
166               GtkWindow *window;
167               GtkWindowType type;
168
169               window = GTK_WINDOW (widget);
170               focus_widget = gtk_window_get_focus (window);
171               g_object_get (window, "type", &type, NULL);
172
173               if (focus_widget)
174                 {
175                   /*
176                    * If we already have a potential focus widget set this
177                    * windows's focus widget to focus_before_menu so that 
178                    * it will be reported when menu item is unset.
179                    */
180                   if (next_focus_widget)
181                     {
182                       if (GTK_IS_MENU_ITEM (next_focus_widget) &&
183                           !focus_before_menu)
184                         {
185                           void *vp_focus_before_menu = &focus_before_menu;
186                           focus_before_menu = focus_widget;
187                           g_object_add_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
188                         }
189
190                       return TRUE;
191                     }
192                   widget = focus_widget;
193                 }
194               else if (type == GTK_WINDOW_POPUP)
195                 {
196                   if (GTK_IS_BIN (widget))
197                     {
198                       GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
199
200                       if (GTK_IS_WIDGET (child) && gtk_widget_has_grab (child))
201                         {
202                           if (GTK_IS_MENU_SHELL (child))
203                             {
204                               if (gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (child)))
205                                 {
206                                   /*
207                                    * We have a menu which has a menu item selected
208                                    * so we do not report focus on the menu.
209                                    */ 
210                                   return TRUE; 
211                                 }
212                             }
213                           widget = child;
214                         } 
215                     }
216                   else /* popup window has no children; this edge case occurs in some custom code (OOo for instance) */
217                     {
218                       return TRUE;
219                     }
220                 }
221               else /* Widget is a non-popup toplevel with no focus children; 
222                       don't emit for this case either, as it's useless */
223                 {
224                   return TRUE;
225                 }
226             }
227         }
228       else
229         {
230           if (next_focus_widget)
231             {
232                GtkWidget *toplevel;
233
234                toplevel = gtk_widget_get_toplevel (next_focus_widget);
235                if (toplevel == widget)
236                  next_focus_widget = NULL; 
237             }
238           /* focus out */
239           widget = NULL;
240         }
241     }
242   else
243     {
244       if (event->type == GDK_MOTION_NOTIFY && gtk_widget_has_focus (widget))
245         {
246           if (widget == _focus_widget)
247             {
248               return TRUE;
249             }
250         }
251       else
252         {
253           return TRUE;
254         }
255     }
256
257 #ifdef GDK_WINDOWING_X11
258   /*
259    * If the focus widget is a GtkSocket without a plug
260    * then ignore the focus notification as the embedded
261    * plug will report a focus notification.
262    */
263   if (GTK_IS_SOCKET (widget) &&
264       gtk_socket_get_plug_window (GTK_SOCKET (widget)) != NULL)
265     return TRUE;
266 #endif
267
268   /*
269    * The widget may not yet be visible on the screen so we wait until it is.
270    */
271   gail_focus_notify_when_idle (widget);
272   return TRUE; 
273 }
274
275 static gboolean
276 gail_select_watcher (GSignalInvocationHint *ihint,
277                      guint                  n_param_values,
278                      const GValue          *param_values,
279                      gpointer               data)
280 {
281   GObject *object;
282   GtkWidget *widget;
283
284   object = g_value_get_object (param_values + 0);
285   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
286
287   widget = GTK_WIDGET (object);
288
289   if (!gtk_widget_get_mapped (widget))
290     {
291       g_signal_connect (widget, "map",
292                         G_CALLBACK (gail_map_cb),
293                         NULL);
294     }
295   else
296     gail_finish_select (widget);
297
298   return TRUE;
299 }
300
301 static void
302 gail_finish_select (GtkWidget *widget)
303 {
304   if (GTK_IS_MENU_ITEM (widget))
305     {
306       GtkMenuItem* menu_item;
307       GtkWidget *submenu;
308
309       menu_item = GTK_MENU_ITEM (widget);
310       submenu = gtk_menu_item_get_submenu (menu_item);
311       if (submenu &&
312           !gtk_widget_get_mapped (submenu))
313         {
314           /*
315            * If the submenu is not visble, wait until it is before
316            * reporting focus on the menu item.
317            */
318           gulong handler_id;
319
320           handler_id = g_signal_handler_find (submenu,
321                                               G_SIGNAL_MATCH_FUNC,
322                                               g_signal_lookup ("map",
323                                                                GTK_TYPE_WINDOW),
324                                               0,
325                                               NULL,
326                                               (gpointer) gail_map_submenu_cb,
327                                               NULL); 
328           if (!handler_id)
329             g_signal_connect (submenu, "map",
330                               G_CALLBACK (gail_map_submenu_cb),
331                               NULL);
332             return;
333         }
334       /*
335        * If we are waiting to report focus on a menubar or a menu item
336        * because of a previous deselect, cancel it.
337        */
338       if (was_deselect &&
339           focus_notify_handler &&
340           next_focus_widget &&
341           (GTK_IS_MENU_BAR (next_focus_widget) ||
342            GTK_IS_MENU_ITEM (next_focus_widget)))
343         {
344           void *vp_next_focus_widget = &next_focus_widget;
345           g_source_remove (focus_notify_handler);
346           g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
347           next_focus_widget = NULL;
348           focus_notify_handler = 0;
349           was_deselect = FALSE;
350         }
351     } 
352   /*
353    * If previously focused widget is not a GtkMenuItem or a GtkMenu,
354    * keep track of it so we can return to it after menubar is deactivated
355    */
356   if (_focus_widget && 
357       !GTK_IS_MENU_ITEM (_focus_widget) && 
358       !GTK_IS_MENU (_focus_widget))
359     {
360       void *vp_focus_before_menu = &focus_before_menu;
361       focus_before_menu = _focus_widget;
362       g_object_add_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
363
364     } 
365   gail_focus_notify_when_idle (widget);
366
367   return; 
368 }
369
370 static void
371 gail_map_cb (GtkWidget *widget)
372 {
373   gail_finish_select (widget);
374 }
375
376 static void
377 gail_map_submenu_cb (GtkWidget *widget)
378 {
379   if (GTK_IS_MENU (widget))
380     {
381       GtkWidget *parent_menu_item;
382
383       parent_menu_item = gtk_menu_get_attach_widget (GTK_MENU (widget));
384       if (parent_menu_item)
385         gail_finish_select (parent_menu_item);
386     }
387 }
388
389
390 static gboolean
391 gail_deselect_watcher (GSignalInvocationHint *ihint,
392                        guint                  n_param_values,
393                        const GValue          *param_values,
394                        gpointer               data)
395 {
396   GObject *object;
397   GtkWidget *widget;
398   GtkWidget *menu_shell;
399
400   object = g_value_get_object (param_values + 0);
401   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
402
403   widget = GTK_WIDGET (object);
404
405   if (!GTK_IS_MENU_ITEM (widget))
406     return TRUE;
407
408   if (subsequent_focus_widget == widget)
409     subsequent_focus_widget = NULL;
410
411   menu_shell = gtk_widget_get_parent (widget);
412   if (GTK_IS_MENU_SHELL (menu_shell))
413     {
414       GtkWidget *parent_menu_shell;
415
416       parent_menu_shell = gtk_menu_shell_get_parent_shell (GTK_MENU_SHELL (menu_shell));
417       if (parent_menu_shell)
418         {
419           GtkWidget *active_menu_item;
420
421           active_menu_item = gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (parent_menu_shell));
422           if (active_menu_item)
423             {
424               gail_focus_notify_when_idle (active_menu_item);
425             }
426         }
427       else
428         {
429           if (!GTK_IS_MENU_BAR (menu_shell))
430             {
431               gail_focus_notify_when_idle (menu_shell);
432             }
433         }
434     }
435   was_deselect = TRUE;
436   return TRUE; 
437 }
438
439 static gboolean 
440 gail_switch_page_watcher (GSignalInvocationHint *ihint,
441                           guint                  n_param_values,
442                           const GValue          *param_values,
443                           gpointer               data)
444 {
445   GObject *object;
446   GtkWidget *widget;
447
448   object = g_value_get_object (param_values + 0);
449   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
450
451   widget = GTK_WIDGET (object);
452
453   if (!GTK_IS_NOTEBOOK (widget))
454     return TRUE;
455
456   if (gtk_notebook_get_current_page (GTK_NOTEBOOK (widget)) == -1)
457     return TRUE;
458
459   gail_focus_notify_when_idle (widget);
460   return TRUE;
461 }
462
463 static gboolean
464 gail_focus_idle_handler (gpointer data)
465 {
466   focus_notify_handler = 0;
467   /*
468    * The widget which was to receive focus may have been removed
469    */
470   if (!next_focus_widget)
471     {
472       if (next_focus_widget != data)
473         return FALSE;
474     }
475   else
476     {
477       void *vp_next_focus_widget = &next_focus_widget;
478       g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
479       next_focus_widget = NULL;
480     }
481     
482   gail_focus_notify (data);
483
484   return FALSE;
485 }
486
487 static void
488 gail_focus_notify (GtkWidget *widget)
489 {
490   AtkObject *atk_obj;
491   gboolean transient;
492
493   if (widget != _focus_widget)
494     {
495       if (_focus_widget)
496         {
497           void *vp_focus_widget = &_focus_widget;
498           g_object_remove_weak_pointer (G_OBJECT (_focus_widget), vp_focus_widget);
499         }
500       _focus_widget = widget;
501       if (_focus_widget)
502         {
503           void *vp_focus_widget = &_focus_widget;
504           g_object_add_weak_pointer (G_OBJECT (_focus_widget), vp_focus_widget);
505           /*
506            * The UI may not have been updated yet; e.g. in gtkhtml2
507            * html_view_layout() is called in a idle handler
508            */
509           if (_focus_widget == focus_before_menu)
510             {
511               void *vp_focus_before_menu = &focus_before_menu;
512               g_object_remove_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
513               focus_before_menu = NULL;
514             }
515         }
516       gail_focus_notify_when_idle (_focus_widget);
517     }
518   else
519     {
520       if (_focus_widget)
521         atk_obj  = get_accessible_for_widget (_focus_widget, &transient);
522       else
523         atk_obj = NULL;
524       /*
525        * Do not report focus on redundant object
526        */
527       if (atk_obj &&
528           (atk_object_get_role(atk_obj) != ATK_ROLE_REDUNDANT_OBJECT))
529           atk_focus_tracker_notify (atk_obj);
530       if (atk_obj && transient)
531         g_object_unref (atk_obj);
532       if (subsequent_focus_widget)
533         {
534           GtkWidget *tmp_widget = subsequent_focus_widget;
535           subsequent_focus_widget = NULL;
536           gail_focus_notify_when_idle (tmp_widget);
537         }
538     }
539 }
540
541 static void
542 gail_focus_notify_when_idle (GtkWidget *widget)
543 {
544   if (focus_notify_handler)
545     {
546       if (widget)
547         {
548           /*
549            * Ignore focus request when menu item is going to be focused.
550            * See bug #124232.
551            */
552           if (GTK_IS_MENU_ITEM (next_focus_widget) && !GTK_IS_MENU_ITEM (widget))
553             return;
554
555           if (next_focus_widget)
556             {
557               if (GTK_IS_MENU_ITEM (next_focus_widget) && GTK_IS_MENU_ITEM (widget))
558                 {
559                   if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (next_focus_widget)) == gtk_widget_get_parent (widget))
560                     {
561                       if (subsequent_focus_widget)
562                         g_assert_not_reached ();
563                       subsequent_focus_widget = widget;
564                       return;
565                     } 
566                 }
567             }
568           g_source_remove (focus_notify_handler);
569           if (next_focus_widget)
570             {
571               void *vp_next_focus_widget = &next_focus_widget;
572               g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
573               next_focus_widget = NULL;
574             }
575         }
576       else
577         /*
578          * Ignore if focus is being set to NULL and we are waiting to set focus
579          */
580         return;
581     }
582
583   if (widget)
584     {
585       void *vp_next_focus_widget = &next_focus_widget;
586       next_focus_widget = widget;
587       g_object_add_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
588     }
589   else
590     {
591       /*
592        * We are about to report focus as NULL so remove the weak pointer
593        * for the widget we were waiting to report focus on.
594        */ 
595       if (next_focus_widget)
596         {
597           void *vp_next_focus_widget = &next_focus_widget;
598           g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
599           next_focus_widget = NULL;
600         }
601     }
602
603   focus_notify_handler = gdk_threads_add_idle (gail_focus_idle_handler, widget);
604 }
605
606 static gboolean
607 gail_deactivate_watcher (GSignalInvocationHint *ihint,
608                          guint                  n_param_values,
609                          const GValue          *param_values,
610                          gpointer               data)
611 {
612   GObject *object;
613   GtkWidget *widget;
614   GtkMenuShell *shell;
615   GtkWidget *focus = NULL;
616
617   object = g_value_get_object (param_values + 0);
618   g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
619   widget = GTK_WIDGET (object);
620
621   g_return_val_if_fail (GTK_IS_MENU_SHELL(widget), TRUE);
622   shell = GTK_MENU_SHELL(widget);
623   if (! gtk_menu_shell_get_parent_shell (shell))
624     focus = focus_before_menu;
625       
626   /*
627    * If we are waiting to report focus on a menubar or a menu item
628    * because of a previous deselect, cancel it.
629    */
630   if (was_deselect &&
631       focus_notify_handler &&
632       next_focus_widget &&
633       (GTK_IS_MENU_BAR (next_focus_widget) ||
634        GTK_IS_MENU_ITEM (next_focus_widget)))
635     {
636       void *vp_next_focus_widget = &next_focus_widget;
637       g_source_remove (focus_notify_handler);
638       g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
639       next_focus_widget = NULL;
640       focus_notify_handler = 0;
641       was_deselect = FALSE;
642     }
643   gail_focus_notify_when_idle (focus);
644
645   return TRUE; 
646 }
647
648 static void
649 gail_focus_tracker_init (void)
650 {
651   static gboolean  emission_hooks_added = FALSE;
652
653   if (!emission_hooks_added)
654     {
655       /*
656        * We cannot be sure that the classes exist so we make sure that they do.
657        */
658       g_type_class_ref (GTK_TYPE_WIDGET);
659       g_type_class_ref (GTK_TYPE_MENU_ITEM);
660       g_type_class_ref (GTK_TYPE_MENU_SHELL);
661       g_type_class_ref (GTK_TYPE_NOTEBOOK);
662
663       /*
664        * We listen for event_after signal and then check that the
665        * event was a focus in event so we get called after the event.
666        */
667       g_signal_add_emission_hook (
668              g_signal_lookup ("event-after", GTK_TYPE_WIDGET), 0,
669              gail_focus_watcher, NULL, (GDestroyNotify) NULL);
670       /*
671        * A "select" signal is emitted when arrow key is used to
672        * move to a list item in the popup window of a GtkCombo or
673        * a menu item in a menu.
674        */
675       g_signal_add_emission_hook (
676              g_signal_lookup ("select", GTK_TYPE_MENU_ITEM), 0,
677              gail_select_watcher, NULL, (GDestroyNotify) NULL);
678
679       /*
680        * A "deselect" signal is emitted when arrow key is used to
681        * move from a menu item in a menu to the parent menu.
682        */
683       g_signal_add_emission_hook (
684              g_signal_lookup ("deselect", GTK_TYPE_MENU_ITEM), 0,
685              gail_deselect_watcher, NULL, (GDestroyNotify) NULL);
686
687       /*
688        * We listen for deactivate signals on menushells to determine
689        * when the "focus" has left the menus.
690        */
691       g_signal_add_emission_hook (
692              g_signal_lookup ("deactivate", GTK_TYPE_MENU_SHELL), 0,
693              gail_deactivate_watcher, NULL, (GDestroyNotify) NULL);
694
695       /*
696        * We listen for "switch-page" signal on a GtkNotebook to notify
697        * when page has changed because of clicking on a notebook tab.
698        */
699       g_signal_add_emission_hook (
700              g_signal_lookup ("switch-page", GTK_TYPE_NOTEBOOK), 0,
701              gail_switch_page_watcher, NULL, (GDestroyNotify) NULL);
702       emission_hooks_added = TRUE;
703     }
704 }
705
706 static void
707 gail_focus_object_destroyed (gpointer data)
708 {
709   GObject *obj;
710
711   obj = G_OBJECT (data);
712   g_object_set_qdata (obj, quark_focus_object, NULL);
713   g_object_unref (obj); 
714 }
715
716 static void
717 gail_focus_tracker (AtkObject *focus_object)
718 {
719   /*
720    * Do not report focus on redundant object
721    */
722   if (focus_object && 
723       (atk_object_get_role(focus_object) != ATK_ROLE_REDUNDANT_OBJECT))
724     {
725       AtkObject *old_focus_object;
726
727       if (!GTK_IS_ACCESSIBLE (focus_object))
728         {
729           AtkObject *parent;
730
731           parent = focus_object;
732           while (1)
733             {
734               parent = atk_object_get_parent (parent);
735               if (parent == NULL)
736                 break;
737               if (GTK_IS_ACCESSIBLE (parent))
738                 break;
739             }
740
741           if (parent)
742             {
743               gail_set_focus_object (focus_object, parent);
744             }
745         }
746       else
747         {
748           old_focus_object = g_object_get_qdata (G_OBJECT (focus_object), quark_focus_object);
749           if (old_focus_object)
750             {
751               g_object_weak_unref (G_OBJECT (old_focus_object),
752                                    (GWeakNotify) gail_focus_object_destroyed,
753                                    focus_object);
754               g_object_set_qdata (G_OBJECT (focus_object), quark_focus_object, NULL);
755               g_object_unref (G_OBJECT (focus_object));
756             }
757         }
758     }
759 }
760
761 static void 
762 gail_set_focus_widget (GtkWidget *focus_widget,
763                        GtkWidget *widget)
764 {
765   AtkObject *focus_obj;
766   AtkObject *obj;
767
768   focus_obj = gtk_widget_get_accessible (focus_widget);
769   obj = gtk_widget_get_accessible (widget);
770   gail_set_focus_object (focus_obj, obj);
771 }
772
773 static void 
774 gail_set_focus_object (AtkObject *focus_obj,
775                        AtkObject *obj)
776 {
777   AtkObject *old_focus_obj;
778
779   old_focus_obj = g_object_get_qdata (G_OBJECT (obj), quark_focus_object);
780   if (old_focus_obj != obj)
781     {
782       if (old_focus_obj)
783         g_object_weak_unref (G_OBJECT (old_focus_obj),
784                              (GWeakNotify) gail_focus_object_destroyed,
785                              obj);
786       else
787         /*
788          * We call g_object_ref as if obj is destroyed 
789          * while the weak reference exists then destroying the 
790          * focus_obj would cause gail_focus_object_destroyed to be 
791          * called when obj is not a valid GObject.
792          */
793         g_object_ref (obj);
794
795       g_object_weak_ref (G_OBJECT (focus_obj),
796                          (GWeakNotify) gail_focus_object_destroyed,
797                          obj);
798       g_object_set_qdata (G_OBJECT (obj), quark_focus_object, focus_obj);
799     }
800 }
801
802 static gboolean
803 state_event_watcher (GSignalInvocationHint *hint,
804                      guint                  n_param_values,
805                      const GValue          *param_values,
806                      gpointer               data)
807 {
808   GObject *object;
809   GtkWidget *widget;
810   AtkObject *atk_obj;
811   AtkObject *parent;
812   GdkEventWindowState *event;
813   gchar *signal_name;
814
815   object = g_value_get_object (param_values + 0);
816   if (!GTK_IS_WINDOW (object))
817     return FALSE;
818
819   event = g_value_get_boxed (param_values + 1);
820   if (event->type == GDK_WINDOW_STATE)
821     return FALSE;
822   widget = GTK_WIDGET (object);
823
824   if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
825     signal_name = "maximize";
826   else if (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED)
827     signal_name = "minimize";
828   else if (event->new_window_state == 0)
829     signal_name = "restore";
830   else
831     return TRUE;
832
833   atk_obj = gtk_widget_get_accessible (widget);
834   if (GTK_IS_WINDOW_ACCESSIBLE (atk_obj))
835     {
836       parent = atk_object_get_parent (atk_obj);
837       if (parent == atk_get_root ())
838         g_signal_emit_by_name (atk_obj, signal_name);
839
840       return TRUE;
841     }
842
843   return FALSE;
844 }
845
846 static gboolean
847 configure_event_watcher (GSignalInvocationHint *hint,
848                          guint                  n_param_values,
849                          const GValue          *param_values,
850                          gpointer               data)
851 {
852   GtkAllocation allocation;
853   GObject *object;
854   GtkWidget *widget;
855   AtkObject *atk_obj;
856   AtkObject *parent;
857   GdkEvent *event;
858   gchar *signal_name;
859
860   object = g_value_get_object (param_values + 0);
861   if (!GTK_IS_WINDOW (object))
862     return FALSE;
863
864   event = g_value_get_boxed (param_values + 1);
865   if (event->type != GDK_CONFIGURE)
866     return FALSE;
867   widget = GTK_WIDGET (object);
868   gtk_widget_get_allocation (widget, &allocation);
869   if (allocation.x == ((GdkEventConfigure *)event)->x &&
870       allocation.y == ((GdkEventConfigure *)event)->y &&
871       allocation.width == ((GdkEventConfigure *)event)->width &&
872       allocation.height == ((GdkEventConfigure *)event)->height)
873     return TRUE;
874
875   if (allocation.width != ((GdkEventConfigure *)event)->width ||
876       allocation.height != ((GdkEventConfigure *)event)->height)
877     signal_name = "resize";
878   else
879     signal_name = "move";
880
881   atk_obj = gtk_widget_get_accessible (widget);
882   if (GTK_IS_WINDOW_ACCESSIBLE (atk_obj))
883     {
884       parent = atk_object_get_parent (atk_obj);
885       if (parent == atk_get_root ())
886         g_signal_emit_by_name (atk_obj, signal_name);
887
888       return TRUE;
889     }
890
891   return FALSE;
892 }
893
894 static gboolean
895 window_focus (GtkWidget     *widget,
896               GdkEventFocus *event)
897 {
898   AtkObject *atk_obj;
899
900   g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
901
902   atk_obj = gtk_widget_get_accessible (widget);
903   g_signal_emit_by_name (atk_obj, event->in ? "activate" : "deactivate");
904
905   return FALSE;
906 }
907
908 static void
909 window_added (AtkObject *atk_obj,
910               guint      index,
911               AtkObject *child)
912 {
913   GtkWidget *widget;
914
915   if (!GTK_IS_WINDOW_ACCESSIBLE (child))
916     return;
917
918   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (child));
919   if (!widget)
920     return;
921
922   g_signal_connect (widget, "focus-in-event", (GCallback) window_focus, NULL);
923   g_signal_connect (widget, "focus-out-event", (GCallback) window_focus, NULL);
924   g_signal_emit_by_name (child, "create");
925 }
926
927 static void
928 window_removed (AtkObject *atk_obj,
929                 guint      index,
930                 AtkObject *child)
931 {
932   GtkWidget *widget;
933   GtkWindow *window;
934
935   if (!GTK_IS_WINDOW_ACCESSIBLE (child))
936     return;
937
938   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (child));
939   if (!widget)
940     return;
941
942   window = GTK_WINDOW (widget);
943   /*
944    * Deactivate window if it is still focused and we are removing it. This
945    * can happen when a dialog displayed by gok is removed.
946    */
947   if (gtk_window_is_active (window) && gtk_window_has_toplevel_focus (window))
948     g_signal_emit_by_name (child, "deactivate");
949
950   g_signal_handlers_disconnect_by_func (widget, (gpointer) window_focus, NULL);
951   g_signal_emit_by_name (child, "destroy");
952 }
953
954 static void
955 do_window_event_initialization (void)
956 {
957   AtkObject *root;
958
959   g_type_class_ref (GTK_TYPE_WINDOW_ACCESSIBLE);
960   g_signal_add_emission_hook (g_signal_lookup ("window-state-event", GTK_TYPE_WIDGET),
961                               0, state_event_watcher, NULL, (GDestroyNotify) NULL);
962   g_signal_add_emission_hook (g_signal_lookup ("configure-event", GTK_TYPE_WIDGET),
963                               0, configure_event_watcher, NULL, (GDestroyNotify) NULL);
964
965   root = atk_get_root ();
966   g_signal_connect (root, "children-changed::add", (GCallback) window_added, NULL);
967   g_signal_connect (root, "children-changed::remove", (GCallback) window_removed, NULL);
968 }
969
970 static void
971 undo_window_event_initialization (void)
972 {
973   AtkObject *root;
974
975   root = atk_get_root ();
976
977   g_signal_handlers_disconnect_by_func (root, (GCallback) window_added, NULL);
978   g_signal_handlers_disconnect_by_func (root, (GCallback) window_removed, NULL);
979 }
980
981
982 void
983 _gtk_accessibility_shutdown (void)
984 {
985   if (!initialized)
986     return;
987
988   initialized = FALSE;
989
990   g_clear_object (&atk_misc_instance);
991
992 #ifdef GDK_WINDOWING_X11
993   atk_bridge_adaptor_cleanup ();
994 #endif
995
996   undo_window_event_initialization ();
997 }
998
999 void
1000 _gtk_accessibility_init (void)
1001 {
1002   if (initialized)
1003     return;
1004
1005   initialized = TRUE;
1006   quark_focus_object = g_quark_from_static_string ("gail-focus-object");
1007
1008   atk_focus_tracker_init (gail_focus_tracker_init);
1009   focus_tracker_id = atk_add_focus_tracker (gail_focus_tracker);
1010
1011   _gtk_accessibility_override_atk_util ();
1012   do_window_event_initialization ();
1013
1014 #ifdef GDK_WINDOWING_X11
1015   atk_bridge_adaptor_init (NULL, NULL);
1016 #endif
1017
1018   atk_misc_instance = g_object_new (GTK_TYPE_MISC_IMPL, NULL);
1019 }