[gtk/a11y/atspi] atspi: Add textview utilities



commit 925ef1f90faa4b4cebf54f4a424b566c36977393
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat Oct 10 23:47:34 2020 -0400

    atspi: Add textview utilities
    
    These are very similar to the pango utilities,
    and are copying code from the textview accessible
    implementation in GTK 3.

 gtk/a11y/gtkatspitextbuffer.c        | 984 +++++++++++++++++++++++++++++++++++
 gtk/a11y/gtkatspitextbufferprivate.h |  54 ++
 gtk/a11y/meson.build                 |   1 +
 3 files changed, 1039 insertions(+)
---
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 <http://www.gnu.org/licenses/>.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 <http://www.gnu.org/licenses/>.
+ */
+
+#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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]