[gtk/a11y/atspi] atspi: Implement Text interface for labels



commit b80272a7e7d56b7600e21e74478fe35f5e5adb2f
Author: Matthias Clasen <mclasen redhat com>
Date:   Fri Oct 9 23:35:00 2020 -0400

    atspi: Implement Text interface for labels
    
    This is a not-quite-complete implementation of the
    Text interface for GtkLabel. The missing parts are
    anything around extents and positions, as well as
    the ScrollSubstring apis.

 gtk/a11y/gtkatspicontext.c | 345 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 345 insertions(+)
---
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 <gio/gio.h>
@@ -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


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