From 374bd21340b1de7a75c5c0625c5c05520e91ac38 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Wed, 14 Oct 2020 15:10:03 +0100 Subject: Initial ATSPI Action implementation for widget Use the actions from the GtkActionMuxer of each widget to populate the list of actions available. --- gtk/a11y/gtkatspiaction.c | 249 +++++++++++++++++++++++++++++++++++++++ gtk/a11y/gtkatspiactionprivate.h | 30 +++++ gtk/a11y/gtkatspicontext.c | 30 ++++- gtk/a11y/meson.build | 9 +- 4 files changed, 308 insertions(+), 10 deletions(-) create mode 100644 gtk/a11y/gtkatspiaction.c create mode 100644 gtk/a11y/gtkatspiactionprivate.h diff --git a/gtk/a11y/gtkatspiaction.c b/gtk/a11y/gtkatspiaction.c new file mode 100644 index 0000000000..457234efd3 --- /dev/null +++ b/gtk/a11y/gtkatspiaction.c @@ -0,0 +1,249 @@ +/* gtkatspiaction.c: ATSPI Action 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 . + */ + +#include "config.h" + +#include "gtkatspiactionprivate.h" + +#include "gtkatspicontextprivate.h" +#include "gtkatcontextprivate.h" + +#include "a11y/atspi/atspi-action.h" + +#include "gtkactionable.h" +#include "gtkactionmuxerprivate.h" +#include "gtkwidgetprivate.h" + +static gboolean +is_valid_action (GtkActionMuxer *muxer, + const char *action_name) +{ + const GVariantType *param_type = NULL; + gboolean enabled = FALSE; + + /* Skip disabled or parametrized actions */ + if (!gtk_action_muxer_query_action (muxer, action_name, + &enabled, + ¶m_type, NULL, + NULL, NULL)) + return FALSE; + + if (!enabled || param_type != NULL) + return FALSE; + + return TRUE; +} + +static void +add_muxer_actions (GtkActionMuxer *muxer, + char **actions, + int n_actions, + GVariantBuilder *builder) +{ + for (int i = 0; i < n_actions; i++) + { + if (!is_valid_action (muxer, actions[i])) + continue; + + g_variant_builder_add (builder, "(sss)", + actions[i], + actions[i], + ""); + } +} + +static const char * +get_action_at_index (GtkActionMuxer *muxer, + char **actions, + int n_actions, + int pos) +{ + int real_pos = 0; + + for (int i = 0; i < n_actions; i++) + { + if (!is_valid_action (muxer, actions[i])) + continue; + + if (real_pos == pos) + break; + + real_pos += 1; + } + + return actions[real_pos]; +} + +static int +get_valid_actions (GtkActionMuxer *muxer, + char **actions, + int n_actions) +{ + int n_enabled_actions = 0; + + for (int i = 0; i < n_actions; i++) + { + if (!is_valid_action (muxer, actions[i])) + continue; + + n_enabled_actions += 1; + } + + return n_enabled_actions; +} + +static void +widget_handle_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; + GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self)); + GtkWidget *widget = GTK_WIDGET (accessible); + GtkActionMuxer *muxer = _gtk_widget_get_action_muxer (widget, FALSE); + + if (muxer == NULL) + return; + + char **actions = gtk_action_muxer_list_actions (muxer, TRUE); + int n_actions = actions != NULL ? g_strv_length (actions) : 0; + + /* XXX: We need more fields in the action API */ + if (g_strcmp0 (method_name, "GetName") == 0 || + g_strcmp0 (method_name, "GetLocalizedName") == 0 || + g_strcmp0 (method_name, "GetDescription") == 0) + { + int action_idx; + + g_variant_get (parameters, "(i)", &action_idx); + + const char *action = get_action_at_index (muxer, actions, n_actions, action_idx); + + if (action != NULL && gtk_widget_is_sensitive (widget)) + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", action)); + else + g_dbus_method_invocation_return_error (invocation, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "No action with index %d", + action_idx); + } + else if (g_strcmp0 (method_name, "DoAction") == 0) + { + int action_idx; + + g_variant_get (parameters, "(i)", &action_idx); + + const char *action = get_action_at_index (muxer, actions, n_actions, action_idx); + + if (action != NULL && gtk_widget_is_sensitive (widget)) + { + gboolean res = gtk_widget_activate_action_variant (widget, action, NULL); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", res)); + } + else + { + g_dbus_method_invocation_return_error (invocation, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "No action with index %d", + action_idx); + } + } + else if (g_strcmp0 (method_name, "GetKeyBinding") == 0) + { + int action_idx; + + g_variant_get (parameters, "(i)", &action_idx); + + const char *action = get_action_at_index (muxer, actions, n_actions, action_idx); + + if (action != NULL && gtk_widget_is_sensitive (widget)) + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "")); + else + g_dbus_method_invocation_return_error (invocation, + G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, + "No action with index %d", + action_idx); + } + else if (g_strcmp0 (method_name, "GetActions") == 0) + { + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(sss)")); + + if (n_actions >= 0 && gtk_widget_is_sensitive (widget)) + add_muxer_actions (muxer, actions, n_actions, &builder); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a(sss))", &builder)); + } + + g_strfreev (actions); +} + +static GVariant * +widget_handle_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; + GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self)); + GtkWidget *widget = GTK_WIDGET (accessible); + GtkActionMuxer *muxer = _gtk_widget_get_action_muxer (widget, FALSE); + GVariant *res = NULL; + + if (muxer == NULL) + return res; + + char **actions = gtk_action_muxer_list_actions (muxer, TRUE); + int n_actions = actions != NULL ? g_strv_length (actions) : 0; + + if (g_strcmp0 (property_name, "NActions") == 0) + res = g_variant_new ("i", get_valid_actions (muxer, actions, n_actions)); + else + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "Unknown property '%s'", property_name); + + return res; +} + +static const GDBusInterfaceVTable widget_action_vtable = { + widget_handle_method, + widget_handle_get_property, + NULL, +}; + +const GDBusInterfaceVTable * +gtk_atspi_get_action_vtable (GtkAccessible *accessible) +{ + if (GTK_IS_WIDGET (accessible)) + return &widget_action_vtable; + + return NULL; +} diff --git a/gtk/a11y/gtkatspiactionprivate.h b/gtk/a11y/gtkatspiactionprivate.h new file mode 100644 index 0000000000..1f4e3a67e8 --- /dev/null +++ b/gtk/a11y/gtkatspiactionprivate.h @@ -0,0 +1,30 @@ +/* gtkatspiactionprivate.h: ATSPI Action 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 . + */ + +#pragma once + +#include +#include "gtkaccessible.h" + +G_BEGIN_DECLS + +const GDBusInterfaceVTable *gtk_atspi_get_action_vtable (GtkAccessible *accessible); + +G_END_DECLS diff --git a/gtk/a11y/gtkatspicontext.c b/gtk/a11y/gtkatspicontext.c index 1128165b4e..4c3746ba24 100644 --- a/gtk/a11y/gtkatspicontext.c +++ b/gtk/a11y/gtkatspicontext.c @@ -24,18 +24,20 @@ #include "gtkaccessibleprivate.h" +#include "gtkatspiactionprivate.h" #include "gtkatspicacheprivate.h" -#include "gtkatspirootprivate.h" +#include "gtkatspieditabletextprivate.h" #include "gtkatspiprivate.h" -#include "gtkatspiutilsprivate.h" +#include "gtkatspirootprivate.h" +#include "gtkatspiselectionprivate.h" #include "gtkatspitextprivate.h" -#include "gtkatspieditabletextprivate.h" +#include "gtkatspiutilsprivate.h" #include "gtkatspivalueprivate.h" -#include "gtkatspiselectionprivate.h" #include "a11y/atspi/atspi-accessible.h" -#include "a11y/atspi/atspi-text.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" @@ -331,6 +333,7 @@ collect_relations (GtkAtSpiContext *self, } } /* }}} */ + /* {{{ Accessible implementation */ static int get_index_in_parent (GtkWidget *widget) @@ -696,8 +699,8 @@ static const GDBusInterfaceVTable accessible_vtable = { handle_accessible_get_property, NULL, }; - /* }}} */ + /* {{{ Change notification */ static void emit_text_changed (GtkAtSpiContext *self, @@ -1031,6 +1034,21 @@ gtk_at_spi_context_register_object (GtkAtSpiContext *self) 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)); } diff --git a/gtk/a11y/meson.build b/gtk/a11y/meson.build index 49359159ae..5cb0cc88f2 100644 --- a/gtk/a11y/meson.build +++ b/gtk/a11y/meson.build @@ -9,15 +9,16 @@ if gtk_a11y_backends.contains('atspi') subdir('atspi') gtk_a11y_src += files([ + 'gtkatspiaction.c', 'gtkatspicache.c', 'gtkatspicontext.c', - 'gtkatspiroot.c', - 'gtkatspiutils.c', + 'gtkatspieditabletext.c', 'gtkatspipango.c', + 'gtkatspiroot.c', + 'gtkatspiselection.c', 'gtkatspitextbuffer.c', 'gtkatspitext.c', + 'gtkatspiutils.c', 'gtkatspivalue.c', - 'gtkatspieditabletext.c', - 'gtkatspiselection.c', ]) endif -- cgit v1.2.1