Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  gtkatcontext.c

  Sprache: 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:
 *
 * Communicates with 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"

#ifdef HAVE_ACCESSKIT
#include "a11y/gtkaccesskitcontextprivate.h"
#endif
#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,
  PROP_REALIZED,

  N_PROPS
};

enum
{
  STATE_CHANGE,

  LAST_SIGNAL
};

static GParamSpec *obj_props[N_PROPS];

static guint obj_signals[LAST_SIGNAL];

static char *gtk_at_context_get_description_internal (GtkATContext*self, gboolean check_duplicates);
static char *gtk_at_context_get_name_internal (GtkATContext*self, gboolean check_duplicates);

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_clear_weak_pointer (&self->accessible_parent);
    }

  if (self->next_accessible_sibling != NULL)
    {
      g_clear_weak_pointer (&self->next_accessible_sibling);
    }

  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;

    case PROP_REALIZED:
      g_value_set_boolean (value, self->realized);
      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_real_update_caret_position (GtkATContext *self)
{
}

static void
gtk_at_context_real_update_selection_bound (GtkATContext *self)
{
}

static void
gtk_at_context_real_update_text_contents (GtkATContext *self,
                                          GtkAccessibleTextContentChange change,
                                          unsigned int start,
                                          unsigned int end)
{
}

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;
  klass->update_caret_position = gtk_at_context_real_update_caret_position;
  klass->update_selection_bound = gtk_at_context_real_update_selection_bound;
  klass->update_text_contents = gtk_at_context_real_update_text_contents;

  /**
   * GtkATContext: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_NAME);

  /**
   * GtkATContext: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_NAME);

  /**
   * 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_NAME |
                         G_PARAM_EXPLICIT_NOTIFY);

  /**
   * GtkATContext:realized:
   *
   * Whether the `GtkATContext` has been realized or not.
   *
   * Since: 4.24
   */

  obj_props[PROP_REALIZED] =
    g_param_spec_boolean ("realized", NULL, NULL,
                          FALSE,
                          G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_NAME);

  /**
   * 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_FLOW_FROM + 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",
  [GTK_ACCESSIBLE_PROPERTY_HELP_TEXT]           = "helptext",
};

/*< 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_HELP_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",
  [GTK_ACCESSIBLE_RELATION_LABEL_FOR]           = "labelfor",
  [GTK_ACCESSIBLE_RELATION_DESCRIPTION_FOR]     = "descriptionfor",
  [GTK_ACCESSIBLE_RELATION_CONTROLLED_BY]       = "controlledby",
  [GTK_ACCESSIBLE_RELATION_DETAILS_FOR]         = "detailsfor",
  [GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE_FOR]   = "errormessagefor",
  [GTK_ACCESSIBLE_RELATION_FLOW_FROM]           = "flowfrom",
};

/*< 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_FLOW_FROM,
                        "<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:
 * @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:
 * @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;
}


static GtkATContext * get_parent_context (GtkATContext *self);

static inline void
maybe_realize_context (GtkATContext *self)
{
  if (GTK_IS_WIDGET (self->accessible))
    {
      GtkATContext *parent_context = get_parent_context (self);

      if (parent_context && parent_context->realized)
        gtk_at_context_realize (self);

      g_clear_object (&parent_context);
    }
  else
    {
      GtkAccessible *accessible_parent;

      gtk_at_context_realize (self);

      accessible_parent = self->accessible_parent;
      while (accessible_parent && !GTK_IS_WIDGET (accessible_parent))
        {
          GtkATContext *parent_context = gtk_accessible_get_at_context (accessible_parent);

          if (!parent_context)
            break;

          gtk_at_context_realize (parent_context);
          accessible_parent = parent_context->accessible_parent;

          g_clear_object (&parent_context);
        }
    }
}

/*< 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);

          maybe_realize_context (self);
        }
    }
}

/*< 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) || defined(GDK_WINDOWING_X11)
  { "AT-SPI""atspi", gtk_at_spi_create_context },
#endif
#ifdef HAVE_ACCESSKIT
  { "AccessKit""accesskit", gtk_accesskit_create_context },
#endif
  { "Test""test", gtk_test_at_context_new },
};

/**
 * 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;
  GtkATContext *res = NULL;

  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");
#ifdef HAVE_ACCESSKIT
          g_print ("   accesskit - Use the AccessKit accessibility backend\n");
#else
          g_print ("   accesskit - Disabled during GTK build\n");
#endif
#if defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND)
          g_print ("       atspi - Use the AT-SPI accessibility backend\n");
#else
          g_print ("       atspi - Not available on this platform\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;

  for (size_t i = 0; i < G_N_ELEMENTS (a11y_backends); i++)
    {
      g_assert (a11y_backends[i].name != NULL);

      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;

  g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]);
}

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->realized)
    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);
}

static void
append_to_accessible_relation (GtkATContext          *self,
                               GtkAccessibleRelation  relation,
                               GtkAccessible         *accessible)
{
  g_return_if_fail (GTK_IS_AT_CONTEXT (self));
  GtkAccessibleValue *target_value;

  if (gtk_accessible_attribute_set_contains (self->relations, relation))
    {
      target_value = gtk_accessible_value_ref (gtk_accessible_attribute_set_get_value (self->relations, relation));
    }
  else
    {
      target_value = gtk_reference_list_accessible_value_new (NULL);
      gtk_accessible_attribute_set_add (self->relations, relation, target_value);
    }

  gtk_reference_list_accessible_value_append (target_value, accessible);

  gtk_accessible_value_unref (target_value);

  self->updated_relations |= (1 << relation);
}

static void
remove_from_accessible_relation (GtkATContext          *self,
                                 GtkAccessibleRelation  relation,
                                 GtkAccessible         *accessible)
{
  g_return_if_fail (GTK_IS_AT_CONTEXT (self));
  GtkAccessibleValue * target_value;

  if (!gtk_accessible_attribute_set_contains (self->relations, relation))
    return;

  target_value = gtk_accessible_attribute_set_get_value (self->relations, relation);
  gtk_reference_list_accessible_value_remove (target_value, accessible);

  self->updated_relations |= (1 << relation);
}

static void
update_reverse_relation (GtkATContext *self, GtkAccessibleRelation relation, GtkAccessibleValue *value)
{
  struct {
    GtkAccessibleRelation rel;
    GtkAccessibleRelation reverse_rel;
  } reverse_rels_map[] = {
    { GTK_ACCESSIBLE_RELATION_LABELLED_BY, GTK_ACCESSIBLE_RELATION_LABEL_FOR },
    { GTK_ACCESSIBLE_RELATION_DESCRIBED_BY, GTK_ACCESSIBLE_RELATION_DESCRIPTION_FOR },
    { GTK_ACCESSIBLE_RELATION_CONTROLS, GTK_ACCESSIBLE_RELATION_CONTROLLED_BY },
    { GTK_ACCESSIBLE_RELATION_DETAILS, GTK_ACCESSIBLE_RELATION_DETAILS_FOR },
    { GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE, GTK_ACCESSIBLE_RELATION_ERROR_MESSAGE_FOR },
    { GTK_ACCESSIBLE_RELATION_FLOW_TO, GTK_ACCESSIBLE_RELATION_FLOW_FROM },
  };

  GList *l;
  GtkATContext *related_context;
  for (int i = 0; i < G_N_ELEMENTS (reverse_rels_map); i++)
    {
      if (relation == reverse_rels_map[i].rel)
        {
          if (value)
            {
              for (l = gtk_reference_list_accessible_value_get (value); l; l = l->next)
                {
                  related_context = gtk_accessible_get_at_context (l->data);
                  append_to_accessible_relation (related_context, reverse_rels_map[i].reverse_rel, self->accessible);
                  g_clear_object (&related_context);
                }
            }
          else
            {
              if (gtk_accessible_attribute_set_contains (self->relations, relation))
                {
                  GtkAccessibleValue *val = gtk_accessible_attribute_set_get_value (self->relations, relation);
                  for (l = gtk_reference_list_accessible_value_get (val); l; l = l->next)
                    {
                      related_context = gtk_accessible_get_at_context (l->data);
                      if (!related_context)
                        continue;
                      remove_from_accessible_relation (related_context, reverse_rels_map[i].reverse_rel, self->accessible);
                      g_clear_object (&related_context);
                    }
                }
            }
          break;
        }
    }
}

/*< 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;

  /* We are setting the relation to a new value,
  * so we must get rid of the reverse relations first.
  */

  update_reverse_relation (self, relation, NULL);
  /* Now, we can create the new reverse relations if it makes sense. */
  if (value != NULL)
    update_reverse_relation (self, relation, value);

  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_APPLICATION] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
  [GTK_ACCESSIBLE_ROLE_ARTICLE] = NAME_FROM_AUTHOR,
  [GTK_ACCESSIBLE_ROLE_BANNER] = NAME_FROM_AUTHOR,
  [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_COMMENT] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT,
  [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_PARAGRAPH] = GTK_ACCESSIBLE_NAME_PROHIBITED,
  [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] = NAME_FROM_AUTHOR,
  [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_TERMINAL] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
  [GTK_ACCESSIBLE_ROLE_TEXT_BOX] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
  [GTK_ACCESSIBLE_ROLE_TIME] = NAME_FROM_AUTHOR,
  [GTK_ACCESSIBLE_ROLE_TIMER] = NAME_FROM_AUTHOR,
  [GTK_ACCESSIBLE_ROLE_TOGGLE_BUTTON] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_REQUIRED,
  [GTK_ACCESSIBLE_ROLE_TOOLBAR] = NAME_FROM_AUTHOR|GTK_ACCESSIBLE_NAME_RECOMMENDED,
  [GTK_ACCESSIBLE_ROLE_TOOLTIP] = NAME_FROM_AUTHOR|NAME_FROM_CONTENT| GTK_ACCESSIBLE_NAME_REQUIRED,
  [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,
};

/* < 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));
}

gboolean
gtk_at_context_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,
                                    gboolean               check_duplicates)
{
  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, TRUEFALSE, check_duplicates);

                  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, FALSETRUE, check_duplicates);

              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.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))
        {
          gboolean append = !check_duplicates;

          if (!append)
            {
              char *description = gtk_at_context_get_description_internal (self, FALSE);
              char *name = gtk_at_context_get_name_internal (self, FALSE);

              append =
                (property == GTK_ACCESSIBLE_PROPERTY_LABEL && strcmp (text, description) != 0) ||
                (property == GTK_ACCESSIBLE_PROPERTY_DESCRIPTION && strcmp (text, name) != 0);

              g_free (description);
              g_free (name);
            }

          if (append)
            append_with_space (res, text);
        }
    }
}

static char *
gtk_at_context_get_text (GtkATContext          *self,
                         GtkAccessibleProperty  property,
                         GtkAccessibleRelation  relation,
gboolean              check_duplicates)
{
  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 (gtk_at_context_is_nested_button (self))
    {
      parent = get_parent_context (self);
      self = parent;
      if (gtk_at_context_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, FALSEFALSEcheck_duplicates);

  g_ptr_array_unref (nodes);

  g_clear_object (&parent);

  return g_string_free (res, FALSE);
}

static char *
gtk_at_context_get_name_internal (GtkATContext *self, gboolean check_duplicates)
{
  return gtk_at_context_get_text (self, GTK_ACCESSIBLE_PROPERTY_LABEL, GTK_ACCESSIBLE_RELATION_LABELLED_BY, check_duplicates);
}

/*< 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)
{
  /*
  * We intentionally don't check for duplicates here, as the name
  * is more important, and we want the tooltip as the name
  * if everything else fails.
  */

  return gtk_at_context_get_name_internal (self, FALSE);
}

static char *
gtk_at_context_get_description_internal (GtkATContext *self, gboolean check_duplicates)
{
  return gtk_at_context_get_text (self, GTK_ACCESSIBLE_PROPERTY_DESCRIPTION, GTK_ACCESSIBLE_RELATION_DESCRIBED_BY, check_duplicates);
}

/*< 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_description_internal (self, TRUE);
}

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);
}

void
gtk_at_context_announce (GtkATContext                      *self,
                         const char                        *message,
                         GtkAccessibleAnnouncementPriority  priority)
{
  if (!self->realized)
    return;

  GTK_AT_CONTEXT_GET_CLASS (self)->announce (self, message, priority);
}

void
gtk_at_context_update_caret_position (GtkATContext *self)
{
  if (!self->realized)
    return;

  GTK_AT_CONTEXT_GET_CLASS (self)->update_caret_position (self);
}

void
gtk_at_context_update_selection_bound (GtkATContext *self)
{
  if (!self->realized)
    return;

  GTK_AT_CONTEXT_GET_CLASS (self)->update_selection_bound (self);
}

void
gtk_at_context_update_text_contents (GtkATContext *self,
                                     GtkAccessibleTextContentChange change,
                                     unsigned int start,
                                     unsigned int end)
{
  if (!self->realized)
    return;

  GTK_AT_CONTEXT_GET_CLASS (self)->update_text_contents (self, change, start, end);
}

Messung V0.5 in Prozent
C=98 H=96 G=96

¤ Dauer der Verarbeitung: 0.47 Sekunden  (vorverarbeitet am  2026-07-03) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik