diff --git a/gtk/a11y/gtkatspitextbuffer.c b/gtk/a11y/gtkatspitextbuffer.c new file mode 100644 index 0000000000..73380997bc --- /dev/null +++ b/gtk/a11y/gtkatspitextbuffer.c @@ -0,0 +1,984 @@ +/* gtkatspitextbuffer.c - GtkTextBuffer-related utilities for AT-SPI + * + * Copyright (c) 2020 Red Hat, Inc. + * + * 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 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 .Free + */ + +#include "config.h" +#include "gtkatspitextbufferprivate.h" +#include "gtkatspipangoprivate.h" +#include "gtktextviewprivate.h" + +static const char * +gtk_justification_to_string (GtkJustification just) +{ + switch (just) + { + case GTK_JUSTIFY_LEFT: + return "left"; + case GTK_JUSTIFY_RIGHT: + return "right"; + case GTK_JUSTIFY_CENTER: + return "center"; + case GTK_JUSTIFY_FILL: + return "fill"; + default: + g_assert_not_reached (); + } +} + +static const char * +gtk_text_direction_to_string (GtkTextDirection direction) +{ + switch (direction) + { + case GTK_TEXT_DIR_NONE: + return "none"; + case GTK_TEXT_DIR_LTR: + return "ltr"; + case GTK_TEXT_DIR_RTL: + return "rtl"; + default: + g_assert_not_reached (); + } +} + +void +gtk_text_view_add_default_attributes (GtkTextView *view, + GVariantBuilder *builder) +{ + GtkTextAttributes *text_attrs; + PangoFontDescription *font; + char *value; + + text_attrs = gtk_text_view_get_default_attributes (view); + + font = text_attrs->font; + + if (font) + gtk_pango_get_font_attributes (font, builder); + + g_variant_builder_add (builder, "{ss}", "justification", + gtk_justification_to_string (text_attrs->justification)); + g_variant_builder_add (builder, "{ss}", "direction", + gtk_text_direction_to_string (text_attrs->direction)); + g_variant_builder_add (builder, "{ss}", "wrap-mode", + pango_wrap_mode_to_string (text_attrs->wrap_mode)); + g_variant_builder_add (builder, "{ss}", "editable", + text_attrs->editable ? "true" : "false"); + g_variant_builder_add (builder, "{ss}", "invisible", + text_attrs->invisible ? "true" : "false"); + g_variant_builder_add (builder, "{ss}", "bg-full-height", + text_attrs->bg_full_height ? "true" : "false"); + g_variant_builder_add (builder, "{ss}", "strikethrough", + text_attrs->appearance.strikethrough ? "true" : "false"); + g_variant_builder_add (builder, "{ss}", "underline", + pango_underline_to_string (text_attrs->appearance.underline)); + + value = g_strdup_printf ("%u,%u,%u", + (guint)(text_attrs->appearance.bg_rgba->red * 65535), + (guint)(text_attrs->appearance.bg_rgba->green * 65535), + (guint)(text_attrs->appearance.bg_rgba->blue * 65535)); + g_variant_builder_add (builder, "{ss}", "bg-color", value); + g_free (value); + + value = g_strdup_printf ("%u,%u,%u", + (guint)(text_attrs->appearance.fg_rgba->red * 65535), + (guint)(text_attrs->appearance.fg_rgba->green * 65535), + (guint)(text_attrs->appearance.fg_rgba->blue * 65535)); + g_variant_builder_add (builder, "{ss}", "bg-color", value); + g_free (value); + + value = g_strdup_printf ("%g", text_attrs->font_scale); + g_variant_builder_add (builder, "{ss}", "scale", value); + g_free (value); + + value = g_strdup ((gchar *)(text_attrs->language)); + g_variant_builder_add (builder, "{ss}", "language", value); + g_free (value); + + value = g_strdup_printf ("%i", text_attrs->appearance.rise); + g_variant_builder_add (builder, "{ss}", "rise", value); + g_free (value); + + value = g_strdup_printf ("%i", text_attrs->pixels_inside_wrap); + g_variant_builder_add (builder, "{ss}", "pixels-inside-wrap", value); + g_free (value); + + value = g_strdup_printf ("%i", text_attrs->pixels_below_lines); + g_variant_builder_add (builder, "{ss}", "pixels-below-lines", value); + g_free (value); + + value = g_strdup_printf ("%i", text_attrs->pixels_above_lines); + g_variant_builder_add (builder, "{ss}", "pixels-above-lines", value); + g_free (value); + + value = g_strdup_printf ("%i", text_attrs->indent); + g_variant_builder_add (builder, "{ss}", "indent", value); + g_free (value); + + value = g_strdup_printf ("%i", text_attrs->left_margin); + g_variant_builder_add (builder, "{ss}", "left-margin", value); + g_free (value); + + value = g_strdup_printf ("%i", text_attrs->right_margin); + g_variant_builder_add (builder, "{ss}", "right-margin", value); + g_free (value); + + gtk_text_attributes_unref (text_attrs); +} + +void +gtk_text_buffer_get_run_attributes (GtkTextBuffer *buffer, + GVariantBuilder *builder, + int offset, + int *start_offset, + int *end_offset) +{ + GtkTextIter iter; + GSList *tags, *temp_tags; + gdouble scale = 1; + gboolean val_set = FALSE; + + gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset); + + gtk_text_iter_forward_to_tag_toggle (&iter, NULL); + *end_offset = gtk_text_iter_get_offset (&iter); + + gtk_text_iter_backward_to_tag_toggle (&iter, NULL); + *start_offset = gtk_text_iter_get_offset (&iter); + + gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset); + + tags = gtk_text_iter_get_tags (&iter); + tags = g_slist_reverse (tags); + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + PangoStyle style; + + g_object_get (tag, + "style-set", &val_set, + "style", &style, + NULL); + if (val_set) + g_variant_builder_add (builder, "{ss}", "style", pango_style_to_string (style)); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + PangoVariant variant; + + g_object_get (tag, + "variant-set", &val_set, + "variant", &variant, + NULL); + if (val_set) + g_variant_builder_add (builder, "{ss}", "variant", pango_variant_to_string (variant)); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + PangoStretch stretch; + + g_object_get (tag, + "stretch-set", &val_set, + "stretch", &stretch, + NULL); + if (val_set) + g_variant_builder_add (builder, "{ss}", "stretch", pango_stretch_to_string (stretch)); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + GtkJustification justification; + + g_object_get (tag, + "justification-set", &val_set, + "justification", &justification, + NULL); + if (val_set) + g_variant_builder_add (builder, "{ss}", "justification", gtk_justification_to_string (justification)); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + GtkTextDirection direction; + + g_object_get (tag, "direction", &direction, NULL); + if (direction != GTK_TEXT_DIR_NONE) + { + val_set = TRUE; + g_variant_builder_add (builder, "{ss}", "direction", gtk_text_direction_to_string (direction)); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + GtkWrapMode wrap_mode; + + g_object_get (tag, + "wrap-mode-set", &val_set, + "wrap-mode", &wrap_mode, + NULL); + if (val_set) + g_variant_builder_add (builder, "{ss}", "wrap-mode", pango_wrap_mode_to_string (wrap_mode)); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + g_object_get (tag, "foreground-set", &val_set, NULL); + if (val_set) + { + GdkRGBA *rgba; + char *value; + + g_object_get (tag, "foreground", &rgba, NULL); + value = g_strdup_printf ("%u,%u,%u", + (guint) rgba->red * 65535, + (guint) rgba->green * 65535, + (guint) rgba->blue * 65535); + gdk_rgba_free (rgba); + g_variant_builder_add (builder, "{ss}", "fg-color", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + g_object_get (tag, "background-set", &val_set, NULL); + if (val_set) + { + GdkRGBA *rgba; + char *value; + + g_object_get (tag, "background-rgba", &rgba, NULL); + value = g_strdup_printf ("%u,%u,%u", + (guint) rgba->red * 65535, + (guint) rgba->green * 65535, + (guint) rgba->blue * 65535); + gdk_rgba_free (rgba); + g_variant_builder_add (builder, "{ss}", "bg-color", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + g_object_get (tag, "family-set", &val_set, NULL); + + if (val_set) + { + char *value; + g_object_get (tag, "family", &value, NULL); + g_variant_builder_add (builder, "{ss}", "family-name", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + + g_object_get (tag, "language-set", &val_set, NULL); + + if (val_set) + { + char *value; + g_object_get (tag, "language", &value, NULL); + g_variant_builder_add (builder, "{ss}", "language", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + int weight; + + g_object_get (tag, + "weight-set", &val_set, + "weight", &weight, + NULL); + + if (val_set) + { + char *value; + value = g_strdup_printf ("%d", weight); + g_variant_builder_add (builder, "{ss}", "weight", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + /* scale is special as the effective value is the product + * of all specified values + */ + temp_tags = tags; + while (temp_tags) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + gboolean scale_set; + + g_object_get (tag, "scale-set", &scale_set, NULL); + if (scale_set) + { + double font_scale; + g_object_get (tag, "scale", &font_scale, NULL); + val_set = TRUE; + scale *= font_scale; + } + temp_tags = temp_tags->next; + } + if (val_set) + { + char *value = g_strdup_printf ("%g", scale); + g_variant_builder_add (builder, "{ss}", "scale", value); + g_free (value); + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + int size; + + g_object_get (tag, + "size-set", &val_set, + "size", &size, + NULL); + if (val_set) + { + char *value = g_strdup_printf ("%i", size); + g_variant_builder_add (builder, "{ss}", "size", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + gboolean strikethrough; + + g_object_get (tag, + "strikethrough-set", &val_set, + "strikethrough", &strikethrough, + NULL); + if (val_set) + g_variant_builder_add (builder, "{ss}", "strikethrough", strikethrough ? "true" : "false"); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + PangoUnderline underline; + + g_object_get (tag, + "underline-set", &val_set, + "underline", &underline, + NULL); + if (val_set) + g_variant_builder_add (builder, "{ss}", "underline", + pango_underline_to_string (underline)); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + int rise; + + g_object_get (tag, + "rise-set", &val_set, + "rise", &rise, + NULL); + if (val_set) + { + char *value = g_strdup_printf ("%i", rise); + g_variant_builder_add (builder, "{ss}", "rise", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + gboolean bg_full_height; + + g_object_get (tag, + "background-full-height-set", &val_set, + "background-full-height", &bg_full_height, + NULL); + if (val_set) + g_variant_builder_add (builder, "{ss}", "bg-full-height", bg_full_height ? "true" : "false"); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + int pixels; + + g_object_get (tag, + "pixels-inside-wrap-set", &val_set, + "pixels-inside-wrap", &pixels, + NULL); + if (val_set) + { + char *value = g_strdup_printf ("%i", pixels); + g_variant_builder_add (builder, "{ss}", "pixels-inside-wrap", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + int pixels; + + g_object_get (tag, + "pixels-below-lines-set", &val_set, + "pixels-below-lines", &pixels, + NULL); + if (val_set) + { + char *value = g_strdup_printf ("%i", pixels); + g_variant_builder_add (builder, "{ss}", "pixels-below-lines", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + int pixels; + + g_object_get (tag, + "pixels-above-lines-set", &val_set, + "pixels-above-lines", &pixels, + NULL); + if (val_set) + { + char *value = g_strdup_printf ("%i", pixels); + g_variant_builder_add (builder, "{ss}", "pixels-above-lines", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + gboolean editable; + + g_object_get (tag, + "editable-set", &val_set, + "editable", &editable, + NULL); + if (val_set) + g_variant_builder_add (builder, "{ss}", "editable", editable ? "true" : "false"); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + gboolean invisible; + + g_object_get (tag, + "invisible-set", &val_set, + "invisible", &invisible, + NULL); + if (val_set) + g_variant_builder_add (builder, "{ss}", "invisible", invisible ? "true" : "false"); + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + int indent; + + g_object_get (tag, + "indent-set", &val_set, + "indent", &indent, + NULL); + if (val_set) + { + char *value = g_strdup_printf ("%i", indent); + g_variant_builder_add (builder, "{ss}", "indent", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + int margin; + + g_object_get (tag, + "right-margin-set", &val_set, + "right-margin", &margin, + NULL); + if (val_set) + { + char *value = g_strdup_printf ("%i", margin); + g_variant_builder_add (builder, "{ss}", "right-margin", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + temp_tags = tags; + while (temp_tags && !val_set) + { + GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data); + int margin; + + g_object_get (tag, + "left-margin-set", &val_set, + "left-margin", &margin, + NULL); + if (val_set) + { + char *value = g_strdup_printf ("%i", margin); + g_variant_builder_add (builder, "{ss}", "left-margin", value); + g_free (value); + } + temp_tags = temp_tags->next; + } + val_set = FALSE; + + g_slist_free (tags); +} + +char * +gtk_text_view_get_text_before (GtkTextView *view, + int offset, + AtspiTextBoundaryType boundary_type, + int *start_offset, + int *end_offset) +{ + GtkTextBuffer *buffer; + GtkTextIter pos, start, end; + + buffer = gtk_text_view_get_buffer (view); + gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset); + start = end = pos; + + switch (boundary_type) + { + case ATSPI_TEXT_BOUNDARY_CHAR: + gtk_text_iter_backward_char (&start); + break; + + case ATSPI_TEXT_BOUNDARY_WORD_START: + if (!gtk_text_iter_starts_word (&start)) + gtk_text_iter_backward_word_start (&start); + end = start; + gtk_text_iter_backward_word_start (&start); + break; + + case ATSPI_TEXT_BOUNDARY_WORD_END: + if (gtk_text_iter_inside_word (&start) && + !gtk_text_iter_starts_word (&start)) + gtk_text_iter_backward_word_start (&start); + while (!gtk_text_iter_ends_word (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + end = start; + gtk_text_iter_backward_word_start (&start); + while (!gtk_text_iter_ends_word (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + break; + + case ATSPI_TEXT_BOUNDARY_SENTENCE_START: + if (!gtk_text_iter_starts_sentence (&start)) + gtk_text_iter_backward_sentence_start (&start); + end = start; + gtk_text_iter_backward_sentence_start (&start); + break; + + case ATSPI_TEXT_BOUNDARY_SENTENCE_END: + if (gtk_text_iter_inside_sentence (&start) && + !gtk_text_iter_starts_sentence (&start)) + gtk_text_iter_backward_sentence_start (&start); + while (!gtk_text_iter_ends_sentence (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + end = start; + gtk_text_iter_backward_sentence_start (&start); + while (!gtk_text_iter_ends_sentence (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + break; + + case ATSPI_TEXT_BOUNDARY_LINE_START: + gtk_text_view_backward_display_line_start (view, &start); + end = start; + gtk_text_view_backward_display_line (view, &start); + gtk_text_view_backward_display_line_start (view, &start); + break; + + case ATSPI_TEXT_BOUNDARY_LINE_END: + gtk_text_view_backward_display_line_start (view, &start); + if (!gtk_text_iter_is_start (&start)) + { + gtk_text_view_backward_display_line (view, &start); + end = start; + gtk_text_view_forward_display_line_end (view, &end); + if (!gtk_text_iter_is_start (&start)) + { + if (gtk_text_view_backward_display_line (view, &start)) + gtk_text_view_forward_display_line_end (view, &start); + else + gtk_text_iter_set_offset (&start, 0); + } + } + else + end = start; + break; + + default: + g_assert_not_reached (); + } + + *start_offset = gtk_text_iter_get_offset (&start); + *end_offset = gtk_text_iter_get_offset (&end); + + return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE); +} + +char * +gtk_text_view_get_text_at (GtkTextView *view, + int offset, + AtspiTextBoundaryType boundary_type, + int *start_offset, + int *end_offset) +{ + GtkTextBuffer *buffer; + GtkTextIter pos, start, end; + + buffer = gtk_text_view_get_buffer (view); + gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset); + start = end = pos; + + switch (boundary_type) + { + case ATSPI_TEXT_BOUNDARY_CHAR: + gtk_text_iter_forward_char (&end); + break; + + case ATSPI_TEXT_BOUNDARY_WORD_START: + if (!gtk_text_iter_starts_word (&start)) + gtk_text_iter_backward_word_start (&start); + if (gtk_text_iter_inside_word (&end)) + gtk_text_iter_forward_word_end (&end); + while (!gtk_text_iter_starts_word (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + break; + + case ATSPI_TEXT_BOUNDARY_WORD_END: + if (gtk_text_iter_inside_word (&start) && + !gtk_text_iter_starts_word (&start)) + gtk_text_iter_backward_word_start (&start); + while (!gtk_text_iter_ends_word (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + gtk_text_iter_forward_word_end (&end); + break; + + case ATSPI_TEXT_BOUNDARY_SENTENCE_START: + if (!gtk_text_iter_starts_sentence (&start)) + gtk_text_iter_backward_sentence_start (&start); + if (gtk_text_iter_inside_sentence (&end)) + gtk_text_iter_forward_sentence_end (&end); + while (!gtk_text_iter_starts_sentence (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + break; + + case ATSPI_TEXT_BOUNDARY_SENTENCE_END: + if (gtk_text_iter_inside_sentence (&start) && + !gtk_text_iter_starts_sentence (&start)) + gtk_text_iter_backward_sentence_start (&start); + while (!gtk_text_iter_ends_sentence (&start)) + { + if (!gtk_text_iter_backward_char (&start)) + break; + } + gtk_text_iter_forward_sentence_end (&end); + break; + + case ATSPI_TEXT_BOUNDARY_LINE_START: + gtk_text_view_backward_display_line_start (view, &start); + gtk_text_view_forward_display_line (view, &end); + break; + + case ATSPI_TEXT_BOUNDARY_LINE_END: + gtk_text_view_backward_display_line_start (view, &start); + if (!gtk_text_iter_is_start (&start)) + { + gtk_text_view_backward_display_line (view, &start); + gtk_text_view_forward_display_line_end (view, &start); + } + gtk_text_view_forward_display_line_end (view, &end); + break; + + default: + g_assert_not_reached (); + } + + *start_offset = gtk_text_iter_get_offset (&start); + *end_offset = gtk_text_iter_get_offset (&end); + + return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE); +} + +char * +gtk_text_view_get_text_after (GtkTextView *view, + int offset, + AtspiTextBoundaryType boundary_type, + int *start_offset, + int *end_offset) +{ + GtkTextBuffer *buffer; + GtkTextIter pos, start, end; + + buffer = gtk_text_view_get_buffer (view); + gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset); + start = end = pos; + + switch (boundary_type) + { + case ATSPI_TEXT_BOUNDARY_CHAR: + gtk_text_iter_forward_char (&start); + gtk_text_iter_forward_chars (&end, 2); + break; + + case ATSPI_TEXT_BOUNDARY_WORD_START: + if (gtk_text_iter_inside_word (&end)) + gtk_text_iter_forward_word_end (&end); + while (!gtk_text_iter_starts_word (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + start = end; + if (!gtk_text_iter_is_end (&end)) + { + gtk_text_iter_forward_word_end (&end); + while (!gtk_text_iter_starts_word (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + } + break; + + case ATSPI_TEXT_BOUNDARY_WORD_END: + gtk_text_iter_forward_word_end (&end); + start = end; + if (!gtk_text_iter_is_end (&end)) + gtk_text_iter_forward_word_end (&end); + break; + + case ATSPI_TEXT_BOUNDARY_SENTENCE_START: + if (gtk_text_iter_inside_sentence (&end)) + gtk_text_iter_forward_sentence_end (&end); + while (!gtk_text_iter_starts_sentence (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + start = end; + if (!gtk_text_iter_is_end (&end)) + { + gtk_text_iter_forward_sentence_end (&end); + while (!gtk_text_iter_starts_sentence (&end)) + { + if (!gtk_text_iter_forward_char (&end)) + break; + } + } + break; + + case ATSPI_TEXT_BOUNDARY_SENTENCE_END: + gtk_text_iter_forward_sentence_end (&end); + start = end; + if (!gtk_text_iter_is_end (&end)) + gtk_text_iter_forward_sentence_end (&end); + break; + + case ATSPI_TEXT_BOUNDARY_LINE_START: + gtk_text_view_forward_display_line (view, &end); + start = end; + gtk_text_view_forward_display_line (view, &end); + break; + + case ATSPI_TEXT_BOUNDARY_LINE_END: + gtk_text_view_forward_display_line_end (view, &end); + start = end; + gtk_text_view_forward_display_line (view, &end); + gtk_text_view_forward_display_line_end (view, &end); + break; + + default: + g_assert_not_reached (); + } + + *start_offset = gtk_text_iter_get_offset (&start); + *end_offset = gtk_text_iter_get_offset (&end); + + return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE); +} + +char * +gtk_text_view_get_string_at (GtkTextView *view, + int offset, + AtspiTextGranularity granularity, + int *start_offset, + int *end_offset) +{ + GtkTextBuffer *buffer; + GtkTextIter pos, start, end; + + buffer = gtk_text_view_get_buffer (view); + gtk_text_buffer_get_iter_at_offset (buffer, &pos, offset); + start = end = pos; + + if (granularity == ATSPI_TEXT_GRANULARITY_CHAR) + { + gtk_text_iter_forward_char (&end); + } + else if (granularity == ATSPI_TEXT_GRANULARITY_WORD) + { + if (!gtk_text_iter_starts_word (&start)) + gtk_text_iter_backward_word_start (&start); + gtk_text_iter_forward_word_end (&end); + } + else if (granularity == ATSPI_TEXT_GRANULARITY_SENTENCE) + { + if (!gtk_text_iter_starts_sentence (&start)) + gtk_text_iter_backward_sentence_start (&start); + gtk_text_iter_forward_sentence_end (&end); + } + else if (granularity == ATSPI_TEXT_GRANULARITY_LINE) + { + if (!gtk_text_view_starts_display_line (view, &start)) + gtk_text_view_backward_display_line (view, &start); + gtk_text_view_forward_display_line_end (view, &end); + } + else if (granularity == ATSPI_TEXT_GRANULARITY_PARAGRAPH) + { + gtk_text_iter_set_line_offset (&start, 0); + gtk_text_iter_forward_to_line_end (&end); + } + + *start_offset = gtk_text_iter_get_offset (&start); + *end_offset = gtk_text_iter_get_offset (&end); + + return gtk_text_buffer_get_slice (buffer, &start, &end, FALSE); +} diff --git a/gtk/a11y/gtkatspitextbufferprivate.h b/gtk/a11y/gtkatspitextbufferprivate.h new file mode 100644 index 0000000000..d049e3d2e1 --- /dev/null +++ b/gtk/a11y/gtkatspitextbufferprivate.h @@ -0,0 +1,54 @@ +/* gtkatspitextbufferprivate.h: Utilities for GtkTextBuffer and AT-SPI + * Copyright 2020 Red Hat, Inc + * + * 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 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 "gtkatspiprivate.h" +#include "gtktextview.h" + +G_BEGIN_DECLS + +void gtk_text_view_add_default_attributes (GtkTextView *view, + GVariantBuilder *builder); +void gtk_text_buffer_get_run_attributes (GtkTextBuffer *buffer, + GVariantBuilder *builder, + int offset, + int *start_offset, + int *end_offset); + +char *gtk_text_view_get_text_before (GtkTextView *view, + int offset, + AtspiTextBoundaryType boundary_type, + int *start_offset, + int *end_offset); +char *gtk_text_view_get_text_at (GtkTextView *view, + int offset, + AtspiTextBoundaryType boundary_type, + int *start_offset, + int *end_offset); +char *gtk_text_view_get_text_after (GtkTextView *view, + int offset, + AtspiTextBoundaryType boundary_type, + int *start_offset, + int *end_offset); +char *gtk_text_view_get_string_at (GtkTextView *view, + int offset, + AtspiTextGranularity granularity, + int *start_offset, + int *end_offset); + +G_END_DECLS diff --git a/gtk/a11y/meson.build b/gtk/a11y/meson.build index efed4154d7..f9f66e1ad7 100644 --- a/gtk/a11y/meson.build +++ b/gtk/a11y/meson.build @@ -14,5 +14,6 @@ if gtk_a11y_backends.contains('atspi') 'gtkatspiroot.c', 'gtkatspiutils.c', 'gtkatspipango.c', + 'gtkatspitextbuffer.c', ]) endif