source: trunk/third/gail/gail/gail.c @ 20902

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