diff --git a/gtk/a11y/gtkatspicontext.c b/gtk/a11y/gtkatspicontext.c index d932f06c2c..c3fc6960bf 100644 --- a/gtk/a11y/gtkatspicontext.c +++ b/gtk/a11y/gtkatspicontext.c @@ -26,6 +26,7 @@ #include "gtkatspirootprivate.h" #include "gtkatspiprivate.h" #include "gtkatspiutilsprivate.h" +#include "gtkatspipangoprivate.h" #include "a11y/atspi/atspi-accessible.h" #include "a11y/atspi/atspi-text.h" @@ -33,6 +34,7 @@ #include "gtkdebug.h" #include "gtkwindow.h" #include "gtklabel.h" +#include "gtklabelprivate.h" #include "gtkroot.h" #include @@ -421,6 +423,8 @@ handle_accessible_method (GDBusConnection *connection, GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("as")); g_variant_builder_add (&builder, "s", "org.a11y.atspi.Accessible"); + if (GTK_IS_LABEL (accessible)) + g_variant_builder_add (&builder, "s", "org.a11y.atspi.Text"); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(as)", &builder)); } @@ -505,6 +509,336 @@ static const GDBusInterfaceVTable accessible_vtable = { NULL, }; +static void +handle_text_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); + + if (g_strcmp0 (method_name, "GetCaretOffset") == 0) + { + int offset; + + offset = _gtk_label_get_cursor_position (GTK_LABEL (widget)); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", offset)); + } + else if (g_strcmp0 (method_name, "SetCaretOffset") == 0) + { + int offset; + + g_variant_get (parameters, "(i)", &offset); + + if (gtk_label_get_selectable (GTK_LABEL (widget))) + gtk_label_select_region (GTK_LABEL (widget), offset, offset); + + g_dbus_method_invocation_return_value (invocation, NULL); + } + else if (g_strcmp0 (method_name, "GetText") == 0) + { + int start, end; + const char *text; + int len; + char *string; + + g_variant_get (parameters, "(ii)", &start, &end); + + text = gtk_label_get_text (GTK_LABEL (widget)); + len = g_utf8_strlen (text, -1); + + start = CLAMP (start, 0, len); + end = CLAMP (end, 0, len); + + if (end <= start) + string = g_strdup (""); + else + { + const char *p, *q; + p = g_utf8_offset_to_pointer (text, start); + q = g_utf8_offset_to_pointer (text, end); + string = g_strndup (p, q - p); + } + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", string)); + g_free (string); + } + else if (g_strcmp0 (method_name, "GetTextBeforeOffset") == 0) + { + PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget)); + int offset; + AtspiTextBoundaryType boundary_type; + char *string; + int start, end; + + g_variant_get (parameters, "(iu)", &offset, &boundary_type); + string = gtk_pango_get_text_before (layout, offset, boundary_type, &start, &end); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end)); + g_free (string); + } + else if (g_strcmp0 (method_name, "GetTextAtOffset") == 0) + { + PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget)); + int offset; + AtspiTextBoundaryType boundary_type; + char *string; + int start, end; + + g_variant_get (parameters, "(iu)", &offset, &boundary_type); + string = gtk_pango_get_text_at (layout, offset, boundary_type, &start, &end); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end)); + g_free (string); + } + else if (g_strcmp0 (method_name, "GetTextAfterOffset") == 0) + { + PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget)); + int offset; + AtspiTextBoundaryType boundary_type; + char *string; + int start, end; + + g_variant_get (parameters, "(iu)", &offset, &boundary_type); + string = gtk_pango_get_text_after (layout, offset, boundary_type, &start, &end); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end)); + g_free (string); + } + else if (g_strcmp0 (method_name, "GetCharacterAtOffset") == 0) + { + int offset; + const char *text; + gunichar ch = 0; + + g_variant_get (parameters, "(i)", &offset); + + text = gtk_label_get_text (GTK_LABEL (widget)); + + if (0 <= offset && offset < g_utf8_strlen (text, -1)) + ch = g_utf8_get_char (g_utf8_offset_to_pointer (text, offset)); + + g_dbus_method_invocation_return_value (invocation, g_variant_new_int32 (ch)); + } + else if (g_strcmp0 (method_name, "GetStringAtOffset") == 0) + { + PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget)); + int offset; + AtspiTextGranularity granularity; + char *string; + int start, end; + + g_variant_get (parameters, "(iu)", &offset, &granularity); + + string = gtk_pango_get_string_at (layout, offset, granularity, &start, &end); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end)); + g_free (string); + } + else if (g_strcmp0 (method_name, "GetAttributes") == 0) + { + 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; + + g_variant_get (parameters, "(i)", &offset); + + gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end)); + } + else if (g_strcmp0 (method_name, "GetAttributeValue") == 0) + { + PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget)); + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); + int offset; + const char *name; + int start, end; + GVariant *attrs; + const char *val; + + g_variant_get (parameters, "(i&s)", &offset, &name); + gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end); + attrs = g_variant_builder_end (&builder); + if (!g_variant_lookup (attrs, name, "&s", &val)) + val = ""; + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", val)); + g_variant_unref (attrs); + } + else if (g_strcmp0 (method_name, "GetAttributeRun") == 0) + { + PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget)); + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); + int offset; + gboolean include_defaults; + int start, end; + + g_variant_get (parameters, "(ib)", &offset, &include_defaults); + + if (include_defaults) + gtk_pango_get_default_attributes (layout, &builder); + + gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end)); + } + else if (g_strcmp0 (method_name, "GetDefaultAttributes") == 0 || + g_strcmp0 (method_name, "GetDefaultAttributeSet") == 0) + { + PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget)); + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); + + gtk_pango_get_default_attributes (layout, &builder); + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss})", &builder)); + } + else if (g_strcmp0 (method_name, "GetNSelections") == 0) + { + int n = 0; + + if (gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL)) + n = 1; + + g_dbus_method_invocation_return_value (invocation, g_variant_new_int32 (n)); + } + else if (g_strcmp0 (method_name, "GetSelection") == 0) + { + int num; + int start, end; + + g_variant_get (parameters, "(i)", &num); + + if (num != 0 || + !gtk_label_get_selection_bounds (GTK_LABEL (widget), &start, &end)) + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Not a valid selection: %d", num); + else + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(ii)", start, end)); + } + else if (g_strcmp0 (method_name, "AddSelection") == 0) + { + int start, end; + gboolean ret; + + g_variant_get (parameters, "(ii)", &start, &end); + + if (!gtk_label_get_selectable (GTK_LABEL (widget)) || + gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL)) + { + ret = FALSE; + } + else + { + gtk_label_select_region (GTK_LABEL (widget), start, end); + ret = TRUE; + } + + g_dbus_method_invocation_return_value (invocation, g_variant_new_boolean (ret)); + } + else if (g_strcmp0 (method_name, "RemoveSelection") == 0) + { + int num; + int start, end; + gboolean ret; + + g_variant_get (parameters, "(i)", &num); + + if (num != 0 || + !gtk_label_get_selectable (GTK_LABEL (widget)) || + !gtk_label_get_selection_bounds (GTK_LABEL (widget), &start, &end)) + { + ret = FALSE; + } + else + { + gtk_label_select_region (GTK_LABEL (widget), end, end); + ret = TRUE; + } + + g_dbus_method_invocation_return_value (invocation, g_variant_new_boolean (ret)); + } + else if (g_strcmp0 (method_name, "SetSelection") == 0) + { + int num; + int start, end; + gboolean ret; + + g_variant_get (parameters, "(iii)", &num, &start, &end); + + if (num != 0 || + !gtk_label_get_selectable (GTK_LABEL (widget)) || + !gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL)) + { + ret = FALSE; + } + else + { + gtk_label_select_region (GTK_LABEL (widget), start, end); + ret = TRUE; + } + + g_dbus_method_invocation_return_value (invocation, g_variant_new_boolean (ret)); + } + 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 * +handle_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) +{ + GtkAtSpiContext *self = user_data; + GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self)); + GtkWidget *widget = GTK_WIDGET (accessible); + + if (g_strcmp0 (property_name, "CharacterCount") == 0) + { + const char *text; + int len; + + text = gtk_label_get_text (GTK_LABEL (widget)); + len = g_utf8_strlen (text, -1); + + return g_variant_new_int32 (len); + } + else if (g_strcmp0 (property_name, "CaretOffset") == 0) + { + return g_variant_new_int32 (0); + } + + return NULL; +} + +static const GDBusInterfaceVTable text_vtable = { + handle_text_method, + handle_text_get_property, + NULL, +}; + static void gtk_at_spi_context_register_object (GtkAtSpiContext *self) { @@ -515,6 +849,17 @@ gtk_at_spi_context_register_object (GtkAtSpiContext *self) self, NULL, NULL); + + if (GTK_IS_LABEL (gtk_at_context_get_accessible (GTK_AT_CONTEXT (self)))) + { + g_dbus_connection_register_object (self->connection, + self->context_path, + (GDBusInterfaceInfo *) &atspi_text_interface, + &text_vtable, + self, + NULL, + NULL); + } } static void