Implement accessible support, fixes #454653.

2008-01-25  Johan Dahlin  <johan@gnome.org>

        * gtk/gtkwidget.c: (gtk_widget_buildable_interface_init),
        (gtk_widget_buildable_get_internal_child), (free_action),
        (free_relation), (gtk_widget_buildable_parser_finished),
        (accessibility_start_element),
        (gtk_widget_buildable_custom_tag_start),
        (gtk_widget_buildable_custom_finished):
        Implement accessible support, fixes #454653.

        * gtk/gtk-builder-convert:
        Add support for migrating old glade files
        
        * tests/buildertest.c: (test_widget), (test_file):
        Add accessible tests and improve the test_file function to display
        toplevels and run dialogs.


svn path=/trunk/; revision=19403
This commit is contained in:
Johan Dahlin
2008-01-25 16:17:38 +00:00
committed by Johan Dahlin
parent 7b8050aff2
commit fddc9b8561
7 changed files with 464 additions and 74 deletions

View File

@ -251,6 +251,9 @@ static void gtk_widget_buildable_interface_init (GtkBuildableIfa
static void gtk_widget_buildable_set_name (GtkBuildable *buildable,
const gchar *name);
static const gchar * gtk_widget_buildable_get_name (GtkBuildable *buildable);
static GObject * gtk_widget_buildable_get_internal_child (GtkBuildable *buildable,
GtkBuilder *builder,
const gchar *childname);
static void gtk_widget_buildable_set_buildable_property (GtkBuildable *buildable,
GtkBuilder *builder,
const gchar *name,
@ -8786,17 +8789,20 @@ gtk_widget_ref_accessible (AtkImplementor *implementor)
/*
* GtkBuildable implementation
*/
static GQuark quark_builder_has_default = 0;
static GQuark quark_builder_has_focus = 0;
static GQuark quark_builder_has_default = 0;
static GQuark quark_builder_has_focus = 0;
static GQuark quark_builder_atk_relations = 0;
static void
gtk_widget_buildable_interface_init (GtkBuildableIface *iface)
{
quark_builder_has_default = g_quark_from_static_string ("gtk-builder-has-default");
quark_builder_has_focus = g_quark_from_static_string ("gtk-builder-has-focus");
quark_builder_atk_relations = g_quark_from_static_string ("gtk-builder-atk-relations");
iface->set_name = gtk_widget_buildable_set_name;
iface->get_name = gtk_widget_buildable_get_name;
iface->get_internal_child = gtk_widget_buildable_get_internal_child;
iface->set_buildable_property = gtk_widget_buildable_set_buildable_property;
iface->parser_finished = gtk_widget_buildable_parser_finished;
iface->custom_tag_start = gtk_widget_buildable_custom_tag_start;
@ -8816,6 +8822,17 @@ gtk_widget_buildable_get_name (GtkBuildable *buildable)
return gtk_widget_get_name (GTK_WIDGET (buildable));
}
static GObject *
gtk_widget_buildable_get_internal_child (GtkBuildable *buildable,
GtkBuilder *builder,
const gchar *childname)
{
if (strcmp (childname, "accessible") == 0)
return G_OBJECT (gtk_widget_get_accessible (GTK_WIDGET (buildable)));
return NULL;
}
static void
gtk_widget_buildable_set_buildable_property (GtkBuildable *buildable,
GtkBuilder *builder,
@ -8832,16 +8849,223 @@ gtk_widget_buildable_set_buildable_property (GtkBuildable *buildable,
g_object_set_property (G_OBJECT (buildable), name, value);
}
typedef struct {
gchar *action_name;
gchar *description;
} AtkActionData;
typedef struct {
gchar *target;
gchar *type;
} AtkRelationData;
static void
free_action (AtkActionData *data, gpointer user_data)
{
g_free (data->action_name);
g_free (data->description);
g_slice_free (AtkActionData, data);
}
static void
free_relation (AtkRelationData *data, gpointer user_data)
{
g_free (data->target);
g_free (data->type);
g_slice_free (AtkRelationData, data);
}
static void
gtk_widget_buildable_parser_finished (GtkBuildable *buildable,
GtkBuilder *builder)
{
GSList *atk_relations;
if (g_object_get_qdata (G_OBJECT (buildable), quark_builder_has_default))
gtk_widget_grab_default (GTK_WIDGET (buildable));
if (g_object_get_qdata (G_OBJECT (buildable), quark_builder_has_focus))
gtk_widget_grab_focus (GTK_WIDGET (buildable));
atk_relations = g_object_get_qdata (G_OBJECT (buildable),
quark_builder_atk_relations);
if (atk_relations)
{
AtkObject *accessible;
AtkRelationSet *relation_set;
GSList *l;
GObject *target;
AtkRelationType relation_type;
AtkObject *target_accessible;
accessible = gtk_widget_get_accessible (GTK_WIDGET (buildable));
relation_set = atk_object_ref_relation_set (accessible);
for (l = atk_relations; l; l = l->next)
{
AtkRelationData *relation = (AtkRelationData*)l->data;
target = gtk_builder_get_object (builder, relation->target);
if (!target)
{
g_warning ("Target object %s in <relation> does not exist",
relation->target);
continue;
}
target_accessible = gtk_widget_get_accessible (GTK_WIDGET (target));
g_assert (target_accessible != NULL);
relation_type = atk_relation_type_for_name (relation->type);
if (relation_type == ATK_RELATION_NULL)
{
g_warning ("<relation> type %s not found",
relation->type);
continue;
}
atk_relation_set_add_relation_by_type (relation_set, relation_type,
target_accessible);
}
g_object_unref (relation_set);
g_slist_foreach (atk_relations, (GFunc)free_relation, NULL);
g_slist_free (atk_relations);
g_object_set_qdata (G_OBJECT (buildable), quark_builder_atk_relations,
NULL);
}
}
typedef struct {
GSList *actions;
GSList *relations;
} AccessibilitySubParserData;
static void
accessibility_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **names,
const gchar **values,
gpointer user_data,
GError **error)
{
AccessibilitySubParserData *data = (AccessibilitySubParserData*)user_data;
guint i;
gint line_number, char_number;
if (strcmp (element_name, "relation") == 0)
{
gchar *target = NULL;
gchar *type = NULL;
AtkRelationData *relation;
for (i = 0; names[i]; i++)
{
if (strcmp (names[i], "target") == 0)
target = g_strdup (values[i]);
else if (strcmp (names[i], "type") == 0)
type = g_strdup (values[i]);
else
{
g_markup_parse_context_get_position (context,
&line_number,
&char_number);
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_ATTRIBUTE,
"%s:%d:%d '%s' is not a valid attribute of <%s>",
"<input>",
line_number, char_number, names[i], "relation");
g_free (target);
g_free (type);
return;
}
}
if (!target || !type)
{
g_markup_parse_context_get_position (context,
&line_number,
&char_number);
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
"%s:%d:%d <%s> requires attribute \"%s\"",
"<input>",
line_number, char_number, "relation",
type ? "target" : "type");
g_free (target);
g_free (type);
return;
}
relation = g_slice_new (AtkRelationData);
relation->target = target;
relation->type = type;
data->relations = g_slist_prepend (data->relations, relation);
}
else if (strcmp (element_name, "action") == 0)
{
gchar *action_name = NULL;
gchar *description = NULL;
AtkActionData *action;
for (i = 0; names[i]; i++)
{
if (strcmp (names[i], "action_name") == 0)
action_name = g_strdup (values[i]);
else if (strcmp (names[i], "description") == 0)
description = g_strdup (values[i]);
else
{
g_markup_parse_context_get_position (context,
&line_number,
&char_number);
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_INVALID_ATTRIBUTE,
"%s:%d:%d '%s' is not a valid attribute of <%s>",
"<input>",
line_number, char_number, names[i], "action");
g_free (action_name);
g_free (description);
return;
}
}
if (!action_name || !description)
{
g_markup_parse_context_get_position (context,
&line_number,
&char_number);
g_set_error (error,
GTK_BUILDER_ERROR,
GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
"%s:%d:%d <%s> requires attribute \"%s\"",
"<input>",
line_number, char_number, "action",
description ? "action_name" : "description");
g_free (action_name);
g_free (description);
return;
}
action = g_slice_new (AtkActionData);
action->action_name = action_name;
action->description = description;
data->actions = g_slist_prepend (data->actions, action);
}
else if (strcmp (element_name, "accessibility") == 0)
;
else
g_warning ("Unsupported tag for GtkWidget: %s\n", element_name);
}
static const GMarkupParser accessibility_parser =
{
accessibility_start_element,
};
typedef struct {
GObject *object;
guint key;
@ -8894,12 +9118,6 @@ static const GMarkupParser accel_group_parser =
accel_group_start_element,
};
static const GMarkupParser accessibility_parser =
{
NULL,
};
static gboolean
gtk_widget_buildable_custom_tag_start (GtkBuildable *buildable,
GtkBuilder *builder,
@ -8908,31 +9126,25 @@ gtk_widget_buildable_custom_tag_start (GtkBuildable *buildable,
GMarkupParser *parser,
gpointer *data)
{
AccelGroupParserData *parser_data;
g_assert (buildable);
if (strcmp (tagname, "accelerator") == 0)
{
AccelGroupParserData *parser_data;
parser_data = g_slice_new0 (AccelGroupParserData);
parser_data->object = g_object_ref (buildable);
*parser = accel_group_parser;
*data = parser_data;
return TRUE;
}
else if (strcmp (tagname, "accessibility") == 0)
if (strcmp (tagname, "accessibility") == 0)
{
static gboolean warning_showed = FALSE;
if (!warning_showed)
{
g_warning ("<accessibility> is being ignored,\n"
"see http://bugzilla.gnome.org/show_bug.cgi?id=454653\n");
warning_showed = TRUE;
}
AccessibilitySubParserData *parser_data;
parser_data = g_slice_new0 (AccessibilitySubParserData);
*parser = accessibility_parser;
*data = NULL;
*data = parser_data;
return TRUE;
}
return FALSE;
@ -8945,17 +9157,18 @@ gtk_widget_buildable_custom_finished (GtkBuildable *buildable,
const gchar *tagname,
gpointer user_data)
{
AccelGroupParserData *data;
AccelGroupParserData *accel_data;
AccessibilitySubParserData *a11y_data;
GtkWidget *toplevel;
GSList *accel_groups;
GtkAccelGroup *accel_group;
if (strcmp (tagname, "accelerator") == 0)
{
data = (AccelGroupParserData*)user_data;
g_assert (data->object);
accel_data = (AccelGroupParserData*)user_data;
g_assert (accel_data->object);
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (data->object));
toplevel = gtk_widget_get_toplevel (GTK_WIDGET (accel_data->object));
accel_groups = gtk_accel_groups_from_object (G_OBJECT (toplevel));
if (g_slist_length (accel_groups) == 0)
{
@ -8967,15 +9180,56 @@ gtk_widget_buildable_custom_finished (GtkBuildable *buildable,
g_assert (g_slist_length (accel_groups) == 1);
accel_group = g_slist_nth_data (accel_groups, 0);
}
gtk_widget_add_accelerator (GTK_WIDGET(data->object),
data->signal,
gtk_widget_add_accelerator (GTK_WIDGET (accel_data->object),
accel_data->signal,
accel_group,
data->key,
data->modifiers,
accel_data->key,
accel_data->modifiers,
GTK_ACCEL_VISIBLE);
g_object_unref (data->object);
g_free (data->signal);
g_slice_free (AccelGroupParserData, data);
g_object_unref (accel_data->object);
g_free (accel_data->signal);
g_slice_free (AccelGroupParserData, accel_data);
}
else if (strcmp (tagname, "accessibility") == 0)
{
a11y_data = (AccessibilitySubParserData*)user_data;
if (a11y_data->actions)
{
AtkObject *accessible;
AtkAction *action;
gint i, n_actions;
GSList *l;
accessible = gtk_widget_get_accessible (GTK_WIDGET (buildable));
action = ATK_ACTION (accessible);
n_actions = atk_action_get_n_actions (action);
for (l = a11y_data->actions; l; l = l->next)
{
AtkActionData *action_data = (AtkActionData*)l->data;
for (i = 0; i < n_actions; i++)
if (strcmp (atk_action_get_name (action, i),
action_data->action_name) == 0)
break;
if (i < n_actions)
atk_action_set_description (action, i,
action_data->description);
}
g_slist_foreach (a11y_data->actions, (GFunc)free_action, NULL);
g_slist_free (a11y_data->actions);
}
if (a11y_data->relations)
g_object_set_qdata (G_OBJECT (buildable), quark_builder_atk_relations,
a11y_data->relations);
g_slice_free (AccessibilitySubParserData, a11y_data);
}
}