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/gtkcombobox.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/gtksocket.h>
37 #include <gtk/gtktogglebutton.h>
38 #include <gtk/gtkaccessible.h>
39
40 #ifdef HAVE_ATK_BRIDGE
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*
get_accessible_for_widget(GtkWidget * widget,gboolean * transient)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
gail_focus_watcher(GSignalInvocationHint * ihint,guint n_param_values,const GValue * param_values,gpointer data)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
gail_select_watcher(GSignalInvocationHint * ihint,guint n_param_values,const GValue * param_values,gpointer data)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
gail_finish_select(GtkWidget * widget)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
333 return;
334 }
335 /*
336 * If we are waiting to report focus on a menubar or a menu item
337 * because of a previous deselect, cancel it.
338 */
339 if (was_deselect &&
340 focus_notify_handler &&
341 next_focus_widget &&
342 (GTK_IS_MENU_BAR (next_focus_widget) ||
343 GTK_IS_MENU_ITEM (next_focus_widget)))
344 {
345 void *vp_next_focus_widget = &next_focus_widget;
346 g_source_remove (focus_notify_handler);
347 g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
348 next_focus_widget = NULL;
349 focus_notify_handler = 0;
350 was_deselect = FALSE;
351 }
352 }
353 /*
354 * If previously focused widget is not a GtkMenuItem or a GtkMenu,
355 * keep track of it so we can return to it after menubar is deactivated
356 */
357 if (_focus_widget &&
358 !GTK_IS_MENU_ITEM (_focus_widget) &&
359 !GTK_IS_MENU (_focus_widget))
360 {
361 void *vp_focus_before_menu = &focus_before_menu;
362 focus_before_menu = _focus_widget;
363 g_object_add_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
364
365 }
366 gail_focus_notify_when_idle (widget);
367
368 return;
369 }
370
371 static void
gail_map_cb(GtkWidget * widget)372 gail_map_cb (GtkWidget *widget)
373 {
374 gail_finish_select (widget);
375 }
376
377 static void
gail_map_submenu_cb(GtkWidget * widget)378 gail_map_submenu_cb (GtkWidget *widget)
379 {
380 if (GTK_IS_MENU (widget))
381 {
382 GtkWidget *parent_menu_item;
383
384 parent_menu_item = gtk_menu_get_attach_widget (GTK_MENU (widget));
385 if (parent_menu_item)
386 gail_finish_select (parent_menu_item);
387 }
388 }
389
390
391 static gboolean
gail_deselect_watcher(GSignalInvocationHint * ihint,guint n_param_values,const GValue * param_values,gpointer data)392 gail_deselect_watcher (GSignalInvocationHint *ihint,
393 guint n_param_values,
394 const GValue *param_values,
395 gpointer data)
396 {
397 GObject *object;
398 GtkWidget *widget;
399 GtkWidget *menu_shell;
400
401 object = g_value_get_object (param_values + 0);
402 g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
403
404 widget = GTK_WIDGET (object);
405
406 if (!GTK_IS_MENU_ITEM (widget))
407 return TRUE;
408
409 if (subsequent_focus_widget == widget)
410 subsequent_focus_widget = NULL;
411
412 menu_shell = gtk_widget_get_parent (widget);
413 if (GTK_IS_MENU_SHELL (menu_shell))
414 {
415 GtkWidget *parent_menu_shell;
416
417 parent_menu_shell = gtk_menu_shell_get_parent_shell (GTK_MENU_SHELL (menu_shell));
418 if (parent_menu_shell)
419 {
420 GtkWidget *active_menu_item;
421
422 active_menu_item = gtk_menu_shell_get_selected_item (GTK_MENU_SHELL (parent_menu_shell));
423 if (active_menu_item)
424 {
425 gail_focus_notify_when_idle (active_menu_item);
426 }
427 }
428 else
429 {
430 if (!GTK_IS_MENU_BAR (menu_shell))
431 {
432 gail_focus_notify_when_idle (menu_shell);
433 }
434 }
435 }
436 was_deselect = TRUE;
437 return TRUE;
438 }
439
440 static gboolean
gail_switch_page_watcher(GSignalInvocationHint * ihint,guint n_param_values,const GValue * param_values,gpointer data)441 gail_switch_page_watcher (GSignalInvocationHint *ihint,
442 guint n_param_values,
443 const GValue *param_values,
444 gpointer data)
445 {
446 GObject *object;
447 GtkWidget *widget;
448
449 object = g_value_get_object (param_values + 0);
450 g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
451
452 widget = GTK_WIDGET (object);
453
454 if (!GTK_IS_NOTEBOOK (widget))
455 return TRUE;
456
457 if (gtk_notebook_get_current_page (GTK_NOTEBOOK (widget)) == -1)
458 return TRUE;
459
460 gail_focus_notify_when_idle (widget);
461 return TRUE;
462 }
463
464 static gboolean
gail_focus_idle_handler(gpointer data)465 gail_focus_idle_handler (gpointer data)
466 {
467 focus_notify_handler = 0;
468 /*
469 * The widget which was to receive focus may have been removed
470 */
471 if (!next_focus_widget)
472 {
473 if (next_focus_widget != data)
474 return FALSE;
475 }
476 else
477 {
478 void *vp_next_focus_widget = &next_focus_widget;
479 g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
480 next_focus_widget = NULL;
481 }
482
483 gail_focus_notify (data);
484
485 return FALSE;
486 }
487
488 static void
gail_focus_notify(GtkWidget * widget)489 gail_focus_notify (GtkWidget *widget)
490 {
491 AtkObject *atk_obj;
492 gboolean transient;
493
494 if (widget != _focus_widget)
495 {
496 if (_focus_widget)
497 {
498 void *vp_focus_widget = &_focus_widget;
499 g_object_remove_weak_pointer (G_OBJECT (_focus_widget), vp_focus_widget);
500 }
501 _focus_widget = widget;
502 if (_focus_widget)
503 {
504 void *vp_focus_widget = &_focus_widget;
505 g_object_add_weak_pointer (G_OBJECT (_focus_widget), vp_focus_widget);
506 /*
507 * The UI may not have been updated yet; e.g. in gtkhtml2
508 * html_view_layout() is called in a idle handler
509 */
510 if (_focus_widget == focus_before_menu)
511 {
512 void *vp_focus_before_menu = &focus_before_menu;
513 g_object_remove_weak_pointer (G_OBJECT (focus_before_menu), vp_focus_before_menu);
514 focus_before_menu = NULL;
515 }
516 }
517 gail_focus_notify_when_idle (_focus_widget);
518 }
519 else
520 {
521 if (_focus_widget)
522 atk_obj = get_accessible_for_widget (_focus_widget, &transient);
523 else
524 atk_obj = NULL;
525 /*
526 * Do not report focus on redundant object
527 */
528 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
529 if (atk_obj &&
530 (atk_object_get_role(atk_obj) != ATK_ROLE_REDUNDANT_OBJECT))
531 atk_focus_tracker_notify (atk_obj);
532 G_GNUC_END_IGNORE_DEPRECATIONS;
533 if (atk_obj && transient)
534 g_object_unref (atk_obj);
535 if (subsequent_focus_widget)
536 {
537 GtkWidget *tmp_widget = subsequent_focus_widget;
538 subsequent_focus_widget = NULL;
539 gail_focus_notify_when_idle (tmp_widget);
540 }
541 }
542 }
543
544 static void
gail_focus_notify_when_idle(GtkWidget * widget)545 gail_focus_notify_when_idle (GtkWidget *widget)
546 {
547 if (focus_notify_handler)
548 {
549 if (widget)
550 {
551 /*
552 * Ignore focus request when menu item is going to be focused.
553 * See bug #124232.
554 */
555 if (GTK_IS_MENU_ITEM (next_focus_widget) && !GTK_IS_MENU_ITEM (widget))
556 return;
557
558 if (next_focus_widget)
559 {
560 if (GTK_IS_MENU_ITEM (next_focus_widget) && GTK_IS_MENU_ITEM (widget))
561 {
562 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (next_focus_widget)) == gtk_widget_get_parent (widget))
563 {
564 if (subsequent_focus_widget)
565 g_assert_not_reached ();
566 subsequent_focus_widget = widget;
567 return;
568 }
569 }
570 }
571 g_source_remove (focus_notify_handler);
572 if (next_focus_widget)
573 {
574 void *vp_next_focus_widget = &next_focus_widget;
575 g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
576 next_focus_widget = NULL;
577 }
578 }
579 else
580 /*
581 * Ignore if focus is being set to NULL and we are waiting to set focus
582 */
583 return;
584 }
585
586 if (widget)
587 {
588 void *vp_next_focus_widget = &next_focus_widget;
589 next_focus_widget = widget;
590 g_object_add_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
591 }
592 else
593 {
594 /*
595 * We are about to report focus as NULL so remove the weak pointer
596 * for the widget we were waiting to report focus on.
597 */
598 if (next_focus_widget)
599 {
600 void *vp_next_focus_widget = &next_focus_widget;
601 g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
602 next_focus_widget = NULL;
603 }
604 }
605
606 focus_notify_handler = gdk_threads_add_idle (gail_focus_idle_handler, widget);
607 g_source_set_name_by_id (focus_notify_handler, "[gtk+] gail_focus_idle_handler");
608 }
609
610 static gboolean
gail_deactivate_watcher(GSignalInvocationHint * ihint,guint n_param_values,const GValue * param_values,gpointer data)611 gail_deactivate_watcher (GSignalInvocationHint *ihint,
612 guint n_param_values,
613 const GValue *param_values,
614 gpointer data)
615 {
616 GObject *object;
617 GtkWidget *widget;
618 GtkMenuShell *shell;
619 GtkWidget *focus = NULL;
620
621 object = g_value_get_object (param_values + 0);
622 g_return_val_if_fail (GTK_IS_WIDGET(object), FALSE);
623 widget = GTK_WIDGET (object);
624
625 g_return_val_if_fail (GTK_IS_MENU_SHELL(widget), TRUE);
626 shell = GTK_MENU_SHELL(widget);
627 if (! gtk_menu_shell_get_parent_shell (shell))
628 focus = focus_before_menu;
629
630 /*
631 * If we are waiting to report focus on a menubar or a menu item
632 * because of a previous deselect, cancel it.
633 */
634 if (was_deselect &&
635 focus_notify_handler &&
636 next_focus_widget &&
637 (GTK_IS_MENU_BAR (next_focus_widget) ||
638 GTK_IS_MENU_ITEM (next_focus_widget)))
639 {
640 void *vp_next_focus_widget = &next_focus_widget;
641 g_source_remove (focus_notify_handler);
642 g_object_remove_weak_pointer (G_OBJECT (next_focus_widget), vp_next_focus_widget);
643 next_focus_widget = NULL;
644 focus_notify_handler = 0;
645 was_deselect = FALSE;
646 }
647 gail_focus_notify_when_idle (focus);
648
649 return TRUE;
650 }
651
652 static void
gail_focus_tracker_init(void)653 gail_focus_tracker_init (void)
654 {
655 static gboolean emission_hooks_added = FALSE;
656
657 if (!emission_hooks_added)
658 {
659 /*
660 * We cannot be sure that the classes exist so we make sure that they do.
661 */
662 g_type_class_ref (GTK_TYPE_WIDGET);
663 g_type_class_ref (GTK_TYPE_MENU_ITEM);
664 g_type_class_ref (GTK_TYPE_MENU_SHELL);
665 g_type_class_ref (GTK_TYPE_NOTEBOOK);
666
667 /*
668 * We listen for event_after signal and then check that the
669 * event was a focus in event so we get called after the event.
670 */
671 g_signal_add_emission_hook (
672 g_signal_lookup ("event-after", GTK_TYPE_WIDGET), 0,
673 gail_focus_watcher, NULL, (GDestroyNotify) NULL);
674 /*
675 * A "select" signal is emitted when arrow key is used to
676 * move to a list item in the popup window of a GtkCombo or
677 * a menu item in a menu.
678 */
679 g_signal_add_emission_hook (
680 g_signal_lookup ("select", GTK_TYPE_MENU_ITEM), 0,
681 gail_select_watcher, NULL, (GDestroyNotify) NULL);
682
683 /*
684 * A "deselect" signal is emitted when arrow key is used to
685 * move from a menu item in a menu to the parent menu.
686 */
687 g_signal_add_emission_hook (
688 g_signal_lookup ("deselect", GTK_TYPE_MENU_ITEM), 0,
689 gail_deselect_watcher, NULL, (GDestroyNotify) NULL);
690
691 /*
692 * We listen for deactivate signals on menushells to determine
693 * when the "focus" has left the menus.
694 */
695 g_signal_add_emission_hook (
696 g_signal_lookup ("deactivate", GTK_TYPE_MENU_SHELL), 0,
697 gail_deactivate_watcher, NULL, (GDestroyNotify) NULL);
698
699 /*
700 * We listen for "switch-page" signal on a GtkNotebook to notify
701 * when page has changed because of clicking on a notebook tab.
702 */
703 g_signal_add_emission_hook (
704 g_signal_lookup ("switch-page", GTK_TYPE_NOTEBOOK), 0,
705 gail_switch_page_watcher, NULL, (GDestroyNotify) NULL);
706 emission_hooks_added = TRUE;
707 }
708 }
709
710 static void
gail_focus_object_destroyed(gpointer data)711 gail_focus_object_destroyed (gpointer data)
712 {
713 GObject *obj;
714
715 obj = G_OBJECT (data);
716 g_object_set_qdata (obj, quark_focus_object, NULL);
717 g_object_unref (obj);
718 }
719
720 static void
gail_focus_tracker(AtkObject * focus_object)721 gail_focus_tracker (AtkObject *focus_object)
722 {
723 /*
724 * Do not report focus on redundant object
725 */
726 if (focus_object &&
727 (atk_object_get_role(focus_object) != ATK_ROLE_REDUNDANT_OBJECT))
728 {
729 AtkObject *old_focus_object;
730
731 if (!GTK_IS_ACCESSIBLE (focus_object))
732 {
733 AtkObject *parent;
734
735 parent = focus_object;
736 while (1)
737 {
738 parent = atk_object_get_parent (parent);
739 if (parent == NULL)
740 break;
741 if (GTK_IS_ACCESSIBLE (parent))
742 break;
743 }
744
745 if (parent)
746 {
747 gail_set_focus_object (focus_object, parent);
748 }
749 }
750 else
751 {
752 old_focus_object = g_object_get_qdata (G_OBJECT (focus_object), quark_focus_object);
753 if (old_focus_object)
754 {
755 g_object_weak_unref (G_OBJECT (old_focus_object),
756 (GWeakNotify) gail_focus_object_destroyed,
757 focus_object);
758 g_object_set_qdata (G_OBJECT (focus_object), quark_focus_object, NULL);
759 g_object_unref (G_OBJECT (focus_object));
760 }
761 }
762 }
763 }
764
765 static void
gail_set_focus_widget(GtkWidget * focus_widget,GtkWidget * widget)766 gail_set_focus_widget (GtkWidget *focus_widget,
767 GtkWidget *widget)
768 {
769 AtkObject *focus_obj;
770 AtkObject *obj;
771
772 focus_obj = gtk_widget_get_accessible (focus_widget);
773 obj = gtk_widget_get_accessible (widget);
774 gail_set_focus_object (focus_obj, obj);
775 }
776
777 static void
gail_set_focus_object(AtkObject * focus_obj,AtkObject * obj)778 gail_set_focus_object (AtkObject *focus_obj,
779 AtkObject *obj)
780 {
781 AtkObject *old_focus_obj;
782
783 old_focus_obj = g_object_get_qdata (G_OBJECT (obj), quark_focus_object);
784 if (old_focus_obj != obj)
785 {
786 if (old_focus_obj)
787 g_object_weak_unref (G_OBJECT (old_focus_obj),
788 (GWeakNotify) gail_focus_object_destroyed,
789 obj);
790 else
791 /*
792 * We call g_object_ref as if obj is destroyed
793 * while the weak reference exists then destroying the
794 * focus_obj would cause gail_focus_object_destroyed to be
795 * called when obj is not a valid GObject.
796 */
797 g_object_ref (obj);
798
799 g_object_weak_ref (G_OBJECT (focus_obj),
800 (GWeakNotify) gail_focus_object_destroyed,
801 obj);
802 g_object_set_qdata (G_OBJECT (obj), quark_focus_object, focus_obj);
803 }
804 }
805
806 static gboolean
state_event_watcher(GSignalInvocationHint * hint,guint n_param_values,const GValue * param_values,gpointer data)807 state_event_watcher (GSignalInvocationHint *hint,
808 guint n_param_values,
809 const GValue *param_values,
810 gpointer data)
811 {
812 GObject *object;
813 GtkWidget *widget;
814 AtkObject *atk_obj;
815 AtkObject *parent;
816 GdkEventWindowState *event;
817 gchar *signal_name;
818
819 object = g_value_get_object (param_values + 0);
820 if (!GTK_IS_WINDOW (object))
821 return FALSE;
822
823 event = g_value_get_boxed (param_values + 1);
824 if (event->type == GDK_WINDOW_STATE)
825 return FALSE;
826 widget = GTK_WIDGET (object);
827
828 if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
829 signal_name = "maximize";
830 else if (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED)
831 signal_name = "minimize";
832 else if (event->new_window_state == 0)
833 signal_name = "restore";
834 else
835 return TRUE;
836
837 atk_obj = gtk_widget_get_accessible (widget);
838 if (GTK_IS_WINDOW_ACCESSIBLE (atk_obj))
839 {
840 parent = atk_object_get_parent (atk_obj);
841 if (parent == atk_get_root ())
842 g_signal_emit_by_name (atk_obj, signal_name);
843
844 return TRUE;
845 }
846
847 return FALSE;
848 }
849
850 static gboolean
configure_event_watcher(GSignalInvocationHint * hint,guint n_param_values,const GValue * param_values,gpointer data)851 configure_event_watcher (GSignalInvocationHint *hint,
852 guint n_param_values,
853 const GValue *param_values,
854 gpointer data)
855 {
856 GtkAllocation allocation;
857 GObject *object;
858 GtkWidget *widget;
859 AtkObject *atk_obj;
860 AtkObject *parent;
861 GdkEvent *event;
862 gchar *signal_name;
863
864 object = g_value_get_object (param_values + 0);
865 if (!GTK_IS_WINDOW (object))
866 return FALSE;
867
868 event = g_value_get_boxed (param_values + 1);
869 if (event->type != GDK_CONFIGURE)
870 return FALSE;
871 widget = GTK_WIDGET (object);
872 gtk_widget_get_allocation (widget, &allocation);
873 if (allocation.x == ((GdkEventConfigure *)event)->x &&
874 allocation.y == ((GdkEventConfigure *)event)->y &&
875 allocation.width == ((GdkEventConfigure *)event)->width &&
876 allocation.height == ((GdkEventConfigure *)event)->height)
877 return TRUE;
878
879 if (allocation.width != ((GdkEventConfigure *)event)->width ||
880 allocation.height != ((GdkEventConfigure *)event)->height)
881 signal_name = "resize";
882 else
883 signal_name = "move";
884
885 atk_obj = gtk_widget_get_accessible (widget);
886 if (GTK_IS_WINDOW_ACCESSIBLE (atk_obj))
887 {
888 parent = atk_object_get_parent (atk_obj);
889 if (parent == atk_get_root ())
890 g_signal_emit_by_name (atk_obj, signal_name);
891
892 return TRUE;
893 }
894
895 return FALSE;
896 }
897
898 static gboolean
window_focus(GtkWidget * widget,GdkEventFocus * event)899 window_focus (GtkWidget *widget,
900 GdkEventFocus *event)
901 {
902 AtkObject *atk_obj;
903
904 g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
905
906 atk_obj = gtk_widget_get_accessible (widget);
907 g_signal_emit_by_name (atk_obj, event->in ? "activate" : "deactivate");
908
909 return FALSE;
910 }
911
912 static void
window_added(AtkObject * atk_obj,guint index,AtkObject * child)913 window_added (AtkObject *atk_obj,
914 guint index,
915 AtkObject *child)
916 {
917 GtkWidget *widget;
918
919 if (!GTK_IS_WINDOW_ACCESSIBLE (child))
920 return;
921
922 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (child));
923 if (!widget)
924 return;
925
926 g_signal_connect (widget, "focus-in-event", (GCallback) window_focus, NULL);
927 g_signal_connect (widget, "focus-out-event", (GCallback) window_focus, NULL);
928 g_signal_emit_by_name (child, "create");
929 }
930
931 static void
window_removed(AtkObject * atk_obj,guint index,AtkObject * child)932 window_removed (AtkObject *atk_obj,
933 guint index,
934 AtkObject *child)
935 {
936 GtkWidget *widget;
937 GtkWindow *window;
938
939 if (!GTK_IS_WINDOW_ACCESSIBLE (child))
940 return;
941
942 widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (child));
943 if (!widget)
944 return;
945
946 window = GTK_WINDOW (widget);
947 /*
948 * Deactivate window if it is still focused and we are removing it. This
949 * can happen when a dialog displayed by gok is removed.
950 */
951 if (gtk_window_is_active (window) && gtk_window_has_toplevel_focus (window))
952 g_signal_emit_by_name (child, "deactivate");
953
954 g_signal_handlers_disconnect_by_func (widget, (gpointer) window_focus, NULL);
955 g_signal_emit_by_name (child, "destroy");
956 }
957
958 static void
do_window_event_initialization(void)959 do_window_event_initialization (void)
960 {
961 AtkObject *root;
962
963 g_type_class_ref (GTK_TYPE_WINDOW_ACCESSIBLE);
964 g_signal_add_emission_hook (g_signal_lookup ("window-state-event", GTK_TYPE_WIDGET),
965 0, state_event_watcher, NULL, (GDestroyNotify) NULL);
966 g_signal_add_emission_hook (g_signal_lookup ("configure-event", GTK_TYPE_WIDGET),
967 0, configure_event_watcher, NULL, (GDestroyNotify) NULL);
968
969 root = atk_get_root ();
970 g_signal_connect (root, "children-changed::add", (GCallback) window_added, NULL);
971 g_signal_connect (root, "children-changed::remove", (GCallback) window_removed, NULL);
972 }
973
974 void
_gtk_accessibility_init(void)975 _gtk_accessibility_init (void)
976 {
977 if (initialized)
978 return;
979
980 initialized = TRUE;
981 quark_focus_object = g_quark_from_static_string ("gail-focus-object");
982
983 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
984 atk_focus_tracker_init (gail_focus_tracker_init);
985 focus_tracker_id = atk_add_focus_tracker (gail_focus_tracker);
986 G_GNUC_END_IGNORE_DEPRECATIONS;
987
988 _gtk_accessibility_override_atk_util ();
989 do_window_event_initialization ();
990
991 #ifdef HAVE_ATK_BRIDGE
992 atk_bridge_adaptor_init (NULL, NULL);
993 #endif
994
995 atk_misc_instance = g_object_new (GTK_TYPE_MISC_IMPL, NULL);
996 }
997