IMPORTANT: This article is being preserved for historical purposes. Current information and documentation about GNOME Accessibility can be found at: the GNOME Accessibility Project


Prev Next


Examples that Use the Accessibility API

As noted earlier, you should have little or no work to do to make your application accessible if you use the GTK widget set, or any other widget library that implements the ATK interfaces. The two most common things you may have to do in this case are:

  • provide descriptions of some controls and images using atk_object_set_description() or atk_image_set_description():
    • Example 3. Setting the accessible description for a button

      {
         AtkObject *obj;
         obj = gtk_widget_get_accessible (button);
         atk_object_set_description (obj, _("Opens Preferences dialog"));
      }
  • specify relationships between any unusual groupings of widgets, using atk_relation_new() and atk_relation_set_add():
    • Example 4. Specifying accessible relationship between two controls

      {
              GtkWidget *widget;
              GtkLabel *label;

              AtkObject *atk_widget, *atk_label;
              AtkRelationSet *relation_set;
              AtkRelation *relation;
              AtkObject *targets[1];

              atk_widget = gtk_widget_get_accessible (widget);
              atk_label = gtk_widget_get_accessible (GTK_WIDGET (label));

              relation_set = atk_object_ref_relation_set (atk_label);
              targets[0] = atk_widget;

              relation = atk_relation_new (targets, 1, ATK_RELATION_LABEL_FOR);
              atk_relation_set_add (relation_set, relation);
              g_object_unref (G_OBJECT (relation));
      }
  • Can we be more specific about when you will need to specify your own relations? Which relations does GAIL implement by default, other than button groups?

The examples in the rest of this section are mostly to give you of a flavour of the scope of the ATK. They cover techniques that you may never need to use as an application developer, although they may be of interest if you are writing your own custom widgets (see Making Custom Components Accessible) or if you want to write an assistive technology application.

Gtk Modules

Programs that make use of GAIL (the accessibility implementation library for GTK widgets) are written as GTK modules. GTK modules are loaded into the program space if the GTK_MODULES environment variable specifies the module library name(s). If there are multiple module libraries, separate them with colons. For example:

setenv GTK_MODULES "libgail:libtestprops"

All GTK modules have a gtk_module_init() function. What would typically go in this function?

Gathering accessibility information from an application

A program that wishes to make use of ATK calls would likely need to do one (or more) of the following things:

  1. Create an event watcher, for example with the atk_add_focus_tracker() function:

      atk_add_focus_tracker (_my_focus_tracker);
  • where _my_focus_tracker() is a function with this prototype:

    void _my_focus_tracker (AtkObject *aobject);

  1. Set up a global event listener, with atk_add_global_event_listener():

      mouse_watcher_focus_id =
         atk_add_global_event_listener(_my_global_listener,
         "Gtk:GtkWidget:enter_notify_event");
  • where _my_global_listener has the prototype of a Glib GSignalEmissionHook. This example would cause the _my_global_listener() to be called whenever an enter_notify_even signal occurs on a GtkWidget object.

  1. Access the ATK top-level object with the following function call.

      AtkObject *root_obj = atk_get_root();
  • This returns an AtkObject which contains all toplevel windows in the currently running program. The user could then navigate through the object heirarchy by accessing the root object's children, which corresponds to the toplevel windows.

Querying an AtkObject's Interfaces

Having located the AtkObject associated with an object in the application (e.g. by using gtk_widget_get_accessible()), you can find out what interfaces it implements in various ways:

  1. Use the supplied ATK_IS_... macros, for example:
    • ATK_IS_ACTION(atkobj)
    • ATK_IS_COMPONENT(atkobj)
    • etc. (there is one for each interface)
    • If the macro returns TRUE, the interface calls can safely be made on that ATK object.
  2. Test the role of the AtkObject by calling atk_object_get_role(). Any given role implements a specific number of ATK APIs. For example...

Setting up an ATK Signal Handler

Using the column_inserted signal as an example:

table_column_inserted_id = g_signal_connect_closure_by_id (my_atk_obj, 
   g_signal_lookup ("column_inserted", G_OBJECT_TYPE (my_atk_obj)), 
   0, g_cclosure_new (G_CALLBACK (_my_table_column_inserted_func), 
   NULL, NULL), FALSE); 

This will cause _my_table_column_inserted_func() to be called whenever a column_inserted signal is emitted on the AtkObject my_atk_object.

Connecting to a signal is slightly different if the signal supports detail. The children_changed signal supports the add detail. To connect to a signal when the add detail is also specified, this technique is used:

child_added_id = g_signal_connect_closure (my_atk_obj,
   "children_changed::add", 
   g_cclosure_new (G_CALLBACK (_my_children_changed_func), 
    NULL, NULL), FALSE); 

This will cause _my_children_changed_func() to be called whenever a children_changed signal with the add detail is emitted on the AtkObject my_atk_obj.

Implementing an ATK Object

You will need to implement your own ATK objects for any widgets that do not already have an accessible implementation in GAIL (or the equivalent library for other widget sets). This should be implemented as a GTK module, which, as before, should be included in the GTK_MODULES environment variable so it is loaded at runtime.

Registry

For this example we will assume there is an object called GTK_TYPE_MYTYPE. The ATK implementation will be called MYATKIMP_TYPE_MYTYPE. A factory will be needed which will be called MYATKIMP_TYPE_MYTYPE_FACTORY.

To register an ATK implementation of a GTK object, these steps must be followed in the module's gtk_module_init() function:

  1. Access the default registry:

      default_registry = atk_get_default_registry();
  1. Register the ATK object in the gtk_module_init() function of this module by making this function call:

      atk_registry_set_factory_type (default_registry, GTK_TYPE_MYTYPE, 
                                     MYATKIMP_TYPE_MYTYPE_FACTORY); 

This will register the AtkObject implementation of GTK_TYPE_MYTYPE to MYATKIMP_TYPE_MYTYPE_FACTORY. This factory will be implemented so that it knows how to build objects of type MYATKIMP_TYPE_MYTYPE.

Factory

The factory must be implemented as a child of class type ATK_TYPE_OBJECT_FACTORY and must implement the function create_accessible(). This function must create an appropriate AtkObject. A factory can be used to create more than one type of object, in which case its create_accessible() function will need to be smart enough to build and return the correct AtkObject.

Example code snippets for factory here...?

ATK Implemetation for a Specific Object

All GObjects implement a get_type() function. Using the above example the naming convention for this function name would be myatkimp_mytype_get_type().

In this function, you specify which interfaces your object implements. If the following logic were included in this get_type() function, this object would implement the ATK_TEXT interface:

Example 5. Sample get_type() function

static const GInterfaceInfo atk_text_info = 
{ 
   (GInterfaceInitFunc) atk_text_interface_init, 
   (GInterfaceFinalizeFunc) NULL, 
   NULL 
}; 

g_type_add_interface_static (type, ATK_TYPE_TEXT, 
                             &atk_text_info); 

The function atk_text_interface_init(), which has the following prototype, would need to be implemented:

void atk_text_interface_init (AtkTextIface *iface);

This function would connect the interface function calls to the specific implementation as follows:

Example 6. Connecting custom interface calls to an AtkObject implementation

void 
atk_text_interface_init (AtkTextIface *iface) 
{ 
   g_return_if_fail (iface != NULL); 
   iface->get_text = myatkimp_mytype_get_text; 
   iface->get_character_at_offset = myatkimp_mytype_get_character_at_offset; 
   ... 
}

Then the functions myatkimp_mytype_get_text(), myatkimp_mytype_get_character_at_offset(), and the rest of the ATK_TEXT interface functions would need to be implemented.

AtkObject Implementation

AtkObjects are GObjects, and all GObjects need to specify the get_type() function. Here is an example that sets up a class and instance initializer. This get_type() function also specifies that the object implements ATK_TEXT and specifies the parent object to be MYATKIMP_MYPARENTTYPE.

Example 7. Sample get_type() implementation

GType 
myatkimp_mytype_get_type (void) 
{ 
   static GType type = 0; 

   if (!type) 
   { 
      static const GTypeInfo tinfo = 
      { 
         sizeof (GailLabelClass), 
         (GBaseInitFunc) NULL,                              /* base init */ 
         (GBaseFinalizeFunc) NULL,                          /* base finalize */
         (GClassInitFunc) myatkimp_mytype_class_init,       /* class init */ 
         (GClassFinalizeFunc) NULL,                         /* class finalize */ 
         NULL,                                              /* class data */ 
         sizeof (GailLabel),                                /* instance size */ 
         0,                                                 /* nb preallocs */ 
         (GInstanceInitFunc) myatkimp_mytype_instance_init, /* instance init */ 
         NULL                                               /* value table */ 
      }; 

      /* Set up atk_text_info structure used below */ 
      static const GInterfaceInfo atk_text_info = 
      { 
         (GInterfaceInitFunc) atk_text_interface_init, 
         (GInterfaceFinalizeFunc) NULL, 
         NULL 
      }; 

      /* Set up typename and specify parent type */ 
      type = g_type_register_static (MYATKIMP_MYPARENTTYPE, 
            "MyatkimpMytype", &tinfo, 0); 

      /* This class implements interface ATK_TYPE_TEXT */ 
      g_type_add_interface_static (type, ATK_TYPE_TEXT, 
                                   &atk_text_info); 
   } 
   return type; 
} 

Class/Instance Initializers

You will have to set up a class initializer for the GObject if your AtkObject implementation either:

  1. Redefines any function calls defined by the object's parent. This is typically necessary when an object needs to implement a function like atk_object_get_n_accessible_children(). This is necessary if the object has children, but they are not represented with widgets.
    • For example, if your ATK implementation needs to over-ride the AtkObject function get_name(), then the class initializer would look like:

      Example 8. Class initializer that overrides parent's get_name() function

      myatkimp_mytype_class_init (GailLabelClass *klass) 
      { 
        AtkObjectClass *class = ATK_OBJECT_CLASS (klass); 
        class->get_name = myatkimp_mytype_get_name; 
      } 
  1. Requires a parent->init, parent->notify_gtk, or parent->finalize function. This example defines all three:

    • Example 9. Class initializer that defines its own init(), notify_gtk() and finalize() functions

      static ParentObjectType *parent_class = NULL; 

      myatkimp_mytype_class_init (GailLabelClass *klass) 
      { 
         ParentObjectType *parent_class = (ParentObjectType*)klass; 

         /* 
          * Caching the parent_class is necessary if the init, 
          * notify_gtk, or finalize functions are set up. 
          */ 
          parent_class = g_type_class_ref (MYATKIMP_TYPE_PARENT); 

          parent_class->init = myatkimp_mytype_widget_init; 
          parent_class->notify_gtk = myatkimp_mytype_real_notify_gtk; 
          parent_class->finalize = myatkimp_mytype_finalize; 
      }
  1. parent->init

    • A parent->init() function may be necessary if the ATK implementation needs to do either of the following:

      1. Cache any data obtained from a backing GTK widget.
      2. Listen to any signals from the backing GTK widget.
      Here is an example of both:

      Example 10. A custom init() function

            void 
            gail_tree_view_widget_init (MyatkimpMytype  *mytype, 
                                        GtkWidget       *gtk_widget) 
            { 
               /* Make sure to call the parent's init function */ 
               parent_class->init (widget, gtk_widget); 
               
               /* Cache a value in the ATK implementation */ 
               mytype->cached_value = gtk_widget_function_call(); 

               /* Listen to a signal */ 
               gtk_signal_connect (GTK_OBJECT (gtk_widget), 
                                   "signal-type", 
                                   GTK_SIGNAL_FUNC (_myatkimp_mytype_signal_type), 
                                   NULL); 
            } 
  • In this example, if the specified signal-type signal were generated on the backing gtk_widget, then the _myatkimp_mytype_signal_type() function would be called.
  1. parent->notify_gtk

    • If the ATK implementation needs to listen to any property notifications on the backing GTK object, a parent->notify_gtk() function may be necessary. For example:

      Example 11. A custom notify_gtk() function

            void 
            myatkimp_mytype_real_notify_gtk (GObject    *obj, 
                                             GParamSpec *pspec) 
            { 
               GtkWidget *widget = GTK_WIDGET (obj); 
               AtkObject* atk_obj = gtk_widget_get_accessible (widget); 

               if (strcmp (pspec->name, "property-of-interest") == 0) 
               { 
                  /* Handle the property change. */ 
               } 
               else 
               { 
                  parent_class->notify_gtk (obj, pspec); 
               } 
            } 
  1. parent->finalize

    • If it is necessary to free any data when a GObject instance is destroyed, then a finalize() function is needed to free the memory. For example:

      Example 12. A custom finalize() function

            void 
            myatkimp_mytype_finalize (GObject *object) 
            { 
               MyAtkimpMyType *my_type = MYATKIMP_MYTYPE (object); 

               g_object_unref (my_type->cached_value); 
               G_OBJECT_CLASS (parent_class)->finalize (object); 
            } 


Home Prev Next

Accessibility/Documentation/GNOME2/GNOME Accessibility for Developers/Examples that Use the Accessibility API (last edited 2011-07-21 17:59:16 by JoanmarieDiggs)