1485 lines
49 KiB
C
1485 lines
49 KiB
C
/* gtkatcontext.c: Assistive technology context
|
|
*
|
|
* Copyright 2020 GNOME Foundation
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* GtkATContext:
|
|
*
|
|
* `GtkATContext` is an abstract class provided by GTK to communicate to
|
|
* platform-specific assistive technologies API.
|
|
*
|
|
* Each platform supported by GTK implements a `GtkATContext` subclass, and
|
|
* is responsible for updating the accessible state in response to state
|
|
* changes in `GtkAccessible`.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkatcontextprivate.h"
|
|
|
|
#include "gtkaccessiblevalueprivate.h"
|
|
#include "gtkaccessibleprivate.h"
|
|
#include "gtkdebug.h"
|
|
#include "gtkprivate.h"
|
|
#include "gtktestatcontextprivate.h"
|
|
#include "gtktypebuiltins.h"
|
|
|
|
#include "gtkbutton.h"
|
|
#include "gtktogglebutton.h"
|
|
#include "gtkmenubutton.h"
|
|
#include "gtkdropdown.h"
|
|
#include "gtkcolordialogbutton.h"
|
|
#include "gtkfontdialogbutton.h"
|
|
#include "gtkscalebutton.h"
|
|
#include "print/gtkprinteroptionwidgetprivate.h"
|
|
|
|
#if defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND)
|
|
#include "a11y/gtkatspicontextprivate.h"
|
|
#endif
|
|
|
|
G_DEFINE_ABSTRACT_TYPE (GtkATContext, gtk_at_context, G_TYPE_OBJECT)
|
|
|
|
enum
|
|
{
|
|
PROP_ACCESSIBLE_ROLE = 1,
|
|
PROP_ACCESSIBLE,
|
|
PROP_DISPLAY,
|
|
|
|
N_PROPS
|
|
};
|
|
|
|
enum
|
|
{
|
|
STATE_CHANGE,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static GParamSpec *obj_props[N_PROPS];
|
|
|
|
static guint obj_signals[LAST_SIGNAL];
|
|
|
|
static void
|
|
gtk_at_context_finalize (GObject *gobject)
|
|
{
|
|
GtkATContext *self = GTK_AT_CONTEXT (gobject);
|
|
|
|
gtk_accessible_attribute_set_unref (self->properties);
|
|
gtk_accessible_attribute_set_unref (self->relations);
|
|
gtk_accessible_attribute_set_unref (self->states);
|
|
|
|
G_OBJECT_CLASS (gtk_at_context_parent_class)->finalize (gobject);
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_dispose (GObject *gobject)
|
|
{
|
|
GtkATContext *self = GTK_AT_CONTEXT (gobject);
|
|
|
|
gtk_at_context_unrealize (self);
|
|
|
|
if (self->accessible_parent != NULL)
|
|
{
|
|
g_object_remove_weak_pointer (G_OBJECT (self->accessible_parent),
|
|
(gpointer *) &self->accessible_parent);
|
|
self->accessible_parent = NULL;
|
|
}
|
|
|
|
if (self->next_accessible_sibling != NULL)
|
|
{
|
|
g_object_remove_weak_pointer (G_OBJECT (self->next_accessible_sibling),
|
|
(gpointer *) &self->next_accessible_sibling);
|
|
self->next_accessible_sibling = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (gtk_at_context_parent_class)->dispose (gobject);
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkATContext *self = GTK_AT_CONTEXT (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_ACCESSIBLE_ROLE:
|
|
gtk_at_context_set_accessible_role (self, g_value_get_enum (value));
|
|
break;
|
|
|
|
case PROP_ACCESSIBLE:
|
|
self->accessible = g_value_get_object (value);
|
|
break;
|
|
|
|
case PROP_DISPLAY:
|
|
gtk_at_context_set_display (self, g_value_get_object (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkATContext *self = GTK_AT_CONTEXT (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_ACCESSIBLE_ROLE:
|
|
g_value_set_enum (value, self->accessible_role);
|
|
break;
|
|
|
|
case PROP_ACCESSIBLE:
|
|
g_value_set_object (value, self->accessible);
|
|
break;
|
|
|
|
case PROP_DISPLAY:
|
|
g_value_set_object (value, self->display);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_real_state_change (GtkATContext *self,
|
|
GtkAccessibleStateChange changed_states,
|
|
GtkAccessiblePropertyChange changed_properties,
|
|
GtkAccessibleRelationChange changed_relations,
|
|
GtkAccessibleAttributeSet *states,
|
|
GtkAccessibleAttributeSet *properties,
|
|
GtkAccessibleAttributeSet *relations)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_real_platform_change (GtkATContext *self,
|
|
GtkAccessiblePlatformChange change)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_real_bounds_change (GtkATContext *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_real_child_change (GtkATContext *self,
|
|
GtkAccessibleChildChange change,
|
|
GtkAccessible *child)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_real_realize (GtkATContext *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_real_unrealize (GtkATContext *self)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_class_init (GtkATContextClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->set_property = gtk_at_context_set_property;
|
|
gobject_class->get_property = gtk_at_context_get_property;
|
|
gobject_class->dispose = gtk_at_context_dispose;
|
|
gobject_class->finalize = gtk_at_context_finalize;
|
|
|
|
klass->realize = gtk_at_context_real_realize;
|
|
klass->unrealize = gtk_at_context_real_unrealize;
|
|
klass->state_change = gtk_at_context_real_state_change;
|
|
klass->platform_change = gtk_at_context_real_platform_change;
|
|
klass->bounds_change = gtk_at_context_real_bounds_change;
|
|
klass->child_change = gtk_at_context_real_child_change;
|
|
|
|
/**
|
|
* GtkATContext:accessible-role: (attributes org.gtk.Property.get=gtk_at_context_get_accessible_role)
|
|
*
|
|
* The accessible role used by the AT context.
|
|
*
|
|
* Depending on the given role, different states and properties can be
|
|
* set or retrieved.
|
|
*/
|
|
obj_props[PROP_ACCESSIBLE_ROLE] =
|
|
g_param_spec_enum ("accessible-role", NULL, NULL,
|
|
GTK_TYPE_ACCESSIBLE_ROLE,
|
|
GTK_ACCESSIBLE_ROLE_NONE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* GtkATContext:accessible: (attributes org.gtk.Property.get=gtk_at_context_get_accessible)
|
|
*
|
|
* The `GtkAccessible` that created the `GtkATContext` instance.
|
|
*/
|
|
obj_props[PROP_ACCESSIBLE] =
|
|
g_param_spec_object ("accessible", NULL, NULL,
|
|
GTK_TYPE_ACCESSIBLE,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
/**
|
|
* GtkATContext:display:
|
|
*
|
|
* The `GdkDisplay` for the `GtkATContext`.
|
|
*/
|
|
obj_props[PROP_DISPLAY] =
|
|
g_param_spec_object ("display", NULL, NULL,
|
|
GDK_TYPE_DISPLAY,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS |
|
|
G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
/**
|
|
* GtkATContext::state-change:
|
|
* @self: the `GtkATContext`
|
|
*
|
|
* Emitted when the attributes of the accessible for the
|
|
* `GtkATContext` instance change.
|
|
*/
|
|
obj_signals[STATE_CHANGE] =
|
|
g_signal_new ("state-change",
|
|
G_TYPE_FROM_CLASS (gobject_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
0,
|
|
NULL, NULL,
|
|
NULL,
|
|
G_TYPE_NONE, 0);
|
|
|
|
g_object_class_install_properties (gobject_class, N_PROPS, obj_props);
|
|
}
|
|
|
|
#define N_PROPERTIES (GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT + 1)
|
|
#define N_RELATIONS (GTK_ACCESSIBLE_RELATION_SET_SIZE + 1)
|
|
#define N_STATES (GTK_ACCESSIBLE_STATE_SELECTED + 1)
|
|
|
|
static const char *property_attrs[] = {
|
|
[GTK_ACCESSIBLE_PROPERTY_AUTOCOMPLETE] = "autocomplete",
|
|
[GTK_ACCESSIBLE_PROPERTY_DESCRIPTION] = "description",
|
|
[GTK_ACCESSIBLE_PROPERTY_HAS_POPUP] = "haspopup",
|
|
[GTK_ACCESSIBLE_PROPERTY_KEY_SHORTCUTS] = "keyshortcuts",
|
|
[GTK_ACCESSIBLE_PROPERTY_LABEL] = "label",
|
|
[GTK_ACCESSIBLE_PROPERTY_LEVEL] = "level",
|
|
[GTK_ACCESSIBLE_PROPERTY_MODAL] = "modal",
|
|
[GTK_ACCESSIBLE_PROPERTY_MULTI_LINE] = "multiline",
|
|
[GTK_ACCESSIBLE_PROPERTY_MULTI_SELECTABLE] = "multiselectable",
|
|
[GTK_ACCESSIBLE_PROPERTY_ORIENTATION] = "orientation",
|
|
[GTK_ACCESSIBLE_PROPERTY_PLACEHOLDER] = "placeholder",
|
|
[GTK_ACCESSIBLE_PROPERTY_READ_ONLY] = "readonly",
|
|
[GTK_ACCESSIBLE_PROPERTY_REQUIRED] = "required",
|
|
[GTK_ACCESSIBLE_PROPERTY_ROLE_DESCRIPTION] = "roledescription",
|
|
[GTK_ACCESSIBLE_PROPERTY_SORT] = "sort",
|
|
[GTK_ACCESSIBLE_PROPERTY_VALUE_MAX] = "valuemax",
|
|
[GTK_ACCESSIBLE_PROPERTY_VALUE_MIN] = "valuemin",
|
|
[GTK_ACCESSIBLE_PROPERTY_VALUE_NOW] = "valuenow",
|
|
[GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT] = "valuetext",
|
|
};
|
|
|
|
/*< private >
|
|
* gtk_accessible_property_get_attribute_name:
|
|
* @property: a `GtkAccessibleProperty`
|
|
*
|
|
* Retrieves the name of a `GtkAccessibleProperty`.
|
|
*
|
|
* Returns: (transfer none): the name of the accessible property
|
|
*/
|
|
const char *
|
|
gtk_accessible_property_get_attribute_name (GtkAccessibleProperty property)
|
|
{
|
|
g_return_val_if_fail (property >= GTK_ACCESSIBLE_PROPERTY_AUTOCOMPLETE &&
|
|
property <= GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT,
|
|
"<none>");
|
|
|
|
return property_attrs[property];
|
|
}
|
|
|
|
static const char *relation_attrs[] = {
|
|
[GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT] = "activedescendant",
|
|
[GTK_ACCESSIBLE_RELATION_COL_COUNT] = "colcount",
|
|
[GTK_ACCESSIBLE_RELATION_COL_INDEX] = "colindex",
|
|
[GTK_ACCESSIBLE_RELATION_COL_INDEX_TEXT] = "colindextext",
|
|
[GTK_ACCESSIBLE_RELATION_COL_SPAN] = "colspan",
|
|
[GTK_ACCESSIBLE_RELATION_CONTROLS] = "controls",
|
|
[GTK_ACCESSIBLE_RELATION_DESCRIBED_BY] = "describedby",
|
|
[GTK_ACCESSIBLE_RELATION_DETAILS] = "details",
|
|
[GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE] = "errormessage",
|
|
[GTK_ACCESSIBLE_RELATION_FLOW_TO] = "flowto",
|
|
[GTK_ACCESSIBLE_RELATION_LABELLED_BY] = "labelledby",
|
|
[GTK_ACCESSIBLE_RELATION_OWNS] = "owns",
|
|
[GTK_ACCESSIBLE_RELATION_POS_IN_SET] = "posinset",
|
|
[GTK_ACCESSIBLE_RELATION_ROW_COUNT] = "rowcount",
|
|
[GTK_ACCESSIBLE_RELATION_ROW_INDEX] = "rowindex",
|
|
[GTK_ACCESSIBLE_RELATION_ROW_INDEX_TEXT] = "rowindextext",
|
|
[GTK_ACCESSIBLE_RELATION_ROW_SPAN] = "rowspan",
|
|
[GTK_ACCESSIBLE_RELATION_SET_SIZE] = "setsize",
|
|
};
|
|
|
|
/*< private >
|
|
* gtk_accessible_relation_get_attribute_name:
|
|
* @relation: a `GtkAccessibleRelation`
|
|
*
|
|
* Retrieves the name of a `GtkAccessibleRelation`.
|
|
*
|
|
* Returns: (transfer none): the name of the accessible relation
|
|
*/
|
|
const char *
|
|
gtk_accessible_relation_get_attribute_name (GtkAccessibleRelation relation)
|
|
{
|
|
g_return_val_if_fail (relation >= GTK_ACCESSIBLE_RELATION_ACTIVE_DESCENDANT &&
|
|
relation <= GTK_ACCESSIBLE_RELATION_SET_SIZE,
|
|
"<none>");
|
|
|
|
return relation_attrs[relation];
|
|
}
|
|
|
|
static const char *state_attrs[] = {
|
|
[GTK_ACCESSIBLE_STATE_BUSY] = "busy",
|
|
[GTK_ACCESSIBLE_STATE_CHECKED] = "checked",
|
|
[GTK_ACCESSIBLE_STATE_DISABLED] = "disabled",
|
|
[GTK_ACCESSIBLE_STATE_EXPANDED] = "expanded",
|
|
[GTK_ACCESSIBLE_STATE_HIDDEN] = "hidden",
|
|
[GTK_ACCESSIBLE_STATE_INVALID] = "invalid",
|
|
[GTK_ACCESSIBLE_STATE_PRESSED] = "pressed",
|
|
[GTK_ACCESSIBLE_STATE_SELECTED] = "selected",
|
|
[GTK_ACCESSIBLE_STATE_VISITED] = "visited",
|
|
};
|
|
|
|
/*< private >
|
|
* gtk_accessible_state_get_attribute_name:
|
|
* @state: a `GtkAccessibleState`
|
|
*
|
|
* Retrieves the name of a `GtkAccessibleState`.
|
|
*
|
|
* Returns: (transfer none): the name of the accessible state
|
|
*/
|
|
const char *
|
|
gtk_accessible_state_get_attribute_name (GtkAccessibleState state)
|
|
{
|
|
g_return_val_if_fail (state >= GTK_ACCESSIBLE_STATE_BUSY &&
|
|
state <= GTK_ACCESSIBLE_STATE_SELECTED,
|
|
"<none>");
|
|
|
|
return state_attrs[state];
|
|
}
|
|
|
|
static void
|
|
gtk_at_context_init (GtkATContext *self)
|
|
{
|
|
self->accessible_role = GTK_ACCESSIBLE_ROLE_NONE;
|
|
|
|
self->properties =
|
|
gtk_accessible_attribute_set_new (G_N_ELEMENTS (property_attrs),
|
|
(GtkAccessibleAttributeNameFunc) gtk_accessible_property_get_attribute_name,
|
|
(GtkAccessibleAttributeDefaultFunc) gtk_accessible_value_get_default_for_property);
|
|
self->relations =
|
|
gtk_accessible_attribute_set_new (G_N_ELEMENTS (relation_attrs),
|
|
(GtkAccessibleAttributeNameFunc) gtk_accessible_relation_get_attribute_name,
|
|
(GtkAccessibleAttributeDefaultFunc) gtk_accessible_value_get_default_for_relation);
|
|
self->states =
|
|
gtk_accessible_attribute_set_new (G_N_ELEMENTS (state_attrs),
|
|
(GtkAccessibleAttributeNameFunc) gtk_accessible_state_get_attribute_name,
|
|
(GtkAccessibleAttributeDefaultFunc) gtk_accessible_value_get_default_for_state);
|
|
}
|
|
|
|
/**
|
|
* gtk_at_context_get_accessible: (attributes org.gtk.Method.get_property=accessible)
|
|
* @self: a `GtkATContext`
|
|
*
|
|
* Retrieves the `GtkAccessible` using this context.
|
|
*
|
|
* Returns: (transfer none): a `GtkAccessible`
|
|
*/
|
|
GtkAccessible *
|
|
gtk_at_context_get_accessible (GtkATContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
|
|
|
return self->accessible;
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_set_accessible_role:
|
|
* @self: a `GtkATContext`
|
|
* @role: the accessible role for the context
|
|
*
|
|
* Sets the accessible role for the given `GtkATContext`.
|
|
*
|
|
* This function can only be called if the `GtkATContext` is unrealized.
|
|
*/
|
|
void
|
|
gtk_at_context_set_accessible_role (GtkATContext *self,
|
|
GtkAccessibleRole role)
|
|
{
|
|
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
|
|
g_return_if_fail (!self->realized);
|
|
|
|
if (self->accessible_role == role)
|
|
return;
|
|
|
|
self->accessible_role = role;
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACCESSIBLE_ROLE]);
|
|
}
|
|
|
|
/**
|
|
* gtk_at_context_get_accessible_role: (attributes org.gtk.Method.get_property=accessible-role)
|
|
* @self: a `GtkATContext`
|
|
*
|
|
* Retrieves the accessible role of this context.
|
|
*
|
|
* Returns: a `GtkAccessibleRole`
|
|
*/
|
|
GtkAccessibleRole
|
|
gtk_at_context_get_accessible_role (GtkATContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), GTK_ACCESSIBLE_ROLE_NONE);
|
|
|
|
return self->accessible_role;
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_get_accessible_parent:
|
|
* @self: a `GtkAtContext`
|
|
*
|
|
* Retrieves the parent accessible object of the given `GtkAtContext`.
|
|
*
|
|
* Returns: (nullable) (transfer none): the parent accessible object, or `NULL` if not set.
|
|
*/
|
|
GtkAccessible *
|
|
gtk_at_context_get_accessible_parent (GtkATContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
|
|
|
return self->accessible_parent;
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_set_accessible_parent:
|
|
* @self: a `GtkAtContext`
|
|
* @parent: (nullable): the parent `GtkAccessible` to set
|
|
*
|
|
* Sets the parent accessible object of the given `GtkAtContext`.
|
|
*/
|
|
void
|
|
gtk_at_context_set_accessible_parent (GtkATContext *self,
|
|
GtkAccessible *parent)
|
|
{
|
|
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
|
|
|
|
if (self->accessible_parent != parent)
|
|
{
|
|
if (self->accessible_parent != NULL)
|
|
g_object_remove_weak_pointer (G_OBJECT (self->accessible_parent),
|
|
(gpointer *) &self->accessible_parent);
|
|
|
|
self->accessible_parent = parent;
|
|
if (self->accessible_parent != NULL)
|
|
g_object_add_weak_pointer (G_OBJECT (self->accessible_parent),
|
|
(gpointer *) &self->accessible_parent);
|
|
}
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_get_next_accessible_sibling:
|
|
* @self: a `GtkAtContext`
|
|
*
|
|
* Retrieves the next accessible sibling of the given `GtkAtContext`.
|
|
*
|
|
* Returns: (nullable) (transfer none): the next accessible sibling.
|
|
*/
|
|
GtkAccessible *
|
|
gtk_at_context_get_next_accessible_sibling (GtkATContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
|
|
|
return self->next_accessible_sibling;
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_set_next_accessible_sibling:
|
|
* @self: a `GtkAtContext`
|
|
* @sibling: (nullable): the next accessible sibling
|
|
*
|
|
* Sets the next accessible sibling object of the given `GtkAtContext`.
|
|
*/
|
|
void
|
|
gtk_at_context_set_next_accessible_sibling (GtkATContext *self,
|
|
GtkAccessible *sibling)
|
|
{
|
|
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
|
|
|
|
if (self->next_accessible_sibling != sibling)
|
|
{
|
|
if (self->next_accessible_sibling != NULL)
|
|
g_object_remove_weak_pointer (G_OBJECT (self->next_accessible_sibling),
|
|
(gpointer *) &self->next_accessible_sibling);
|
|
|
|
self->next_accessible_sibling = sibling;
|
|
|
|
if (self->next_accessible_sibling != NULL)
|
|
g_object_add_weak_pointer (G_OBJECT (self->next_accessible_sibling),
|
|
(gpointer *) &self->next_accessible_sibling);
|
|
}
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_set_display:
|
|
* @self: a `GtkATContext`
|
|
* @display: a `GdkDisplay`
|
|
*
|
|
* Sets the `GdkDisplay` used by the `GtkATContext`.
|
|
*
|
|
* This function can only be called if the `GtkATContext` is
|
|
* not realized.
|
|
*/
|
|
void
|
|
gtk_at_context_set_display (GtkATContext *self,
|
|
GdkDisplay *display)
|
|
{
|
|
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
|
|
g_return_if_fail (display == NULL || GDK_IS_DISPLAY (display));
|
|
|
|
if (self->display == display)
|
|
return;
|
|
|
|
if (self->realized)
|
|
return;
|
|
|
|
self->display = display;
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DISPLAY]);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_get_display:
|
|
* @self: a `GtkATContext`
|
|
*
|
|
* Retrieves the `GdkDisplay` used to create the context.
|
|
*
|
|
* Returns: (transfer none): a `GdkDisplay`
|
|
*/
|
|
GdkDisplay *
|
|
gtk_at_context_get_display (GtkATContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
|
|
|
return self->display;
|
|
}
|
|
|
|
static const struct {
|
|
const char *name;
|
|
const char *env_name;
|
|
GtkATContext * (* create_context) (GtkAccessibleRole accessible_role,
|
|
GtkAccessible *accessible,
|
|
GdkDisplay *display);
|
|
} a11y_backends[] = {
|
|
#if defined(GDK_WINDOWING_WAYLAND)
|
|
{ "AT-SPI (Wayland)", "atspi", gtk_at_spi_create_context },
|
|
#endif
|
|
#if defined(GDK_WINDOWING_X11)
|
|
{ "AT-SPI (X11)", "atspi", gtk_at_spi_create_context },
|
|
#endif
|
|
{ "Test", "test", gtk_test_at_context_new },
|
|
{ NULL, NULL, NULL },
|
|
};
|
|
|
|
/**
|
|
* gtk_at_context_create: (constructor)
|
|
* @accessible_role: the accessible role used by the `GtkATContext`
|
|
* @accessible: the `GtkAccessible` implementation using the `GtkATContext`
|
|
* @display: the `GdkDisplay` used by the `GtkATContext`
|
|
*
|
|
* Creates a new `GtkATContext` instance for the given accessible role,
|
|
* accessible instance, and display connection.
|
|
*
|
|
* The `GtkATContext` implementation being instantiated will depend on the
|
|
* platform.
|
|
*
|
|
* Returns: (nullable) (transfer full): the `GtkATContext`
|
|
*/
|
|
GtkATContext *
|
|
gtk_at_context_create (GtkAccessibleRole accessible_role,
|
|
GtkAccessible *accessible,
|
|
GdkDisplay *display)
|
|
{
|
|
static const char *gtk_a11y_env;
|
|
|
|
if (gtk_a11y_env == NULL)
|
|
{
|
|
gtk_a11y_env = g_getenv ("GTK_A11Y");
|
|
if (gtk_a11y_env == NULL)
|
|
gtk_a11y_env = "0";
|
|
|
|
if (g_ascii_strcasecmp (gtk_a11y_env, "help") == 0)
|
|
{
|
|
g_print ("Supported arguments for GTK_A11Y environment variable:\n");
|
|
|
|
#if defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND)
|
|
g_print (" atspi - Use the AT-SPI accessibility backend\n");
|
|
#endif
|
|
g_print (" test - Use the test accessibility backend\n");
|
|
g_print (" none - Disable the accessibility backend\n");
|
|
g_print (" help - Print this help\n\n");
|
|
g_print ("Other arguments will cause a warning and be ignored.\n");
|
|
|
|
gtk_a11y_env = "0";
|
|
}
|
|
}
|
|
|
|
/* Short-circuit disabling the accessibility support */
|
|
if (g_ascii_strcasecmp (gtk_a11y_env, "none") == 0)
|
|
return NULL;
|
|
|
|
GtkATContext *res = NULL;
|
|
|
|
for (guint i = 0; i < G_N_ELEMENTS (a11y_backends); i++)
|
|
{
|
|
if (a11y_backends[i].name == NULL)
|
|
break;
|
|
|
|
if (a11y_backends[i].create_context != NULL &&
|
|
(*gtk_a11y_env == '0' || g_ascii_strcasecmp (a11y_backends[i].env_name, gtk_a11y_env) == 0))
|
|
{
|
|
res = a11y_backends[i].create_context (accessible_role, accessible, display);
|
|
if (res != NULL)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (*gtk_a11y_env != '0' && res == NULL)
|
|
g_warning ("Unrecognized accessibility backend \"%s\". Try GTK_A11Y=help", gtk_a11y_env);
|
|
|
|
/* Fall back to the test context, so we can get debugging data */
|
|
if (res == NULL)
|
|
res = g_object_new (GTK_TYPE_TEST_AT_CONTEXT,
|
|
"accessible_role", accessible_role,
|
|
"accessible", accessible,
|
|
"display", display,
|
|
NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_clone: (constructor)
|
|
* @self: the `GtkATContext` to clone
|
|
* @role: the accessible role of the clone, or %GTK_ACCESSIBLE_ROLE_NONE to
|
|
* use the same accessible role of @self
|
|
* @accessible: (nullable): the accessible creating the context, or %NULL to
|
|
* use the same `GtkAccessible` of @self
|
|
* @display: (nullable): the display connection, or %NULL to use the same
|
|
* `GdkDisplay` of @self
|
|
*
|
|
* Clones the state of the given `GtkATContext`, using @role, @accessible,
|
|
* and @display.
|
|
*
|
|
* If @self is realized, the returned `GtkATContext` will also be realized.
|
|
*
|
|
* Returns: (transfer full): the newly created `GtkATContext`
|
|
*/
|
|
GtkATContext *
|
|
gtk_at_context_clone (GtkATContext *self,
|
|
GtkAccessibleRole role,
|
|
GtkAccessible *accessible,
|
|
GdkDisplay *display)
|
|
{
|
|
g_return_val_if_fail (self == NULL || GTK_IS_AT_CONTEXT (self), NULL);
|
|
g_return_val_if_fail (accessible == NULL || GTK_IS_ACCESSIBLE (accessible), NULL);
|
|
g_return_val_if_fail (display == NULL || GDK_IS_DISPLAY (display), NULL);
|
|
|
|
if (self != NULL && role == GTK_ACCESSIBLE_ROLE_NONE)
|
|
role = self->accessible_role;
|
|
|
|
if (self != NULL && accessible == NULL)
|
|
accessible = self->accessible;
|
|
|
|
if (self != NULL && display == NULL)
|
|
display = self->display;
|
|
|
|
GtkATContext *res = gtk_at_context_create (role, accessible, display);
|
|
|
|
if (self != NULL)
|
|
{
|
|
g_clear_pointer (&res->states, gtk_accessible_attribute_set_unref);
|
|
g_clear_pointer (&res->properties, gtk_accessible_attribute_set_unref);
|
|
g_clear_pointer (&res->relations, gtk_accessible_attribute_set_unref);
|
|
|
|
res->states = gtk_accessible_attribute_set_ref (self->states);
|
|
res->properties = gtk_accessible_attribute_set_ref (self->properties);
|
|
res->relations = gtk_accessible_attribute_set_ref (self->relations);
|
|
|
|
if (self->realized)
|
|
gtk_at_context_realize (res);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
gboolean
|
|
gtk_at_context_is_realized (GtkATContext *self)
|
|
{
|
|
return self->realized;
|
|
}
|
|
|
|
void
|
|
gtk_at_context_realize (GtkATContext *self)
|
|
{
|
|
if (self->realized)
|
|
return;
|
|
|
|
GTK_DEBUG (A11Y, "Realizing AT context '%s'", G_OBJECT_TYPE_NAME (self));
|
|
GTK_AT_CONTEXT_GET_CLASS (self)->realize (self);
|
|
|
|
self->realized = TRUE;
|
|
}
|
|
|
|
void
|
|
gtk_at_context_unrealize (GtkATContext *self)
|
|
{
|
|
if (!self->realized)
|
|
return;
|
|
|
|
GTK_DEBUG (A11Y, "Unrealizing AT context '%s'", G_OBJECT_TYPE_NAME (self));
|
|
GTK_AT_CONTEXT_GET_CLASS (self)->unrealize (self);
|
|
|
|
self->realized = FALSE;
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_update:
|
|
* @self: a `GtkATContext`
|
|
*
|
|
* Notifies the AT connected to this `GtkATContext` that the accessible
|
|
* state and its properties have changed.
|
|
*/
|
|
void
|
|
gtk_at_context_update (GtkATContext *self)
|
|
{
|
|
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
|
|
|
|
if (!self->realized)
|
|
return;
|
|
|
|
/* There's no point in notifying of state changes if there weren't any */
|
|
if (self->updated_properties == 0 &&
|
|
self->updated_relations == 0 &&
|
|
self->updated_states == 0)
|
|
return;
|
|
|
|
GTK_AT_CONTEXT_GET_CLASS (self)->state_change (self,
|
|
self->updated_states, self->updated_properties, self->updated_relations,
|
|
self->states, self->properties, self->relations);
|
|
g_signal_emit (self, obj_signals[STATE_CHANGE], 0);
|
|
|
|
self->updated_properties = 0;
|
|
self->updated_relations = 0;
|
|
self->updated_states = 0;
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_set_accessible_state:
|
|
* @self: a `GtkATContext`
|
|
* @state: a `GtkAccessibleState`
|
|
* @value: (nullable): `GtkAccessibleValue`
|
|
*
|
|
* Sets the @value for the given @state of a `GtkATContext`.
|
|
*
|
|
* If @value is %NULL, the state is unset.
|
|
*
|
|
* This function will accumulate state changes until gtk_at_context_update()
|
|
* is called.
|
|
*/
|
|
void
|
|
gtk_at_context_set_accessible_state (GtkATContext *self,
|
|
GtkAccessibleState state,
|
|
GtkAccessibleValue *value)
|
|
{
|
|
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
|
|
|
|
gboolean res = FALSE;
|
|
|
|
if (value != NULL)
|
|
res = gtk_accessible_attribute_set_add (self->states, state, value);
|
|
else
|
|
res = gtk_accessible_attribute_set_remove (self->states, state);
|
|
|
|
if (res)
|
|
self->updated_states |= (1 << state);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_has_accessible_state:
|
|
* @self: a `GtkATContext`
|
|
* @state: a `GtkAccessibleState`
|
|
*
|
|
* Checks whether a `GtkATContext` has the given @state set.
|
|
*
|
|
* Returns: %TRUE, if the accessible state is set
|
|
*/
|
|
gboolean
|
|
gtk_at_context_has_accessible_state (GtkATContext *self,
|
|
GtkAccessibleState state)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), FALSE);
|
|
|
|
return gtk_accessible_attribute_set_contains (self->states, state);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_get_accessible_state:
|
|
* @self: a `GtkATContext`
|
|
* @state: a `GtkAccessibleState`
|
|
*
|
|
* Retrieves the value for the accessible state of a `GtkATContext`.
|
|
*
|
|
* Returns: (transfer none): the value for the given state
|
|
*/
|
|
GtkAccessibleValue *
|
|
gtk_at_context_get_accessible_state (GtkATContext *self,
|
|
GtkAccessibleState state)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
|
|
|
return gtk_accessible_attribute_set_get_value (self->states, state);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_set_accessible_property:
|
|
* @self: a `GtkATContext`
|
|
* @property: a `GtkAccessibleProperty`
|
|
* @value: (nullable): `GtkAccessibleValue`
|
|
*
|
|
* Sets the @value for the given @property of a `GtkATContext`.
|
|
*
|
|
* If @value is %NULL, the property is unset.
|
|
*
|
|
* This function will accumulate property changes until gtk_at_context_update()
|
|
* is called.
|
|
*/
|
|
void
|
|
gtk_at_context_set_accessible_property (GtkATContext *self,
|
|
GtkAccessibleProperty property,
|
|
GtkAccessibleValue *value)
|
|
{
|
|
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
|
|
|
|
gboolean res = FALSE;
|
|
|
|
if (value != NULL)
|
|
res = gtk_accessible_attribute_set_add (self->properties, property, value);
|
|
else
|
|
res = gtk_accessible_attribute_set_remove (self->properties, property);
|
|
|
|
if (res)
|
|
self->updated_properties |= (1 << property);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_has_accessible_property:
|
|
* @self: a `GtkATContext`
|
|
* @property: a `GtkAccessibleProperty`
|
|
*
|
|
* Checks whether a `GtkATContext` has the given @property set.
|
|
*
|
|
* Returns: %TRUE, if the accessible property is set
|
|
*/
|
|
gboolean
|
|
gtk_at_context_has_accessible_property (GtkATContext *self,
|
|
GtkAccessibleProperty property)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), FALSE);
|
|
|
|
return gtk_accessible_attribute_set_contains (self->properties, property);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_get_accessible_property:
|
|
* @self: a `GtkATContext`
|
|
* @property: a `GtkAccessibleProperty`
|
|
*
|
|
* Retrieves the value for the accessible property of a `GtkATContext`.
|
|
*
|
|
* Returns: (transfer none): the value for the given property
|
|
*/
|
|
GtkAccessibleValue *
|
|
gtk_at_context_get_accessible_property (GtkATContext *self,
|
|
GtkAccessibleProperty property)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
|
|
|
return gtk_accessible_attribute_set_get_value (self->properties, property);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_set_accessible_relation:
|
|
* @self: a `GtkATContext`
|
|
* @relation: a `GtkAccessibleRelation`
|
|
* @value: (nullable): `GtkAccessibleValue`
|
|
*
|
|
* Sets the @value for the given @relation of a `GtkATContext`.
|
|
*
|
|
* If @value is %NULL, the relation is unset.
|
|
*
|
|
* This function will accumulate relation changes until gtk_at_context_update()
|
|
* is called.
|
|
*/
|
|
void
|
|
gtk_at_context_set_accessible_relation (GtkATContext *self,
|
|
GtkAccessibleRelation relation,
|
|
GtkAccessibleValue *value)
|
|
{
|
|
g_return_if_fail (GTK_IS_AT_CONTEXT (self));
|
|
|
|
gboolean res = FALSE;
|
|
|
|
if (value != NULL)
|
|
res = gtk_accessible_attribute_set_add (self->relations, relation, value);
|
|
else
|
|
res = gtk_accessible_attribute_set_remove (self->relations, relation);
|
|
|
|
if (res)
|
|
self->updated_relations |= (1 << relation);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_has_accessible_relation:
|
|
* @self: a `GtkATContext`
|
|
* @relation: a `GtkAccessibleRelation`
|
|
*
|
|
* Checks whether a `GtkATContext` has the given @relation set.
|
|
*
|
|
* Returns: %TRUE, if the accessible relation is set
|
|
*/
|
|
gboolean
|
|
gtk_at_context_has_accessible_relation (GtkATContext *self,
|
|
GtkAccessibleRelation relation)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), FALSE);
|
|
|
|
return gtk_accessible_attribute_set_contains (self->relations, relation);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_get_accessible_relation:
|
|
* @self: a `GtkATContext`
|
|
* @relation: a `GtkAccessibleRelation`
|
|
*
|
|
* Retrieves the value for the accessible relation of a `GtkATContext`.
|
|
*
|
|
* Returns: (transfer none): the value for the given relation
|
|
*/
|
|
GtkAccessibleValue *
|
|
gtk_at_context_get_accessible_relation (GtkATContext *self,
|
|
GtkAccessibleRelation relation)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
|
|
|
return gtk_accessible_attribute_set_get_value (self->relations, relation);
|
|
}
|
|
|
|
/* See ARIA 5.2.8.4, 5.2.8.5 and 5.2.8.6 for the prohibited, from author
|
|
* and from content parts, and the table in
|
|
* https://www.w3.org/WAI/ARIA/apg/practices/names-and-descriptions/
|
|
* for the recommended / not recommended parts. We've made a few changes
|
|
* to the recommendations:
|
|
* - We don't recommend against labelling listitems, sincd GtkListView
|
|
* will put the focus on listitems sometimes.
|
|
* - We don't recommend tab lists being labelled, since GtkNotebook does
|
|
* not have a practical way of doing that.
|
|
*/
|
|
|
|
#define NAME_FROM_AUTHOR (1 << 6)
|
|
#define NAME_FROM_CONTENT (1 << 7)
|
|
|
|
static guint8 naming[] = {
|
|
[GTK_ACCESSIBLE_ROLE_ALERT] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_ALERT_DIALOG] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_BANNER] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_BLOCK_QUOTE] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_BUTTON] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_CAPTION] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_CELL] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT,
|
|
[GTK_ACCESSIBLE_ROLE_CHECKBOX] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_COLUMN_HEADER] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_COMBO_BOX] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_COMMAND] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_COMPOSITE] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_DIALOG] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_DOCUMENT] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_FEED] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_RECOMMENDED,
|
|
[GTK_ACCESSIBLE_ROLE_FORM] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_GENERIC] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_GRID] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_GRID_CELL] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT,
|
|
[GTK_ACCESSIBLE_ROLE_GROUP] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_HEADING] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_IMG] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_INPUT] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_LABEL] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT,
|
|
[GTK_ACCESSIBLE_ROLE_LANDMARK] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_LEGEND] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_LINK] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_LIST] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_LIST_BOX] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_LIST_ITEM] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_LOG] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_MAIN] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_MARQUEE] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_MATH] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_RECOMMENDED,
|
|
[GTK_ACCESSIBLE_ROLE_METER] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_MENU] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_RECOMMENDED,
|
|
[GTK_ACCESSIBLE_ROLE_MENU_BAR] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_RECOMMENDED,
|
|
[GTK_ACCESSIBLE_ROLE_MENU_ITEM] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_MENU_ITEM_CHECKBOX] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_MENU_ITEM_RADIO] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_NAVIGATION] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_RECOMMENDED,
|
|
[GTK_ACCESSIBLE_ROLE_NONE] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_NOTE] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_OPTION] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_PRESENTATION] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_PROGRESS_BAR] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_RADIO] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_RADIO_GROUP] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_RANGE] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_REGION] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_ROW] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT,
|
|
[GTK_ACCESSIBLE_ROLE_ROW_GROUP] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_NOT_RECOMMENDED,
|
|
[GTK_ACCESSIBLE_ROLE_ROW_HEADER] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_SCROLLBAR] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_SEARCH] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_RECOMMENDED,
|
|
[GTK_ACCESSIBLE_ROLE_SEARCH_BOX] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_SECTION] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_SECTION_HEAD] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_SELECT] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_SEPARATOR] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_SLIDER] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_SPIN_BUTTON] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_STATUS] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_STRUCTURE] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_SWITCH] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_TAB] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_TABLE] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_TAB_LIST] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_TAB_PANEL] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_TEXT_BOX] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_TIME] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
[GTK_ACCESSIBLE_ROLE_TIMER] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_TOOLBAR] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_RECOMMENDED,
|
|
[GTK_ACCESSIBLE_ROLE_TOOLTIP] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT,
|
|
[GTK_ACCESSIBLE_ROLE_TREE] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_TREE_GRID] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_TREE_ITEM] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_WIDGET] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT,
|
|
[GTK_ACCESSIBLE_ROLE_WINDOW] = NAME_FROM_AUTHOR,
|
|
[GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_APPLICATION] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
|
|
[GTK_ACCESSIBLE_ROLE_PARAGRAPH] = GTK_ACCESSIBLE_NAME_PROHIBITED,
|
|
};
|
|
|
|
/* < private >
|
|
* gtk_accessible_role_supports_name_from_author:
|
|
* @role: a `GtkAccessibleRole`
|
|
*
|
|
* Returns whether this role supports setting the label and description
|
|
* properties or the labelled-by and described-by relations.
|
|
*
|
|
* Returns: %TRUE if the role allows labelling
|
|
*/
|
|
gboolean
|
|
gtk_accessible_role_supports_name_from_author (GtkAccessibleRole role)
|
|
{
|
|
return (naming[role] & NAME_FROM_AUTHOR) != 0;
|
|
}
|
|
|
|
/* < private >
|
|
* gtk_accessible_role_supports_name_from_content:
|
|
* @role: a `GtkAccessibleRole`
|
|
*
|
|
* Returns whether this role will use content of child widgets such
|
|
* as labels for its accessible name and description if no explicit
|
|
* labels are provided.
|
|
*
|
|
* Returns: %TRUE if the role content naming
|
|
*/
|
|
gboolean
|
|
gtk_accessible_role_supports_name_from_content (GtkAccessibleRole role)
|
|
{
|
|
return (naming[role] & NAME_FROM_CONTENT) != 0;
|
|
}
|
|
|
|
/* < private >
|
|
* gtk_accessible_role_get_nameing:
|
|
* @role: a `GtkAccessibleRole`
|
|
*
|
|
* Returns naming information for this role.
|
|
*
|
|
* Returns: information about naming requirements for the role
|
|
*/
|
|
GtkAccessibleNaming
|
|
gtk_accessible_role_get_naming (GtkAccessibleRole role)
|
|
{
|
|
return (GtkAccessibleNaming) (naming[role] & ~(NAME_FROM_AUTHOR|NAME_FROM_CONTENT));
|
|
}
|
|
|
|
static gboolean
|
|
is_nested_button (GtkATContext *self)
|
|
{
|
|
GtkAccessible *accessible;
|
|
GtkWidget *widget, *parent;
|
|
|
|
accessible = gtk_at_context_get_accessible (self);
|
|
|
|
if (!GTK_IS_WIDGET (accessible))
|
|
return FALSE;
|
|
|
|
widget = GTK_WIDGET (accessible);
|
|
parent = gtk_widget_get_parent (widget);
|
|
|
|
if ((GTK_IS_TOGGLE_BUTTON (widget) && GTK_IS_DROP_DOWN (parent)) ||
|
|
(GTK_IS_TOGGLE_BUTTON (widget) && GTK_IS_MENU_BUTTON (parent)) ||
|
|
(GTK_IS_BUTTON (widget) && GTK_IS_COLOR_DIALOG_BUTTON (parent)) ||
|
|
(GTK_IS_BUTTON (widget) && GTK_IS_FONT_DIALOG_BUTTON (parent)) ||
|
|
(GTK_IS_BUTTON (widget) && GTK_IS_SCALE_BUTTON (parent))
|
|
#ifdef G_OS_UNIX
|
|
|| (GTK_IS_PRINTER_OPTION_WIDGET (parent) &&
|
|
(GTK_IS_CHECK_BUTTON (widget) ||
|
|
GTK_IS_DROP_DOWN (widget) ||
|
|
GTK_IS_ENTRY (widget) ||
|
|
GTK_IS_IMAGE (widget) ||
|
|
GTK_IS_LABEL (widget) ||
|
|
GTK_IS_BUTTON (widget)))
|
|
#endif
|
|
)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static GtkATContext *
|
|
get_parent_context (GtkATContext *self)
|
|
{
|
|
GtkAccessible *accessible, *parent;
|
|
|
|
accessible = gtk_at_context_get_accessible (self);
|
|
parent = gtk_accessible_get_accessible_parent (accessible);
|
|
if (parent)
|
|
{
|
|
GtkATContext *context = gtk_accessible_get_at_context (parent);
|
|
g_object_unref (parent);
|
|
return context;
|
|
}
|
|
|
|
return g_object_ref (self);
|
|
}
|
|
|
|
static inline gboolean
|
|
not_just_space (const char *text)
|
|
{
|
|
for (const char *p = text; *p; p = g_utf8_next_char (p))
|
|
{
|
|
if (!g_unichar_isspace (g_utf8_get_char (p)))
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static inline void
|
|
append_with_space (GString *str,
|
|
const char *text)
|
|
{
|
|
if (str->len > 0)
|
|
g_string_append (str, " ");
|
|
g_string_append (str, text);
|
|
}
|
|
|
|
/* See the WAI-ARIA § 4.3, "Accessible Name and Description Computation",
|
|
* and https://www.w3.org/TR/accname-1.2/
|
|
*/
|
|
|
|
static void
|
|
gtk_at_context_get_text_accumulate (GtkATContext *self,
|
|
GPtrArray *nodes,
|
|
GString *res,
|
|
GtkAccessibleProperty property,
|
|
GtkAccessibleRelation relation,
|
|
gboolean is_ref,
|
|
gboolean is_child)
|
|
{
|
|
GtkAccessibleValue *value = NULL;
|
|
|
|
/* Step 2.A */
|
|
if (!is_ref)
|
|
{
|
|
if (gtk_accessible_attribute_set_contains (self->states, GTK_ACCESSIBLE_STATE_HIDDEN))
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (self->states, GTK_ACCESSIBLE_STATE_HIDDEN);
|
|
|
|
if (gtk_boolean_accessible_value_get (value))
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (gtk_accessible_role_supports_name_from_author (self->accessible_role))
|
|
{
|
|
/* Step 2.B */
|
|
if (!is_ref && gtk_accessible_attribute_set_contains (self->relations, relation))
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (self->relations, relation);
|
|
|
|
GList *list = gtk_reference_list_accessible_value_get (value);
|
|
|
|
for (GList *l = list; l != NULL; l = l->next)
|
|
{
|
|
GtkAccessible *rel = GTK_ACCESSIBLE (l->data);
|
|
if (!g_ptr_array_find (nodes, rel, NULL))
|
|
{
|
|
GtkATContext *rel_context = gtk_accessible_get_at_context (rel);
|
|
|
|
g_ptr_array_add (nodes, rel);
|
|
gtk_at_context_get_text_accumulate (rel_context, nodes, res, property, relation, TRUE, FALSE);
|
|
|
|
g_object_unref (rel_context);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* Step 2.C */
|
|
if (gtk_accessible_attribute_set_contains (self->properties, property))
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (self->properties, property);
|
|
|
|
char *str = (char *) gtk_string_accessible_value_get (value);
|
|
if (str[0] != '\0')
|
|
{
|
|
append_with_space (res, gtk_string_accessible_value_get (value));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Step 2.E */
|
|
if ((property == GTK_ACCESSIBLE_PROPERTY_LABEL && is_child) || (relation == GTK_ACCESSIBLE_RELATION_LABELLED_BY && is_ref))
|
|
{
|
|
if (self->accessible_role == GTK_ACCESSIBLE_ROLE_TEXT_BOX)
|
|
{
|
|
if (GTK_IS_EDITABLE (self->accessible))
|
|
{
|
|
const char *text = gtk_editable_get_text (GTK_EDITABLE (self->accessible));
|
|
if (text && not_just_space (text))
|
|
append_with_space (res, text);
|
|
}
|
|
return;
|
|
}
|
|
else if (gtk_accessible_role_is_range_subclass (self->accessible_role))
|
|
{
|
|
if (gtk_accessible_attribute_set_contains (self->properties, GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT))
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (self->properties, GTK_ACCESSIBLE_PROPERTY_VALUE_TEXT);
|
|
append_with_space (res, gtk_string_accessible_value_get (value));
|
|
}
|
|
else if (gtk_accessible_attribute_set_contains (self->properties, GTK_ACCESSIBLE_PROPERTY_VALUE_NOW))
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (self->properties, GTK_ACCESSIBLE_PROPERTY_VALUE_NOW);
|
|
if (res->len > 0)
|
|
g_string_append (res, " ");
|
|
g_string_append_printf (res, "%g", gtk_number_accessible_value_get (value));
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Step 2.F */
|
|
if (gtk_accessible_role_supports_name_from_content (self->accessible_role) || is_ref || is_child)
|
|
{
|
|
if (GTK_IS_WIDGET (self->accessible))
|
|
{
|
|
GString *s = g_string_new ("");
|
|
|
|
for (GtkWidget *child = gtk_widget_get_first_child (GTK_WIDGET (self->accessible));
|
|
child != NULL;
|
|
child = gtk_widget_get_next_sibling (child))
|
|
{
|
|
GtkAccessible *rel = GTK_ACCESSIBLE (child);
|
|
GtkATContext *rel_context = gtk_accessible_get_at_context (rel);
|
|
|
|
gtk_at_context_get_text_accumulate (rel_context, nodes, s, property, relation, FALSE, TRUE);
|
|
|
|
g_object_unref (rel_context);
|
|
}
|
|
|
|
if (s->len > 0)
|
|
{
|
|
append_with_space (res, s->str);
|
|
g_string_free (s, TRUE);
|
|
return;
|
|
}
|
|
|
|
g_string_free (s, TRUE);
|
|
}
|
|
}
|
|
|
|
/* Step 2.G */
|
|
if (GTK_IS_LABEL (self->accessible))
|
|
{
|
|
const char *text = gtk_label_get_text (GTK_LABEL (self->accessible));
|
|
if (text && not_just_space (text))
|
|
append_with_space (res, text);
|
|
return;
|
|
}
|
|
else if (GTK_IS_INSCRIPTION (self->accessible))
|
|
{
|
|
const char *text = gtk_inscription_get_text (GTK_INSCRIPTION (self->accessible));
|
|
if (text && not_just_space (text))
|
|
append_with_space (res, text);
|
|
return;
|
|
}
|
|
|
|
/* Step 2.I */
|
|
if (GTK_IS_WIDGET (self->accessible))
|
|
{
|
|
const char *text = gtk_widget_get_tooltip_text (GTK_WIDGET (self->accessible));
|
|
if (text && not_just_space (text))
|
|
append_with_space (res, text);
|
|
}
|
|
}
|
|
|
|
static char *
|
|
gtk_at_context_get_text (GtkATContext *self,
|
|
GtkAccessibleProperty property,
|
|
GtkAccessibleRelation relation)
|
|
{
|
|
GtkATContext *parent = NULL;
|
|
|
|
g_return_val_if_fail (GTK_IS_AT_CONTEXT (self), NULL);
|
|
|
|
/* Step 1 */
|
|
if (gtk_accessible_role_get_naming (self->accessible_role) == GTK_ACCESSIBLE_NAME_PROHIBITED)
|
|
return g_strdup ("");
|
|
|
|
/* We special case this here since it is a common pattern:
|
|
* We have a 'wrapper' object, like a GtkDropdown which
|
|
* contains a toggle button. The dropdown appears in the
|
|
* ui file and carries all the a11y attributes, but the
|
|
* focus ends up on the toggle button.
|
|
*/
|
|
if (is_nested_button (self))
|
|
{
|
|
parent = get_parent_context (self);
|
|
self = parent;
|
|
if (is_nested_button (self))
|
|
{
|
|
parent = get_parent_context (parent);
|
|
g_object_unref (self);
|
|
self = parent;
|
|
}
|
|
}
|
|
|
|
GPtrArray *nodes = g_ptr_array_new ();
|
|
GString *res = g_string_new ("");
|
|
|
|
/* Step 2 */
|
|
gtk_at_context_get_text_accumulate (self, nodes, res, property, relation, FALSE, FALSE);
|
|
|
|
g_ptr_array_unref (nodes);
|
|
|
|
g_clear_object (&parent);
|
|
|
|
return g_string_free (res, FALSE);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_get_name:
|
|
* @self: a `GtkATContext`
|
|
*
|
|
* Retrieves the accessible name of the `GtkATContext`.
|
|
*
|
|
* This is a convenience function meant to be used by `GtkATContext` implementations.
|
|
*
|
|
* Returns: (transfer full): the label of the `GtkATContext`
|
|
*/
|
|
char *
|
|
gtk_at_context_get_name (GtkATContext *self)
|
|
{
|
|
return gtk_at_context_get_text (self, GTK_ACCESSIBLE_PROPERTY_LABEL, GTK_ACCESSIBLE_RELATION_LABELLED_BY);
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_context_get_description:
|
|
* @self: a `GtkATContext`
|
|
*
|
|
* Retrieves the accessible description of the `GtkATContext`.
|
|
*
|
|
* This is a convenience function meant to be used by `GtkATContext` implementations.
|
|
*
|
|
* Returns: (transfer full): the label of the `GtkATContext`
|
|
*/
|
|
char *
|
|
gtk_at_context_get_description (GtkATContext *self)
|
|
{
|
|
return gtk_at_context_get_text (self, GTK_ACCESSIBLE_PROPERTY_DESCRIPTION, GTK_ACCESSIBLE_RELATION_DESCRIBED_BY);
|
|
}
|
|
|
|
void
|
|
gtk_at_context_platform_changed (GtkATContext *self,
|
|
GtkAccessiblePlatformChange change)
|
|
{
|
|
gtk_at_context_realize (self);
|
|
|
|
GTK_AT_CONTEXT_GET_CLASS (self)->platform_change (self, change);
|
|
}
|
|
|
|
void
|
|
gtk_at_context_bounds_changed (GtkATContext *self)
|
|
{
|
|
if (!self->realized)
|
|
return;
|
|
|
|
GTK_AT_CONTEXT_GET_CLASS (self)->bounds_change (self);
|
|
}
|
|
|
|
void
|
|
gtk_at_context_child_changed (GtkATContext *self,
|
|
GtkAccessibleChildChange change,
|
|
GtkAccessible *child)
|
|
{
|
|
if (!self->realized)
|
|
return;
|
|
|
|
GTK_AT_CONTEXT_GET_CLASS (self)->child_change (self, change, child);
|
|
}
|