forked from AuroraMiddleware/gtk
9de2b4b0e1
The AT-SPI cache interface is used to quickly populate the accessible objects tree. The tricky bit is ensuring that we emit change notifications on the cache only when the cache is available, which means waiting until the root is asynchronously registered.
1859 lines
62 KiB
C
1859 lines
62 KiB
C
/* gtkatspicontext.c: AT-SPI GtkATContext implementation
|
|
*
|
|
* 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/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkatspicontextprivate.h"
|
|
|
|
#include "gtkaccessibleprivate.h"
|
|
|
|
#include "gtkatspiactionprivate.h"
|
|
#include "gtkatspicacheprivate.h"
|
|
#include "gtkatspieditabletextprivate.h"
|
|
#include "gtkatspiprivate.h"
|
|
#include "gtkatspirootprivate.h"
|
|
#include "gtkatspiselectionprivate.h"
|
|
#include "gtkatspitextprivate.h"
|
|
#include "gtkatspiutilsprivate.h"
|
|
#include "gtkatspivalueprivate.h"
|
|
#include "gtkatspicomponentprivate.h"
|
|
#include "a11y/atspi/atspi-accessible.h"
|
|
#include "a11y/atspi/atspi-action.h"
|
|
#include "a11y/atspi/atspi-editabletext.h"
|
|
#include "a11y/atspi/atspi-text.h"
|
|
#include "a11y/atspi/atspi-value.h"
|
|
#include "a11y/atspi/atspi-selection.h"
|
|
#include "a11y/atspi/atspi-component.h"
|
|
|
|
#include "gtkdebug.h"
|
|
#include "gtkeditable.h"
|
|
#include "gtkentryprivate.h"
|
|
#include "gtkroot.h"
|
|
#include "gtkstack.h"
|
|
#include "gtktextview.h"
|
|
#include "gtktypebuiltins.h"
|
|
#include "gtkwindow.h"
|
|
|
|
#include <gio/gio.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#if defined(GDK_WINDOWING_WAYLAND)
|
|
# include <gdk/wayland/gdkwaylanddisplay.h>
|
|
#endif
|
|
#if defined(GDK_WINDOWING_X11)
|
|
# include <gdk/x11/gdkx11display.h>
|
|
# include <gdk/x11/gdkx11property.h>
|
|
#endif
|
|
|
|
/* We create a GtkAtSpiContext object for (almost) every widget.
|
|
*
|
|
* Each context implements a number of Atspi interfaces on a D-Bus
|
|
* object. The context objects are connected into a tree by the
|
|
* Parent property and GetChildAtIndex method of the Accessible
|
|
* interface.
|
|
*
|
|
* The tree is an almost perfect mirror image of the widget tree,
|
|
* with a few notable exceptions:
|
|
*
|
|
* - We don't create contexts for the GtkText widgets inside
|
|
* entry wrappers, since the Text functionality is represented
|
|
* on the entry contexts.
|
|
*
|
|
* - We insert non-widget backed context objects for each page
|
|
* of a stack. The main purpose of these extra context is to
|
|
* hold the TAB_PANEL role and be the target of the CONTROLS
|
|
* relation with their corresponding tabs (in the stack
|
|
* switcher or notebook).
|
|
*/
|
|
|
|
struct _GtkAtSpiContext
|
|
{
|
|
GtkATContext parent_instance;
|
|
|
|
/* The root object, used as a entry point */
|
|
GtkAtSpiRoot *root;
|
|
|
|
/* The cache object, used to retrieve ATContexts */
|
|
GtkAtSpiCache *cache;
|
|
|
|
/* The address for the ATSPI accessibility bus */
|
|
char *bus_address;
|
|
|
|
/* The object path of the ATContext on the bus */
|
|
char *context_path;
|
|
|
|
/* Just a pointer; the connection is owned by the GtkAtSpiRoot
|
|
* associated to the GtkATContext
|
|
*/
|
|
GDBusConnection *connection;
|
|
|
|
/* Accerciser refuses to work unless we implement a GetInterface
|
|
* call that returns a list of all implemented interfaces. We
|
|
* collect the answer here.
|
|
*/
|
|
GVariant *interfaces;
|
|
|
|
guint registration_ids[20];
|
|
guint n_registered_objects;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_BUS_ADDRESS = 1,
|
|
|
|
N_PROPS
|
|
};
|
|
|
|
static GParamSpec *obj_props[N_PROPS];
|
|
|
|
G_DEFINE_TYPE (GtkAtSpiContext, gtk_at_spi_context, GTK_TYPE_AT_CONTEXT)
|
|
|
|
/* {{{ State handling */
|
|
static void
|
|
set_atspi_state (guint64 *states,
|
|
AtspiStateType state)
|
|
{
|
|
*states |= (G_GUINT64_CONSTANT (1) << state);
|
|
}
|
|
|
|
static void
|
|
unset_atspi_state (guint64 *states,
|
|
AtspiStateType state)
|
|
{
|
|
*states &= ~(G_GUINT64_CONSTANT (1) << state);
|
|
}
|
|
|
|
static void
|
|
collect_states (GtkAtSpiContext *self,
|
|
GVariantBuilder *builder)
|
|
{
|
|
GtkATContext *ctx = GTK_AT_CONTEXT (self);
|
|
GtkAccessibleValue *value;
|
|
GtkAccessible *accessible;
|
|
guint64 states = 0;
|
|
|
|
accessible = gtk_at_context_get_accessible (ctx);
|
|
|
|
set_atspi_state (&states, ATSPI_STATE_VISIBLE);
|
|
|
|
if (ctx->accessible_role == GTK_ACCESSIBLE_ROLE_TEXT_BOX ||
|
|
ctx->accessible_role == GTK_ACCESSIBLE_ROLE_SEARCH_BOX ||
|
|
ctx->accessible_role == GTK_ACCESSIBLE_ROLE_SPIN_BUTTON)
|
|
set_atspi_state (&states, ATSPI_STATE_EDITABLE);
|
|
|
|
if (gtk_at_context_has_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_READ_ONLY))
|
|
{
|
|
value = gtk_at_context_get_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_READ_ONLY);
|
|
if (gtk_boolean_accessible_value_get (value))
|
|
{
|
|
set_atspi_state (&states, ATSPI_STATE_READ_ONLY);
|
|
unset_atspi_state (&states, ATSPI_STATE_EDITABLE);
|
|
}
|
|
}
|
|
|
|
if (gtk_accessible_get_platform_state (accessible, GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSABLE))
|
|
set_atspi_state (&states, ATSPI_STATE_FOCUSABLE);
|
|
|
|
if (gtk_accessible_get_platform_state (accessible, GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSED))
|
|
set_atspi_state (&states, ATSPI_STATE_FOCUSED);
|
|
|
|
if (gtk_at_context_has_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_ORIENTATION))
|
|
{
|
|
value = gtk_at_context_get_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_ORIENTATION);
|
|
if (gtk_orientation_accessible_value_get (value) == GTK_ORIENTATION_HORIZONTAL)
|
|
set_atspi_state (&states, ATSPI_STATE_HORIZONTAL);
|
|
else
|
|
set_atspi_state (&states, ATSPI_STATE_VERTICAL);
|
|
}
|
|
|
|
if (gtk_at_context_has_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_MODAL))
|
|
{
|
|
value = gtk_at_context_get_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_MODAL);
|
|
if (gtk_boolean_accessible_value_get (value))
|
|
set_atspi_state (&states, ATSPI_STATE_MODAL);
|
|
}
|
|
|
|
if (gtk_at_context_has_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_MULTI_LINE))
|
|
{
|
|
value = gtk_at_context_get_accessible_property (ctx, GTK_ACCESSIBLE_PROPERTY_MULTI_LINE);
|
|
if (gtk_boolean_accessible_value_get (value))
|
|
set_atspi_state (&states, ATSPI_STATE_MULTI_LINE);
|
|
}
|
|
|
|
if (gtk_at_context_has_accessible_state (ctx, GTK_ACCESSIBLE_STATE_BUSY))
|
|
{
|
|
value = gtk_at_context_get_accessible_state (ctx, GTK_ACCESSIBLE_STATE_BUSY);
|
|
if (gtk_boolean_accessible_value_get (value))
|
|
set_atspi_state (&states, ATSPI_STATE_BUSY);
|
|
}
|
|
|
|
if (gtk_at_context_has_accessible_state (ctx, GTK_ACCESSIBLE_STATE_CHECKED))
|
|
{
|
|
value = gtk_at_context_get_accessible_state (ctx, GTK_ACCESSIBLE_STATE_CHECKED);
|
|
switch (gtk_tristate_accessible_value_get (value))
|
|
{
|
|
case GTK_ACCESSIBLE_TRISTATE_TRUE:
|
|
set_atspi_state (&states, ATSPI_STATE_CHECKED);
|
|
break;
|
|
case GTK_ACCESSIBLE_TRISTATE_MIXED:
|
|
set_atspi_state (&states, ATSPI_STATE_INDETERMINATE);
|
|
break;
|
|
case GTK_ACCESSIBLE_TRISTATE_FALSE:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (gtk_at_context_has_accessible_state (ctx, GTK_ACCESSIBLE_STATE_DISABLED))
|
|
{
|
|
value = gtk_at_context_get_accessible_state (ctx, GTK_ACCESSIBLE_STATE_DISABLED);
|
|
if (!gtk_boolean_accessible_value_get (value))
|
|
set_atspi_state (&states, ATSPI_STATE_SENSITIVE);
|
|
}
|
|
else
|
|
set_atspi_state (&states, ATSPI_STATE_SENSITIVE);
|
|
|
|
if (gtk_at_context_has_accessible_state (ctx, GTK_ACCESSIBLE_STATE_EXPANDED))
|
|
{
|
|
value = gtk_at_context_get_accessible_state (ctx, GTK_ACCESSIBLE_STATE_EXPANDED);
|
|
if (value->value_class->type == GTK_ACCESSIBLE_VALUE_TYPE_BOOLEAN)
|
|
{
|
|
set_atspi_state (&states, ATSPI_STATE_EXPANDABLE);
|
|
if (gtk_boolean_accessible_value_get (value))
|
|
set_atspi_state (&states, ATSPI_STATE_EXPANDED);
|
|
}
|
|
}
|
|
|
|
if (gtk_at_context_has_accessible_state (ctx, GTK_ACCESSIBLE_STATE_INVALID))
|
|
{
|
|
value = gtk_at_context_get_accessible_state (ctx, GTK_ACCESSIBLE_STATE_INVALID);
|
|
switch (gtk_invalid_accessible_value_get (value))
|
|
{
|
|
case GTK_ACCESSIBLE_INVALID_TRUE:
|
|
case GTK_ACCESSIBLE_INVALID_GRAMMAR:
|
|
case GTK_ACCESSIBLE_INVALID_SPELLING:
|
|
set_atspi_state (&states, ATSPI_STATE_INVALID);
|
|
break;
|
|
case GTK_ACCESSIBLE_INVALID_FALSE:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (gtk_at_context_has_accessible_state (ctx, GTK_ACCESSIBLE_STATE_PRESSED))
|
|
{
|
|
value = gtk_at_context_get_accessible_state (ctx, GTK_ACCESSIBLE_STATE_PRESSED);
|
|
switch (gtk_tristate_accessible_value_get (value))
|
|
{
|
|
case GTK_ACCESSIBLE_TRISTATE_TRUE:
|
|
set_atspi_state (&states, ATSPI_STATE_PRESSED);
|
|
break;
|
|
case GTK_ACCESSIBLE_TRISTATE_MIXED:
|
|
set_atspi_state (&states, ATSPI_STATE_INDETERMINATE);
|
|
break;
|
|
case GTK_ACCESSIBLE_TRISTATE_FALSE:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (gtk_at_context_has_accessible_state (ctx, GTK_ACCESSIBLE_STATE_SELECTED))
|
|
{
|
|
value = gtk_at_context_get_accessible_state (ctx, GTK_ACCESSIBLE_STATE_SELECTED);
|
|
if (value->value_class->type == GTK_ACCESSIBLE_VALUE_TYPE_BOOLEAN)
|
|
{
|
|
set_atspi_state (&states, ATSPI_STATE_SELECTABLE);
|
|
if (gtk_boolean_accessible_value_get (value))
|
|
set_atspi_state (&states, ATSPI_STATE_SELECTED);
|
|
}
|
|
}
|
|
|
|
g_variant_builder_add (builder, "u", (guint32) (states & 0xffffffff));
|
|
g_variant_builder_add (builder, "u", (guint32) (states >> 32));
|
|
}
|
|
/* }}} */
|
|
/* {{{ Relation handling */
|
|
static void
|
|
collect_relations (GtkAtSpiContext *self,
|
|
GVariantBuilder *builder)
|
|
{
|
|
GtkATContext *ctx = GTK_AT_CONTEXT (self);
|
|
struct {
|
|
GtkAccessibleRelation r;
|
|
AtspiRelationType s;
|
|
} map[] = {
|
|
{ GTK_ACCESSIBLE_RELATION_LABELLED_BY, ATSPI_RELATION_LABELLED_BY },
|
|
{ GTK_ACCESSIBLE_RELATION_CONTROLS, ATSPI_RELATION_CONTROLLER_FOR },
|
|
{ GTK_ACCESSIBLE_RELATION_DESCRIBED_BY, ATSPI_RELATION_DESCRIBED_BY },
|
|
{ GTK_ACCESSIBLE_RELATION_FLOW_TO, ATSPI_RELATION_FLOWS_TO},
|
|
};
|
|
GtkAccessibleValue *value;
|
|
GList *list, *l;
|
|
GtkATContext *target_ctx;
|
|
int i;
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (map); i++)
|
|
{
|
|
if (!gtk_at_context_has_accessible_relation (ctx, map[i].r))
|
|
continue;
|
|
|
|
GVariantBuilder b = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(so)"));
|
|
|
|
value = gtk_at_context_get_accessible_relation (ctx, map[i].r);
|
|
list = gtk_reference_list_accessible_value_get (value);
|
|
|
|
for (l = list; l; l = l->next)
|
|
{
|
|
target_ctx = gtk_accessible_get_at_context (GTK_ACCESSIBLE (l->data));
|
|
|
|
/* Realize the ATContext of the target, so we can ask for its ref */
|
|
gtk_at_context_realize (target_ctx);
|
|
|
|
g_variant_builder_add (&b, "@(so)",
|
|
gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (target_ctx)));
|
|
}
|
|
|
|
g_variant_builder_add (builder, "(ua(so))", map[i].s, &b);
|
|
}
|
|
}
|
|
/* }}} */
|
|
/* {{{ Accessible implementation */
|
|
static int
|
|
get_index_in_parent (GtkWidget *widget)
|
|
{
|
|
GtkWidget *parent = gtk_widget_get_parent (widget);
|
|
GtkWidget *child;
|
|
int idx;
|
|
|
|
idx = 0;
|
|
for (child = gtk_widget_get_first_child (parent);
|
|
child;
|
|
child = gtk_widget_get_next_sibling (child))
|
|
{
|
|
if (child == widget)
|
|
return idx;
|
|
|
|
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (child)))
|
|
continue;
|
|
|
|
idx++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
get_index_in_toplevels (GtkWidget *widget)
|
|
{
|
|
GListModel *toplevels = gtk_window_get_toplevels ();
|
|
guint n_toplevels = g_list_model_get_n_items (toplevels);
|
|
GtkWidget *window;
|
|
int idx;
|
|
|
|
idx = 0;
|
|
for (guint i = 0; i < n_toplevels; i++)
|
|
{
|
|
window = g_list_model_get_item (toplevels, i);
|
|
|
|
g_object_unref (window);
|
|
|
|
if (window == widget)
|
|
return idx;
|
|
|
|
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (window)))
|
|
continue;
|
|
|
|
idx += 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static GVariant *
|
|
get_parent_context_ref (GtkAccessible *accessible)
|
|
{
|
|
GVariant *res = NULL;
|
|
|
|
if (GTK_IS_WIDGET (accessible))
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (accessible);
|
|
GtkWidget *parent = gtk_widget_get_parent (widget);
|
|
|
|
if (parent == NULL)
|
|
{
|
|
GtkATContext *context = gtk_accessible_get_at_context (accessible);
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context);
|
|
|
|
res = gtk_at_spi_root_to_ref (self->root);
|
|
}
|
|
else if (GTK_IS_STACK (parent))
|
|
{
|
|
GtkStackPage *page =
|
|
gtk_stack_get_page (GTK_STACK (parent), widget);
|
|
GtkATContext *parent_context =
|
|
gtk_accessible_get_at_context (GTK_ACCESSIBLE (page));
|
|
|
|
if (parent_context != NULL)
|
|
{
|
|
gtk_at_context_realize (parent_context);
|
|
|
|
res = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (parent_context));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GtkATContext *parent_context =
|
|
gtk_accessible_get_at_context (GTK_ACCESSIBLE (parent));
|
|
|
|
if (parent_context != NULL)
|
|
{
|
|
/* XXX: This realize() is needed otherwise opening a GtkPopover will
|
|
* emit a warning when getting the context's reference
|
|
*/
|
|
gtk_at_context_realize (parent_context);
|
|
|
|
res = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (parent_context));
|
|
}
|
|
}
|
|
}
|
|
else if (GTK_IS_STACK_PAGE (accessible))
|
|
{
|
|
GtkWidget *parent =
|
|
gtk_widget_get_parent (gtk_stack_page_get_child (GTK_STACK_PAGE (accessible)));
|
|
GtkATContext *parent_context =
|
|
gtk_accessible_get_at_context (GTK_ACCESSIBLE (parent));
|
|
|
|
if (parent_context != NULL)
|
|
res = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (parent_context));
|
|
}
|
|
|
|
if (res == NULL)
|
|
res = gtk_at_spi_null_ref ();
|
|
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
handle_accessible_method (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *method_name,
|
|
GVariant *parameters,
|
|
GDBusMethodInvocation *invocation,
|
|
gpointer user_data)
|
|
{
|
|
GtkAtSpiContext *self = user_data;
|
|
|
|
if (g_strcmp0 (method_name, "GetRole") == 0)
|
|
{
|
|
guint atspi_role = gtk_atspi_role_for_context (GTK_AT_CONTEXT (self));
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(u)", atspi_role));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetRoleName") == 0)
|
|
{
|
|
GtkAccessibleRole role = gtk_at_context_get_accessible_role (GTK_AT_CONTEXT (self));
|
|
const char *name = gtk_accessible_role_to_name (role, NULL);
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", name));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetLocalizedRoleName") == 0)
|
|
{
|
|
GtkAccessibleRole role = gtk_at_context_get_accessible_role (GTK_AT_CONTEXT (self));
|
|
const char *name = gtk_accessible_role_to_name (role, GETTEXT_PACKAGE);
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", name));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetState") == 0)
|
|
{
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(au)"));
|
|
|
|
g_variant_builder_open (&builder, G_VARIANT_TYPE ("au"));
|
|
collect_states (self, &builder);
|
|
g_variant_builder_close (&builder);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_builder_end (&builder));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetAttributes") == 0)
|
|
{
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(a{ss})"));
|
|
|
|
g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{ss}"));
|
|
g_variant_builder_add (&builder, "{ss}", "toolkit", "GTK");
|
|
|
|
if (gtk_at_context_has_accessible_property (GTK_AT_CONTEXT (self), GTK_ACCESSIBLE_PROPERTY_PLACEHOLDER))
|
|
{
|
|
GtkAccessibleValue *value;
|
|
|
|
value = gtk_at_context_get_accessible_property (GTK_AT_CONTEXT (self), GTK_ACCESSIBLE_PROPERTY_PLACEHOLDER);
|
|
|
|
g_variant_builder_add (&builder, "{ss}",
|
|
"placeholder-text", gtk_string_accessible_value_get (value));
|
|
}
|
|
|
|
g_variant_builder_close (&builder);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_builder_end (&builder));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetApplication") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_value (invocation,
|
|
g_variant_new ("(@(so))", gtk_at_spi_root_to_ref (self->root)));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetChildAtIndex") == 0)
|
|
{
|
|
GtkATContext *context = NULL;
|
|
GtkAccessible *accessible;
|
|
int idx, real_idx = 0;
|
|
|
|
g_variant_get (parameters, "(i)", &idx);
|
|
|
|
accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
|
|
|
|
if (GTK_IS_STACK_PAGE (accessible))
|
|
{
|
|
if (idx == 0)
|
|
{
|
|
GtkWidget *child;
|
|
|
|
child = gtk_stack_page_get_child (GTK_STACK_PAGE (accessible));
|
|
if (gtk_accessible_should_present (GTK_ACCESSIBLE (child)))
|
|
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (child));
|
|
}
|
|
}
|
|
else if (GTK_IS_WIDGET (accessible))
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (accessible);
|
|
GtkWidget *child;
|
|
|
|
real_idx = 0;
|
|
for (child = gtk_widget_get_first_child (widget);
|
|
child;
|
|
child = gtk_widget_get_next_sibling (child))
|
|
{
|
|
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (child)))
|
|
continue;
|
|
|
|
if (real_idx == idx)
|
|
break;
|
|
|
|
real_idx += 1;
|
|
}
|
|
|
|
if (child)
|
|
{
|
|
if (GTK_IS_STACK (accessible))
|
|
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (gtk_stack_get_page (GTK_STACK (accessible), child)));
|
|
else
|
|
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (child));
|
|
}
|
|
}
|
|
|
|
if (context == NULL)
|
|
{
|
|
g_dbus_method_invocation_return_error (invocation,
|
|
G_IO_ERROR,
|
|
G_IO_ERROR_INVALID_ARGUMENT,
|
|
"No child with index %d", idx);
|
|
return;
|
|
}
|
|
|
|
/* Realize the child ATContext in order to get its ref */
|
|
gtk_at_context_realize (context);
|
|
|
|
GVariant *ref = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (context));
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@(so))", ref));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetChildren") == 0)
|
|
{
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(so)"));
|
|
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
|
|
if (GTK_IS_WIDGET (accessible))
|
|
{
|
|
GtkWidget *widget = GTK_WIDGET (accessible);
|
|
GtkWidget *child;
|
|
|
|
for (child = gtk_widget_get_first_child (widget);
|
|
child;
|
|
child = gtk_widget_get_next_sibling (child))
|
|
{
|
|
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (child)))
|
|
continue;
|
|
|
|
GtkATContext *context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (child));
|
|
|
|
/* Realize the child ATContext in order to get its ref */
|
|
gtk_at_context_realize (context);
|
|
|
|
GVariant *ref = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (context));
|
|
|
|
if (ref != NULL)
|
|
g_variant_builder_add (&builder, "@(so)", ref);
|
|
}
|
|
}
|
|
else if (GTK_IS_STACK_PAGE (accessible))
|
|
{
|
|
GtkWidget *child = gtk_stack_page_get_child (GTK_STACK_PAGE (accessible));
|
|
|
|
if (gtk_accessible_should_present (GTK_ACCESSIBLE (child)))
|
|
{
|
|
GtkATContext *context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (child));
|
|
|
|
/* Realize the child ATContext in order to get its ref */
|
|
gtk_at_context_realize (context);
|
|
|
|
GVariant *ref = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (context));
|
|
|
|
if (ref != NULL)
|
|
g_variant_builder_add (&builder, "@(so)", ref);
|
|
}
|
|
}
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a(so))", &builder));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetIndexInParent") == 0)
|
|
{
|
|
int idx = gtk_at_spi_context_get_index_in_parent (self);
|
|
|
|
if (idx == -1)
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Not found");
|
|
else
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", idx));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetRelationSet") == 0)
|
|
{
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(ua(so))"));
|
|
collect_relations (self, &builder);
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a(ua(so)))", &builder));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetInterfaces") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@as)", self->interfaces));
|
|
}
|
|
|
|
}
|
|
|
|
static GVariant *
|
|
handle_accessible_get_property (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *property_name,
|
|
GError **error,
|
|
gpointer user_data)
|
|
{
|
|
GtkAtSpiContext *self = user_data;
|
|
GVariant *res = NULL;
|
|
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
|
|
|
|
if (g_strcmp0 (property_name, "Name") == 0)
|
|
{
|
|
char *label = gtk_at_context_get_name (GTK_AT_CONTEXT (self));
|
|
res = g_variant_new_string (label ? label : "");
|
|
g_free (label);
|
|
}
|
|
else if (g_strcmp0 (property_name, "Description") == 0)
|
|
{
|
|
char *label = gtk_at_context_get_description (GTK_AT_CONTEXT (self));
|
|
res = g_variant_new_string (label ? label : "");
|
|
g_free (label);
|
|
}
|
|
else if (g_strcmp0 (property_name, "Locale") == 0)
|
|
res = g_variant_new_string (setlocale (LC_MESSAGES, NULL));
|
|
else if (g_strcmp0 (property_name, "AccessibleId") == 0)
|
|
res = g_variant_new_string ("");
|
|
else if (g_strcmp0 (property_name, "Parent") == 0)
|
|
res = get_parent_context_ref (accessible);
|
|
else if (g_strcmp0 (property_name, "ChildCount") == 0)
|
|
res = g_variant_new_int32 (gtk_at_spi_context_get_child_count (self));
|
|
else
|
|
g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
|
|
"Unknown property '%s'", property_name);
|
|
|
|
return res;
|
|
}
|
|
|
|
static const GDBusInterfaceVTable accessible_vtable = {
|
|
handle_accessible_method,
|
|
handle_accessible_get_property,
|
|
NULL,
|
|
};
|
|
/* }}} */
|
|
/* {{{ Change notification */
|
|
static void
|
|
emit_text_changed (GtkAtSpiContext *self,
|
|
const char *kind,
|
|
int start,
|
|
int end,
|
|
const char *text)
|
|
{
|
|
if (self->connection == NULL)
|
|
return;
|
|
|
|
g_dbus_connection_emit_signal (self->connection,
|
|
NULL,
|
|
self->context_path,
|
|
"org.a11y.atspi.Event.Object",
|
|
"TextChanged",
|
|
g_variant_new ("(siiva{sv})",
|
|
kind, start, end, g_variant_new_string (text), NULL),
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
emit_text_selection_changed (GtkAtSpiContext *self,
|
|
const char *kind,
|
|
int cursor_position)
|
|
{
|
|
if (self->connection == NULL)
|
|
return;
|
|
|
|
if (strcmp (kind, "text-caret-moved") == 0)
|
|
g_dbus_connection_emit_signal (self->connection,
|
|
NULL,
|
|
self->context_path,
|
|
"org.a11y.atspi.Event.Object",
|
|
"TextCaretMoved",
|
|
g_variant_new ("(siiva{sv})",
|
|
"", cursor_position, 0, g_variant_new_string (""), NULL),
|
|
NULL);
|
|
else
|
|
g_dbus_connection_emit_signal (self->connection,
|
|
NULL,
|
|
self->context_path,
|
|
"org.a11y.atspi.Event.Object",
|
|
"TextSelectionChanged",
|
|
g_variant_new ("(siiva{sv})",
|
|
"", 0, 0, g_variant_new_string (""), NULL),
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
emit_selection_changed (GtkAtSpiContext *self,
|
|
const char *kind)
|
|
{
|
|
if (self->connection == NULL)
|
|
return;
|
|
|
|
g_dbus_connection_emit_signal (self->connection,
|
|
NULL,
|
|
self->context_path,
|
|
"org.a11y.atspi.Event.Object",
|
|
"SelectionChanged",
|
|
g_variant_new ("(siiva{sv})",
|
|
"", 0, 0, g_variant_new_string (""), NULL),
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
emit_state_changed (GtkAtSpiContext *self,
|
|
const char *name,
|
|
gboolean enabled)
|
|
{
|
|
if (self->connection == NULL)
|
|
return;
|
|
|
|
g_dbus_connection_emit_signal (self->connection,
|
|
NULL,
|
|
self->context_path,
|
|
"org.a11y.atspi.Event.Object",
|
|
"StateChanged",
|
|
g_variant_new ("(siiva{sv})",
|
|
name, enabled, 0, g_variant_new_string ("0"), NULL),
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
emit_defunct (GtkAtSpiContext *self)
|
|
{
|
|
if (self->connection == NULL)
|
|
return;
|
|
|
|
g_dbus_connection_emit_signal (self->connection,
|
|
NULL,
|
|
self->context_path,
|
|
"org.a11y.atspi.Event.Object",
|
|
"StateChanged",
|
|
g_variant_new ("(siiva{sv})", "defunct", TRUE, 0, g_variant_new_string ("0"), NULL),
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
emit_property_changed (GtkAtSpiContext *self,
|
|
const char *name,
|
|
GVariant *value)
|
|
{
|
|
if (self->connection == NULL)
|
|
return;
|
|
|
|
g_dbus_connection_emit_signal (self->connection,
|
|
NULL,
|
|
self->context_path,
|
|
"org.a11y.atspi.Event.Object",
|
|
"PropertyChange",
|
|
g_variant_new ("(siiva{sv})",
|
|
name, 0, 0, value, NULL),
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
emit_bounds_changed (GtkAtSpiContext *self,
|
|
int x,
|
|
int y,
|
|
int width,
|
|
int height)
|
|
{
|
|
if (self->connection == NULL)
|
|
return;
|
|
|
|
g_dbus_connection_emit_signal (self->connection,
|
|
NULL,
|
|
self->context_path,
|
|
"org.a11y.atspi.Event.Object",
|
|
"BoundsChanged",
|
|
g_variant_new ("(siiva{sv})",
|
|
"", 0, 0, g_variant_new ("(iiii)", x, y, width, height), NULL),
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
emit_children_changed (GtkAtSpiContext *self,
|
|
GtkAtSpiContext *child_context,
|
|
int idx,
|
|
GtkAccessibleChildState state)
|
|
{
|
|
/* If we don't have a connection on either contexts, we cannot emit a signal */
|
|
if (self->connection == NULL || child_context->connection == NULL)
|
|
return;
|
|
|
|
GVariant *context_ref = gtk_at_spi_context_to_ref (self);
|
|
GVariant *child_ref = gtk_at_spi_context_to_ref (child_context);
|
|
|
|
gtk_at_spi_emit_children_changed (self->connection,
|
|
self->context_path,
|
|
state,
|
|
idx,
|
|
child_ref,
|
|
context_ref);
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_state_change (GtkATContext *ctx,
|
|
GtkAccessibleStateChange changed_states,
|
|
GtkAccessiblePropertyChange changed_properties,
|
|
GtkAccessibleRelationChange changed_relations,
|
|
GtkAccessibleAttributeSet *states,
|
|
GtkAccessibleAttributeSet *properties,
|
|
GtkAccessibleAttributeSet *relations)
|
|
{
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ctx);
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (ctx);
|
|
GtkAccessibleValue *value;
|
|
|
|
if (GTK_IS_WIDGET (accessible) && !gtk_widget_get_realized (GTK_WIDGET (accessible)))
|
|
return;
|
|
|
|
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_HIDDEN)
|
|
{
|
|
GtkWidget *parent;
|
|
GtkATContext *context;
|
|
GtkAccessibleChildChange change;
|
|
|
|
value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_HIDDEN);
|
|
if (gtk_boolean_accessible_value_get (value))
|
|
change = GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED;
|
|
else
|
|
change = GTK_ACCESSIBLE_CHILD_CHANGE_ADDED;
|
|
|
|
if (GTK_IS_ROOT (accessible))
|
|
{
|
|
gtk_at_spi_root_child_changed (self->root, change, accessible);
|
|
}
|
|
else
|
|
{
|
|
if (GTK_IS_WIDGET (accessible))
|
|
parent = gtk_widget_get_parent (GTK_WIDGET (accessible));
|
|
else if (GTK_IS_STACK_PAGE (accessible))
|
|
parent = gtk_widget_get_parent (gtk_stack_page_get_child (GTK_STACK_PAGE (accessible)));
|
|
else
|
|
g_assert_not_reached ();
|
|
|
|
context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (parent));
|
|
gtk_at_context_child_changed (context, change, accessible);
|
|
}
|
|
}
|
|
|
|
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_BUSY)
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_BUSY);
|
|
emit_state_changed (self, "busy", gtk_boolean_accessible_value_get (value));
|
|
}
|
|
|
|
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_CHECKED)
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_CHECKED);
|
|
|
|
switch (gtk_tristate_accessible_value_get (value))
|
|
{
|
|
case GTK_ACCESSIBLE_TRISTATE_TRUE:
|
|
emit_state_changed (self, "checked", TRUE);
|
|
emit_state_changed (self, "indeterminate", FALSE);
|
|
break;
|
|
case GTK_ACCESSIBLE_TRISTATE_MIXED:
|
|
emit_state_changed (self, "checked", FALSE);
|
|
emit_state_changed (self, "indeterminate", TRUE);
|
|
break;
|
|
case GTK_ACCESSIBLE_TRISTATE_FALSE:
|
|
emit_state_changed (self, "checked", FALSE);
|
|
emit_state_changed (self, "indeterminate", FALSE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_DISABLED)
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_DISABLED);
|
|
emit_state_changed (self, "sensitive", !gtk_boolean_accessible_value_get (value));
|
|
}
|
|
|
|
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_EXPANDED)
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_EXPANDED);
|
|
if (value->value_class->type == GTK_ACCESSIBLE_VALUE_TYPE_BOOLEAN)
|
|
{
|
|
emit_state_changed (self, "expandable", TRUE);
|
|
emit_state_changed (self, "expanded",gtk_boolean_accessible_value_get (value));
|
|
}
|
|
else
|
|
emit_state_changed (self, "expandable", FALSE);
|
|
}
|
|
|
|
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_INVALID)
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_INVALID);
|
|
switch (gtk_invalid_accessible_value_get (value))
|
|
{
|
|
case GTK_ACCESSIBLE_INVALID_TRUE:
|
|
case GTK_ACCESSIBLE_INVALID_GRAMMAR:
|
|
case GTK_ACCESSIBLE_INVALID_SPELLING:
|
|
emit_state_changed (self, "invalid", TRUE);
|
|
break;
|
|
case GTK_ACCESSIBLE_INVALID_FALSE:
|
|
emit_state_changed (self, "invalid", FALSE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_PRESSED)
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_PRESSED);
|
|
switch (gtk_tristate_accessible_value_get (value))
|
|
{
|
|
case GTK_ACCESSIBLE_TRISTATE_TRUE:
|
|
emit_state_changed (self, "pressed", TRUE);
|
|
emit_state_changed (self, "indeterminate", FALSE);
|
|
break;
|
|
case GTK_ACCESSIBLE_TRISTATE_MIXED:
|
|
emit_state_changed (self, "pressed", FALSE);
|
|
emit_state_changed (self, "indeterminate", TRUE);
|
|
break;
|
|
case GTK_ACCESSIBLE_TRISTATE_FALSE:
|
|
emit_state_changed (self, "pressed", FALSE);
|
|
emit_state_changed (self, "indeterminate", FALSE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (changed_states & GTK_ACCESSIBLE_STATE_CHANGE_SELECTED)
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (states, GTK_ACCESSIBLE_STATE_SELECTED);
|
|
if (value->value_class->type == GTK_ACCESSIBLE_VALUE_TYPE_BOOLEAN)
|
|
{
|
|
emit_state_changed (self, "selectable", TRUE);
|
|
emit_state_changed (self, "selected",gtk_boolean_accessible_value_get (value));
|
|
}
|
|
else
|
|
emit_state_changed (self, "selectable", FALSE);
|
|
}
|
|
|
|
if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_READ_ONLY)
|
|
{
|
|
gboolean readonly;
|
|
|
|
value = gtk_accessible_attribute_set_get_value (properties, GTK_ACCESSIBLE_PROPERTY_READ_ONLY);
|
|
readonly = gtk_boolean_accessible_value_get (value);
|
|
|
|
emit_state_changed (self, "read-only", readonly);
|
|
if (ctx->accessible_role == GTK_ACCESSIBLE_ROLE_TEXT_BOX)
|
|
emit_state_changed (self, "editable", !readonly);
|
|
}
|
|
|
|
if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_ORIENTATION)
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (properties, GTK_ACCESSIBLE_PROPERTY_ORIENTATION);
|
|
if (gtk_orientation_accessible_value_get (value) == GTK_ORIENTATION_HORIZONTAL)
|
|
{
|
|
emit_state_changed (self, "horizontal", TRUE);
|
|
emit_state_changed (self, "vertical", FALSE);
|
|
}
|
|
else
|
|
{
|
|
emit_state_changed (self, "horizontal", FALSE);
|
|
emit_state_changed (self, "vertical", TRUE);
|
|
}
|
|
}
|
|
|
|
if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_MODAL)
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (properties, GTK_ACCESSIBLE_PROPERTY_MODAL);
|
|
emit_state_changed (self, "modal", gtk_boolean_accessible_value_get (value));
|
|
}
|
|
|
|
if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_MULTI_LINE)
|
|
{
|
|
value = gtk_accessible_attribute_set_get_value (properties, GTK_ACCESSIBLE_PROPERTY_MULTI_LINE);
|
|
emit_state_changed (self, "multi-line", gtk_boolean_accessible_value_get (value));
|
|
}
|
|
|
|
if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_LABEL)
|
|
{
|
|
char *label = gtk_at_context_get_name (GTK_AT_CONTEXT (self));
|
|
GVariant *v = g_variant_new_take_string (label);
|
|
emit_property_changed (self, "accessible-name", v);
|
|
}
|
|
|
|
if (changed_properties & GTK_ACCESSIBLE_PROPERTY_CHANGE_DESCRIPTION)
|
|
{
|
|
char *label = gtk_at_context_get_description (GTK_AT_CONTEXT (self));
|
|
GVariant *v = g_variant_new_take_string (label);
|
|
emit_property_changed (self, "accessible-description", v);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_platform_change (GtkATContext *ctx,
|
|
GtkAccessiblePlatformChange changed_platform)
|
|
{
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ctx);
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (ctx);
|
|
GtkWidget *widget;
|
|
|
|
if (!GTK_IS_WIDGET (accessible))
|
|
return;
|
|
|
|
widget = GTK_WIDGET (accessible);
|
|
if (!gtk_widget_get_realized (widget))
|
|
return;
|
|
|
|
if (changed_platform & GTK_ACCESSIBLE_PLATFORM_CHANGE_FOCUSABLE)
|
|
{
|
|
gboolean state = gtk_accessible_get_platform_state (GTK_ACCESSIBLE (widget),
|
|
GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSABLE);
|
|
emit_state_changed (self, "focusable", state);
|
|
}
|
|
|
|
if (changed_platform & GTK_ACCESSIBLE_PLATFORM_CHANGE_FOCUSED)
|
|
{
|
|
gboolean state = gtk_accessible_get_platform_state (GTK_ACCESSIBLE (widget),
|
|
GTK_ACCESSIBLE_PLATFORM_STATE_FOCUSED);
|
|
emit_state_changed (self, "focused", state);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_bounds_change (GtkATContext *ctx)
|
|
{
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ctx);
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (ctx);
|
|
GtkWidget *widget;
|
|
GtkWidget *parent;
|
|
double x, y;
|
|
int width, height;
|
|
|
|
if (!GTK_IS_WIDGET (accessible))
|
|
return;
|
|
|
|
widget = GTK_WIDGET (accessible);
|
|
if (!gtk_widget_get_realized (widget))
|
|
return;
|
|
|
|
parent = gtk_widget_get_parent (widget);
|
|
|
|
if (parent)
|
|
gtk_widget_translate_coordinates (widget, parent, 0., 0., &x, &y);
|
|
else
|
|
x = y = 0.;
|
|
|
|
width = gtk_widget_get_width (widget);
|
|
height = gtk_widget_get_height (widget);
|
|
|
|
emit_bounds_changed (self, (int)x, (int)y, width, height);
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_child_change (GtkATContext *ctx,
|
|
GtkAccessibleChildChange change,
|
|
GtkAccessible *child)
|
|
{
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (ctx);
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (ctx);
|
|
GtkATContext *child_context = gtk_accessible_get_at_context (child);
|
|
GtkWidget *parent_widget;
|
|
GtkWidget *child_widget;
|
|
int idx = 0;
|
|
|
|
if (!GTK_IS_WIDGET (accessible))
|
|
return;
|
|
|
|
if (child_context == NULL)
|
|
return;
|
|
|
|
/* handle the stack page special case */
|
|
if (GTK_IS_WIDGET (child) &&
|
|
GTK_IS_STACK (gtk_widget_get_parent (GTK_WIDGET (child))))
|
|
{
|
|
GtkWidget *stack;
|
|
GtkStackPage *page;
|
|
GListModel *pages;
|
|
|
|
stack = gtk_widget_get_parent (GTK_WIDGET (child));
|
|
page = gtk_stack_get_page (GTK_STACK (stack), GTK_WIDGET (child));
|
|
pages = G_LIST_MODEL (gtk_stack_get_pages (GTK_STACK (stack)));
|
|
idx = 0;
|
|
for (guint i = 0; i < g_list_model_get_n_items (pages); i++)
|
|
{
|
|
GtkStackPage *item = g_list_model_get_item (pages, i);
|
|
|
|
g_object_unref (item);
|
|
|
|
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (item)))
|
|
continue;
|
|
|
|
if (item == page)
|
|
break;
|
|
|
|
idx++;
|
|
}
|
|
g_object_unref (pages);
|
|
|
|
if (change & GTK_ACCESSIBLE_CHILD_CHANGE_ADDED)
|
|
{
|
|
emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (stack))),
|
|
GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))),
|
|
idx,
|
|
GTK_ACCESSIBLE_CHILD_STATE_ADDED);
|
|
|
|
emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))),
|
|
GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (child)),
|
|
0,
|
|
GTK_ACCESSIBLE_CHILD_STATE_ADDED);
|
|
}
|
|
|
|
if (change & GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED)
|
|
{
|
|
emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))),
|
|
GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (child)),
|
|
0,
|
|
GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
|
|
emit_children_changed (GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (stack))),
|
|
GTK_AT_SPI_CONTEXT (gtk_accessible_get_at_context (GTK_ACCESSIBLE (page))),
|
|
idx,
|
|
GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
parent_widget = GTK_WIDGET (accessible);
|
|
|
|
if (GTK_IS_STACK_PAGE (child))
|
|
child_widget = gtk_stack_page_get_child (GTK_STACK_PAGE (child));
|
|
else
|
|
child_widget = GTK_WIDGET (child);
|
|
|
|
if (gtk_widget_get_parent (child_widget) != parent_widget)
|
|
{
|
|
idx = 0;
|
|
}
|
|
else
|
|
{
|
|
for (GtkWidget *iter = gtk_widget_get_first_child (parent_widget);
|
|
iter != NULL;
|
|
iter = gtk_widget_get_next_sibling (iter))
|
|
{
|
|
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (iter)))
|
|
continue;
|
|
|
|
if (iter == child_widget)
|
|
break;
|
|
|
|
idx += 1;
|
|
}
|
|
}
|
|
|
|
if (change & GTK_ACCESSIBLE_CHILD_CHANGE_ADDED)
|
|
emit_children_changed (self,
|
|
GTK_AT_SPI_CONTEXT (child_context),
|
|
idx,
|
|
GTK_ACCESSIBLE_CHILD_STATE_ADDED);
|
|
else if (change & GTK_ACCESSIBLE_CHILD_CHANGE_REMOVED)
|
|
emit_children_changed (self,
|
|
GTK_AT_SPI_CONTEXT (child_context),
|
|
idx,
|
|
GTK_ACCESSIBLE_CHILD_STATE_REMOVED);
|
|
}
|
|
/* }}} */
|
|
/* {{{ D-Bus Registration */
|
|
static void
|
|
gtk_at_spi_context_register_object (GtkAtSpiContext *self)
|
|
{
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
|
|
GVariantBuilder interfaces = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_STRING_ARRAY);
|
|
const GDBusInterfaceVTable *vtable;
|
|
|
|
g_variant_builder_add (&interfaces, "s", atspi_accessible_interface.name);
|
|
self->registration_ids[self->n_registered_objects] =
|
|
g_dbus_connection_register_object (self->connection,
|
|
self->context_path,
|
|
(GDBusInterfaceInfo *) &atspi_accessible_interface,
|
|
&accessible_vtable,
|
|
self,
|
|
NULL,
|
|
NULL);
|
|
self->n_registered_objects++;
|
|
|
|
vtable = gtk_atspi_get_component_vtable (accessible);
|
|
if (vtable)
|
|
{
|
|
g_variant_builder_add (&interfaces, "s", atspi_component_interface.name);
|
|
self->registration_ids[self->n_registered_objects] =
|
|
g_dbus_connection_register_object (self->connection,
|
|
self->context_path,
|
|
(GDBusInterfaceInfo *) &atspi_component_interface,
|
|
vtable,
|
|
self,
|
|
NULL,
|
|
NULL);
|
|
self->n_registered_objects++;
|
|
}
|
|
|
|
vtable = gtk_atspi_get_text_vtable (accessible);
|
|
if (vtable)
|
|
{
|
|
g_variant_builder_add (&interfaces, "s", atspi_text_interface.name);
|
|
self->registration_ids[self->n_registered_objects] =
|
|
g_dbus_connection_register_object (self->connection,
|
|
self->context_path,
|
|
(GDBusInterfaceInfo *) &atspi_text_interface,
|
|
vtable,
|
|
self,
|
|
NULL,
|
|
NULL);
|
|
self->n_registered_objects++;
|
|
}
|
|
|
|
vtable = gtk_atspi_get_editable_text_vtable (accessible);
|
|
if (vtable)
|
|
{
|
|
g_variant_builder_add (&interfaces, "s", atspi_editable_text_interface.name);
|
|
self->registration_ids[self->n_registered_objects] =
|
|
g_dbus_connection_register_object (self->connection,
|
|
self->context_path,
|
|
(GDBusInterfaceInfo *) &atspi_editable_text_interface,
|
|
vtable,
|
|
self,
|
|
NULL,
|
|
NULL);
|
|
self->n_registered_objects++;
|
|
}
|
|
vtable = gtk_atspi_get_value_vtable (accessible);
|
|
if (vtable)
|
|
{
|
|
g_variant_builder_add (&interfaces, "s", atspi_value_interface.name);
|
|
self->registration_ids[self->n_registered_objects] =
|
|
g_dbus_connection_register_object (self->connection,
|
|
self->context_path,
|
|
(GDBusInterfaceInfo *) &atspi_value_interface,
|
|
vtable,
|
|
self,
|
|
NULL,
|
|
NULL);
|
|
self->n_registered_objects++;
|
|
}
|
|
|
|
/* Calling gtk_accessible_get_accessible_role() in here will recurse,
|
|
* so pass the role in explicitly.
|
|
*/
|
|
vtable = gtk_atspi_get_selection_vtable (accessible,
|
|
GTK_AT_CONTEXT (self)->accessible_role);
|
|
if (vtable)
|
|
{
|
|
g_variant_builder_add (&interfaces, "s", atspi_selection_interface.name);
|
|
self->registration_ids[self->n_registered_objects] =
|
|
g_dbus_connection_register_object (self->connection,
|
|
self->context_path,
|
|
(GDBusInterfaceInfo *) &atspi_selection_interface,
|
|
vtable,
|
|
self,
|
|
NULL,
|
|
NULL);
|
|
self->n_registered_objects++;
|
|
}
|
|
|
|
vtable = gtk_atspi_get_action_vtable (accessible);
|
|
if (vtable)
|
|
{
|
|
g_variant_builder_add (&interfaces, "s", atspi_action_interface.name);
|
|
self->registration_ids[self->n_registered_objects] =
|
|
g_dbus_connection_register_object (self->connection,
|
|
self->context_path,
|
|
(GDBusInterfaceInfo *) &atspi_action_interface,
|
|
vtable,
|
|
self,
|
|
NULL,
|
|
NULL);
|
|
self->n_registered_objects++;
|
|
}
|
|
|
|
self->interfaces = g_variant_ref_sink (g_variant_builder_end (&interfaces));
|
|
|
|
GTK_NOTE (A11Y, g_message ("Registered %d interfaces on object path '%s'",
|
|
self->n_registered_objects,
|
|
self->context_path));
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_unregister_object (GtkAtSpiContext *self)
|
|
{
|
|
while (self->n_registered_objects > 0)
|
|
{
|
|
self->n_registered_objects--;
|
|
g_dbus_connection_unregister_object (self->connection,
|
|
self->registration_ids[self->n_registered_objects]);
|
|
self->registration_ids[self->n_registered_objects] = 0;
|
|
}
|
|
|
|
g_clear_pointer (&self->interfaces, g_variant_unref);
|
|
}
|
|
/* }}} */
|
|
/* {{{ GObject boilerplate */
|
|
static void
|
|
gtk_at_spi_context_finalize (GObject *gobject)
|
|
{
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (gobject);
|
|
|
|
gtk_at_spi_context_unregister_object (self);
|
|
|
|
g_clear_object (&self->root);
|
|
|
|
g_free (self->bus_address);
|
|
g_free (self->context_path);
|
|
|
|
G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->finalize (gobject);
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_set_property (GObject *gobject,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_BUS_ADDRESS:
|
|
self->bus_address = g_value_dup_string (value);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_get_property (GObject *gobject,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (gobject);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_BUS_ADDRESS:
|
|
g_value_set_string (value, self->bus_address);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_constructed (GObject *gobject)
|
|
{
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (gobject);
|
|
|
|
/* Make sure that we were properly constructed */
|
|
g_assert (self->bus_address);
|
|
|
|
G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->constructed (gobject);
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_realize (GtkATContext *context)
|
|
{
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context);
|
|
GdkDisplay *display = gtk_at_context_get_display (context);
|
|
|
|
/* Every GTK application has a single root AT-SPI object, which
|
|
* handles all the global state, including the cache of accessible
|
|
* objects. We use the GdkDisplay to store it, so it's guaranteed
|
|
* to be a unique per-display connection
|
|
*/
|
|
self->root =
|
|
g_object_get_data (G_OBJECT (display), "-gtk-atspi-root");
|
|
|
|
if (self->root == NULL)
|
|
{
|
|
self->root = gtk_at_spi_root_new (self->bus_address);
|
|
g_object_set_data_full (G_OBJECT (display), "-gtk-atspi-root",
|
|
g_object_ref (self->root),
|
|
g_object_unref);
|
|
}
|
|
else
|
|
{
|
|
g_object_ref (self->root);
|
|
}
|
|
|
|
/* UUIDs use '-' as the separator, but that's not a valid character
|
|
* for a DBus object path
|
|
*/
|
|
char *uuid = g_uuid_string_random ();
|
|
size_t len = strlen (uuid);
|
|
for (size_t i = 0; i < len; i++)
|
|
{
|
|
if (uuid[i] == '-')
|
|
uuid[i] = '_';
|
|
}
|
|
|
|
self->context_path =
|
|
g_strconcat (gtk_at_spi_root_get_base_path (self->root), "/", uuid, NULL);
|
|
|
|
g_free (uuid);
|
|
|
|
self->connection = gtk_at_spi_root_get_connection (self->root);
|
|
if (self->connection == NULL)
|
|
return;
|
|
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (context);
|
|
|
|
#ifdef G_ENABLE_DEBUG
|
|
if (GTK_DEBUG_CHECK (A11Y))
|
|
{
|
|
GtkAccessibleRole role = gtk_at_context_get_accessible_role (context);
|
|
char *role_name = g_enum_to_string (GTK_TYPE_ACCESSIBLE_ROLE, role);
|
|
g_message ("Realizing ATSPI context “%s” for accessible “%s”, with role: “%s”",
|
|
self->context_path,
|
|
G_OBJECT_TYPE_NAME (accessible),
|
|
role_name);
|
|
g_free (role_name);
|
|
}
|
|
#endif
|
|
|
|
gtk_atspi_connect_text_signals (accessible,
|
|
(GtkAtspiTextChangedCallback *)emit_text_changed,
|
|
(GtkAtspiTextSelectionCallback *)emit_text_selection_changed,
|
|
self);
|
|
gtk_atspi_connect_selection_signals (accessible,
|
|
(GtkAtspiSelectionCallback *)emit_selection_changed,
|
|
self);
|
|
gtk_at_spi_context_register_object (self);
|
|
|
|
gtk_at_spi_root_queue_register (self->root, self);
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_unrealize (GtkATContext *context)
|
|
{
|
|
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context);
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (context);
|
|
|
|
GTK_NOTE (A11Y, g_message ("Unrealizing ATSPI context at '%s' for accessible '%s'",
|
|
self->context_path,
|
|
G_OBJECT_TYPE_NAME (accessible)));
|
|
|
|
/* Notify ATs that the accessible object is going away */
|
|
emit_defunct (self);
|
|
gtk_at_spi_root_unregister (self->root, self);
|
|
|
|
gtk_atspi_disconnect_text_signals (accessible);
|
|
gtk_atspi_disconnect_selection_signals (accessible);
|
|
gtk_at_spi_context_unregister_object (self);
|
|
|
|
g_clear_object (&self->root);
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_class_init (GtkAtSpiContextClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GtkATContextClass *context_class = GTK_AT_CONTEXT_CLASS (klass);
|
|
|
|
gobject_class->constructed = gtk_at_spi_context_constructed;
|
|
gobject_class->set_property = gtk_at_spi_context_set_property;
|
|
gobject_class->get_property = gtk_at_spi_context_get_property;
|
|
gobject_class->finalize = gtk_at_spi_context_finalize;
|
|
|
|
context_class->realize = gtk_at_spi_context_realize;
|
|
context_class->unrealize = gtk_at_spi_context_unrealize;
|
|
context_class->state_change = gtk_at_spi_context_state_change;
|
|
context_class->platform_change = gtk_at_spi_context_platform_change;
|
|
context_class->bounds_change = gtk_at_spi_context_bounds_change;
|
|
context_class->child_change = gtk_at_spi_context_child_change;
|
|
|
|
obj_props[PROP_BUS_ADDRESS] =
|
|
g_param_spec_string ("bus-address", NULL, NULL,
|
|
NULL,
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (gobject_class, N_PROPS, obj_props);
|
|
}
|
|
|
|
static void
|
|
gtk_at_spi_context_init (GtkAtSpiContext *self)
|
|
{
|
|
}
|
|
/* }}} */
|
|
/* {{{ Bus address discovery */
|
|
#ifdef GDK_WINDOWING_X11
|
|
static char *
|
|
get_bus_address_x11 (GdkDisplay *display)
|
|
{
|
|
GTK_NOTE (A11Y, g_message ("Acquiring a11y bus via X11..."));
|
|
|
|
Display *xdisplay = gdk_x11_display_get_xdisplay (display);
|
|
Atom type_return;
|
|
int format_return;
|
|
gulong nitems_return;
|
|
gulong bytes_after_return;
|
|
guchar *data = NULL;
|
|
char *address = NULL;
|
|
|
|
gdk_x11_display_error_trap_push (display);
|
|
XGetWindowProperty (xdisplay, DefaultRootWindow (xdisplay),
|
|
gdk_x11_get_xatom_by_name_for_display (display, "AT_SPI_BUS"),
|
|
0L, BUFSIZ, False,
|
|
(Atom) 31,
|
|
&type_return, &format_return, &nitems_return,
|
|
&bytes_after_return, &data);
|
|
gdk_x11_display_error_trap_pop_ignored (display);
|
|
|
|
address = g_strdup ((char *) data);
|
|
|
|
XFree (data);
|
|
|
|
return address;
|
|
}
|
|
#endif
|
|
|
|
#if defined(GDK_WINDOWING_WAYLAND) || defined(GDK_WINDOWING_X11)
|
|
static char *
|
|
get_bus_address_dbus (GdkDisplay *display)
|
|
{
|
|
GTK_NOTE (A11Y, g_message ("Acquiring a11y bus via DBus..."));
|
|
|
|
GError *error = NULL;
|
|
GDBusConnection *connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
|
|
|
|
if (error != NULL)
|
|
{
|
|
g_critical ("Unable to acquire session bus: %s", error->message);
|
|
g_error_free (error);
|
|
return NULL;
|
|
}
|
|
|
|
GVariant *res =
|
|
g_dbus_connection_call_sync (connection, "org.a11y.Bus",
|
|
"/org/a11y/bus",
|
|
"org.a11y.Bus",
|
|
"GetAddress",
|
|
NULL, NULL,
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
&error);
|
|
if (error != NULL)
|
|
{
|
|
g_critical ("Unable to acquire the address of the accessibility bus: %s",
|
|
error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
char *address = NULL;
|
|
if (res != NULL)
|
|
{
|
|
g_variant_get (res, "(s)", &address);
|
|
g_variant_unref (res);
|
|
}
|
|
|
|
g_object_unref (connection);
|
|
|
|
return address;
|
|
}
|
|
#endif
|
|
|
|
static const char *
|
|
get_bus_address (GdkDisplay *display)
|
|
{
|
|
const char *bus_address;
|
|
|
|
bus_address = g_object_get_data (G_OBJECT (display), "-gtk-atspi-bus-address");
|
|
if (bus_address != NULL)
|
|
return bus_address;
|
|
|
|
/* The bus address environment variable takes precedence; this is the
|
|
* mechanism used by Flatpak to handle the accessibility bus portal
|
|
* between the sandbox and the outside world
|
|
*/
|
|
bus_address = g_getenv ("AT_SPI_BUS_ADDRESS");
|
|
if (bus_address != NULL && *bus_address != '\0')
|
|
{
|
|
GTK_NOTE (A11Y, g_message ("Using ATSPI bus address from environment: %s", bus_address));
|
|
g_object_set_data_full (G_OBJECT (display), "-gtk-atspi-bus-address",
|
|
g_strdup (bus_address),
|
|
g_free);
|
|
goto out;
|
|
}
|
|
|
|
#if defined(GDK_WINDOWING_WAYLAND)
|
|
if (bus_address == NULL)
|
|
{
|
|
if (GDK_IS_WAYLAND_DISPLAY (display))
|
|
{
|
|
char *addr = get_bus_address_dbus (display);
|
|
|
|
GTK_NOTE (A11Y, g_message ("Using ATSPI bus address from D-Bus: %s", addr));
|
|
g_object_set_data_full (G_OBJECT (display), "-gtk-atspi-bus-address",
|
|
addr,
|
|
g_free);
|
|
|
|
bus_address = addr;
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(GDK_WINDOWING_X11)
|
|
if (bus_address == NULL)
|
|
{
|
|
if (GDK_IS_X11_DISPLAY (display))
|
|
{
|
|
char *addr = get_bus_address_dbus (display);
|
|
|
|
if (addr == NULL)
|
|
{
|
|
addr = get_bus_address_x11 (display);
|
|
GTK_NOTE (A11Y, g_message ("Using ATSPI bus address from X11: %s", addr));
|
|
}
|
|
else
|
|
{
|
|
GTK_NOTE (A11Y, g_message ("Using ATSPI bus address from D-Bus: %s", addr));
|
|
}
|
|
|
|
g_object_set_data_full (G_OBJECT (display), "-gtk-atspi-bus-address",
|
|
addr,
|
|
g_free);
|
|
|
|
bus_address = addr;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
out:
|
|
return bus_address;
|
|
}
|
|
/* }}} */
|
|
/* {{{ API */
|
|
GtkATContext *
|
|
gtk_at_spi_create_context (GtkAccessibleRole accessible_role,
|
|
GtkAccessible *accessible,
|
|
GdkDisplay *display)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_ACCESSIBLE (accessible), NULL);
|
|
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
|
|
|
|
const char *bus_address = get_bus_address (display);
|
|
|
|
if (bus_address == NULL)
|
|
return NULL;
|
|
|
|
#if defined(GDK_WINDOWING_WAYLAND)
|
|
if (GDK_IS_WAYLAND_DISPLAY (display))
|
|
return g_object_new (GTK_TYPE_AT_SPI_CONTEXT,
|
|
"accessible-role", accessible_role,
|
|
"accessible", accessible,
|
|
"display", display,
|
|
"bus-address", bus_address,
|
|
NULL);
|
|
#endif
|
|
#if defined(GDK_WINDOWING_X11)
|
|
if (GDK_IS_X11_DISPLAY (display))
|
|
return g_object_new (GTK_TYPE_AT_SPI_CONTEXT,
|
|
"accessible-role", accessible_role,
|
|
"accessible", accessible,
|
|
"display", display,
|
|
"bus-address", bus_address,
|
|
NULL);
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const char *
|
|
gtk_at_spi_context_get_context_path (GtkAtSpiContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), NULL);
|
|
|
|
return self->context_path;
|
|
}
|
|
|
|
/*< private >
|
|
* gtk_at_spi_context_to_ref:
|
|
* @self: a #GtkAtSpiContext
|
|
*
|
|
* Returns an ATSPI object reference for the #GtkAtSpiContext.
|
|
*
|
|
* Returns: (transfer floating): a #GVariant with the reference
|
|
*/
|
|
GVariant *
|
|
gtk_at_spi_context_to_ref (GtkAtSpiContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), NULL);
|
|
|
|
if (self->context_path == NULL)
|
|
return gtk_at_spi_null_ref ();
|
|
|
|
const char *name = g_dbus_connection_get_unique_name (self->connection);
|
|
|
|
return g_variant_new ("(so)", name, self->context_path);
|
|
}
|
|
|
|
GVariant *
|
|
gtk_at_spi_context_get_interfaces (GtkAtSpiContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), NULL);
|
|
|
|
return self->interfaces;
|
|
}
|
|
|
|
GVariant *
|
|
gtk_at_spi_context_get_states (GtkAtSpiContext *self)
|
|
{
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("au"));
|
|
|
|
collect_states (self, &builder);
|
|
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
GVariant *
|
|
gtk_at_spi_context_get_parent_ref (GtkAtSpiContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), NULL);
|
|
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
|
|
|
|
return get_parent_context_ref (accessible);
|
|
}
|
|
|
|
GtkAtSpiRoot *
|
|
gtk_at_spi_context_get_root (GtkAtSpiContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), NULL);
|
|
|
|
return self->root;
|
|
}
|
|
|
|
int
|
|
gtk_at_spi_context_get_index_in_parent (GtkAtSpiContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), -1);
|
|
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
|
|
int idx;
|
|
|
|
if (GTK_IS_ROOT (accessible))
|
|
idx = get_index_in_toplevels (GTK_WIDGET (accessible));
|
|
else if (GTK_IS_STACK_PAGE (accessible))
|
|
idx = get_index_in_parent (gtk_stack_page_get_child (GTK_STACK_PAGE (accessible)));
|
|
else if (GTK_IS_STACK (gtk_widget_get_parent (GTK_WIDGET (accessible))))
|
|
idx = 1;
|
|
else
|
|
idx = get_index_in_parent (GTK_WIDGET (accessible));
|
|
|
|
return idx;
|
|
}
|
|
|
|
int
|
|
gtk_at_spi_context_get_child_count (GtkAtSpiContext *self)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_AT_SPI_CONTEXT (self), -1);
|
|
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
|
|
int n_children = -1;
|
|
|
|
if (GTK_IS_WIDGET (accessible))
|
|
{
|
|
GtkWidget *child;
|
|
|
|
n_children = 0;
|
|
for (child = gtk_widget_get_first_child (GTK_WIDGET (accessible));
|
|
child;
|
|
child = gtk_widget_get_next_sibling (child))
|
|
{
|
|
if (!gtk_accessible_should_present (GTK_ACCESSIBLE (child)))
|
|
continue;
|
|
|
|
n_children++;
|
|
}
|
|
}
|
|
else if (GTK_IS_STACK_PAGE (accessible))
|
|
{
|
|
n_children = 1;
|
|
}
|
|
|
|
return n_children;
|
|
}
|
|
/* }}} */
|
|
|
|
/* vim:set foldmethod=marker expandtab: */
|