From 10c782b106e4d49ab9ef3fd97227dd03b731512e Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Thu, 1 Feb 2024 12:56:04 +0100 Subject: [PATCH] a11y: Translate GtkAccessibleText to AT-SPI Handle GtkAccessibleText in our translation layer, as far as possible. --- gtk/a11y/gtkatspitext.c | 306 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 293 insertions(+), 13 deletions(-) diff --git a/gtk/a11y/gtkatspitext.c b/gtk/a11y/gtkatspitext.c index 273bdb66f0..2763f8c955 100644 --- a/gtk/a11y/gtkatspitext.c +++ b/gtk/a11y/gtkatspitext.c @@ -41,6 +41,7 @@ #include "gtkspinbuttonprivate.h" #include "gtktextbufferprivate.h" #include "gtktextviewprivate.h" +#include "gtkaccessibletext-private.h" #include @@ -64,6 +65,283 @@ atspi_granularity_to_gtk (AtspiTextGranularity granularity) } } +/* {{{ GtkAccessibleText */ + +static void +accessible_text_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) +{ + GtkATContext *self = user_data; + GtkAccessible *accessible = gtk_at_context_get_accessible (self); + GtkAccessibleText *accessible_text = GTK_ACCESSIBLE_TEXT (accessible); + + if (g_strcmp0 (method_name, "GetCaretOffset") == 0) + { + guint offset; + + offset = gtk_accessible_text_get_caret_position (accessible_text); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", (int)offset)); + } + else if (g_strcmp0 (method_name, "SetCaretOffset") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "GetText") == 0) + { + int start, end; + GBytes *contents; + + g_variant_get (parameters, "(ii)", &start, &end); + + contents = gtk_accessible_text_get_contents (accessible_text, start, end < 0 ? G_MAXUINT : end); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", g_bytes_get_data (contents, NULL))); + + g_bytes_unref (contents); + } + else if (g_strcmp0 (method_name, "GetTextBeforeOffset") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "This method is deprecated in favor of GetStringAtOffset"); + } + else if (g_strcmp0 (method_name, "GetTextAtOffset") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "This method is deprecated in favor of GetStringAtOffset"); + } + else if (g_strcmp0 (method_name, "GetTextAfterOffset") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "This method is deprecated in favor of GetStringAtOffset"); + } + else if (g_strcmp0 (method_name, "GetCharacterAtOffset") == 0) + { + int offset; + gunichar ch = 0; + + g_variant_get (parameters, "(i)", &offset); + + GBytes *text = gtk_accessible_text_get_contents (accessible_text, offset, offset + 1); + + if (text != NULL) + { + const char *str = g_bytes_get_data (text, NULL); + + if (0 <= offset && offset < g_utf8_strlen (str, -1)) + ch = g_utf8_get_char (g_utf8_offset_to_pointer (str, offset)); + } + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", ch)); + } + else if (g_strcmp0 (method_name, "GetStringAtOffset") == 0) + { + unsigned int start, end; + int offset; + AtspiTextGranularity granularity; + GBytes *bytes; + + g_variant_get (parameters, "(iu)", &offset, &granularity); + + bytes = gtk_accessible_text_get_contents_at (accessible_text, offset, + atspi_granularity_to_gtk (granularity), + &start, &end); + + if (bytes == NULL) + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", "", -1, -1)); + else + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", g_bytes_get_data (bytes, NULL), start, end)); + + g_bytes_unref (bytes); + } + else if (g_strcmp0 (method_name, "GetAttributes") == 0) + { + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); + int offset; + gsize n_attrs = 0; + GtkAccessibleTextRange *ranges = NULL; + int start, end; + char **attr_names = NULL; + char **attr_values = NULL; + + g_variant_get (parameters, "(i)", &offset); + + gtk_accessible_text_get_attributes (accessible_text, + offset, + &n_attrs, + &ranges, + &attr_names, + &attr_values); + + start = 0; + end = G_MAXINT; + + for (int i = 0; i < n_attrs; i++) + { + g_variant_builder_add (&builder, "{ss}", attr_names[i], attr_values[i]); + start = MAX (start, ranges[i].start); + end = MIN (end, start + ranges[i].length); + } + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end)); + + g_clear_pointer (&ranges, g_free); + g_strfreev (attr_names); + g_strfreev (attr_values); + } + else if (g_strcmp0 (method_name, "GetAttributeValue") == 0) + { + int offset; + const char *name; + const char *val = ""; + char **names, **values; + GtkAccessibleTextRange *ranges; + gsize n_ranges; + + g_variant_get (parameters, "(i&s)", &offset, &name); + + gtk_accessible_text_get_attributes (accessible_text, offset, + &n_ranges, &ranges, + &names, &values); + + for (unsigned i = 0; names[i] != NULL; i++) + { + if (g_strcmp0 (names[i], name) == 0) + { + val = values[i]; + break; + } + } + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", val)); + + g_strfreev (names); + g_strfreev (values); + } + else if (g_strcmp0 (method_name, "GetAttributeRun") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "GetDefaultAttributes") == 0 || + g_strcmp0 (method_name, "GetDefaultAttributeSet") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "GetNSelections") == 0) + { + gsize n_ranges; + GtkAccessibleTextRange *ranges = NULL; + + gtk_accessible_text_get_selection (accessible_text, &n_ranges, &ranges); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", (int)n_ranges)); + + g_clear_pointer (&ranges, g_free); + } + else if (g_strcmp0 (method_name, "GetSelection") == 0) + { + int num; + gsize n_ranges; + GtkAccessibleTextRange *ranges = NULL; + + g_variant_get (parameters, "(i)", &num); + + gtk_accessible_text_get_selection (accessible_text, &n_ranges, &ranges); + + if (num < 0 || num >= n_ranges) + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Not a valid selection: %d", num); + else + { + int start = ranges[num].start; + int end = start + ranges[num].length; + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(ii)", start, end)); + } + + g_clear_pointer (&ranges, g_free); + } + else if (g_strcmp0 (method_name, "AddSelection") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "RemoveSelection") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "SetSelection") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "GetCharacterExtents") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "GetRangeExtents") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "GetBoundedRanges") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "ScrollSubstringTo") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "ScrollSubstringToPoint") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } +} + +static GVariant * +accessible_text_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + GtkATContext *self = user_data; + GtkAccessible *accessible = gtk_at_context_get_accessible (self); + GtkAccessibleText *accessible_text = GTK_ACCESSIBLE_TEXT (accessible); + + if (g_strcmp0 (property_name, "CharacterCount") == 0) + { + GBytes *contents; + const char *str; + gsize len; + + contents = gtk_accessible_text_get_contents (accessible_text, 0, G_MAXUINT); + str = g_bytes_get_data (contents, NULL); + len = g_utf8_strlen (str, -1); + g_bytes_unref (contents); + + return g_variant_new_int32 ((int) len); + } + else if (g_strcmp0 (property_name, "CaretOffset") == 0) + { + guint offset; + + offset = gtk_accessible_text_get_caret_position (accessible_text); + + return g_variant_new_int32 ((int) offset); + } + + return NULL; +} + +static const GDBusInterfaceVTable accessible_text_vtable = { + accessible_text_handle_method, + accessible_text_get_property, + NULL, +}; + +/* }}} */ /* {{{ GtkLabel */ static void @@ -195,7 +473,7 @@ label_handle_method (GDBusConnection *connection, int offset; AtspiTextGranularity granularity; char *string; - int start, end; + unsigned int start, end; g_variant_get (parameters, "(iu)", &offset, &granularity); @@ -211,7 +489,7 @@ label_handle_method (GDBusConnection *connection, PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget)); GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); int offset; - int start, end; + unsigned int start, end; char **names, **values; g_variant_get (parameters, "(i)", &offset); @@ -231,7 +509,7 @@ label_handle_method (GDBusConnection *connection, PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget)); int offset; const char *name; - int start, end; + unsigned int start, end; const char *val = ""; char **names, **values; @@ -259,7 +537,7 @@ label_handle_method (GDBusConnection *connection, GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); int offset; gboolean include_defaults; - int start, end; + unsigned int start, end; char **names, **values; g_variant_get (parameters, "(ib)", &offset, &include_defaults); @@ -585,7 +863,7 @@ inscription_handle_method (GDBusConnection *connection, int offset; AtspiTextGranularity granularity; char *string; - int start, end; + unsigned int start, end; g_variant_get (parameters, "(iu)", &offset, &granularity); @@ -601,7 +879,7 @@ inscription_handle_method (GDBusConnection *connection, PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (widget));; GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); int offset; - int start, end; + unsigned int start, end; char **names, **values; g_variant_get (parameters, "(i)", &offset); @@ -621,7 +899,7 @@ inscription_handle_method (GDBusConnection *connection, PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (widget));; int offset; const char *name; - int start, end; + unsigned int start, end; const char *val = ""; char **names, **values; @@ -649,7 +927,7 @@ inscription_handle_method (GDBusConnection *connection, GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); int offset; gboolean include_defaults; - int start, end; + unsigned int start, end; char **names, **values; g_variant_get (parameters, "(ib)", &offset, &include_defaults); @@ -921,7 +1199,7 @@ editable_handle_method (GDBusConnection *connection, int offset; AtspiTextGranularity granularity; char *string; - int start, end; + unsigned int start, end; g_variant_get (parameters, "(iu)", &offset, &granularity); @@ -937,7 +1215,7 @@ editable_handle_method (GDBusConnection *connection, PangoLayout *layout = gtk_text_get_layout (text_widget); GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); int offset; - int start, end; + unsigned int start, end; char **names, **values; g_variant_get (parameters, "(i)", &offset); @@ -957,7 +1235,7 @@ editable_handle_method (GDBusConnection *connection, PangoLayout *layout = gtk_text_get_layout (text_widget); int offset; const char *name; - int start, end; + unsigned int start, end; const char *val = ""; char **names, **values; @@ -985,7 +1263,7 @@ editable_handle_method (GDBusConnection *connection, GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); int offset; gboolean include_defaults; - int start, end; + unsigned int start, end; char **names, **values; g_variant_get (parameters, "(ib)", &offset, &include_defaults); @@ -1707,7 +1985,9 @@ static const GDBusInterfaceVTable text_view_vtable = { const GDBusInterfaceVTable * gtk_atspi_get_text_vtable (GtkAccessible *accessible) { - if (GTK_IS_LABEL (accessible)) + if (GTK_IS_ACCESSIBLE_TEXT (accessible)) + return &accessible_text_vtable; + else if (GTK_IS_LABEL (accessible)) return &label_vtable; else if (GTK_IS_INSCRIPTION (accessible)) return &inscription_vtable;