[gtk+/drop-gail: 24/24] Move GailLabel to GtkLabelAccessible



commit 0e11f6eb18bf75a0b4420dc60e57ad1ec58404de
Author: Matthias Clasen <mclasen redhat com>
Date:   Sat May 14 22:08:47 2011 -0400

    Move GailLabel to GtkLabelAccessible
    
    This is somewhat incomplete; some of the crazy hacks from GailLabel
    have simply been cut for now. Replacements for most of the
    libgail-util helpers have been parked in gtkpango for now; some
    should perhaps be moved to pango and/or glib.

 gtk/Makefile.am           |    1 +
 gtk/gtklabel.c            |  636 ++++++++++++++++++++++++++++++--
 gtk/gtkpango.c            |  923 +++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtkpango.h            |   65 +++-
 gtk/gtkstylecontext.c     |   55 +++
 gtk/gtkstylecontext.h     |    6 +
 modules/other/gail/gail.c |    6 +-
 7 files changed, 1663 insertions(+), 29 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 7e24d6e..ff63509 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -411,6 +411,7 @@ gtk_private_h_sources =		\
 	gtkimcontextsimpleseqs.h \
 	gtkintl.h		\
 	gtkkeyhash.h		\
+	gtklabelaccessible.h	\
 	gtkmainprivate.h	\
 	gtkmenuprivate.h	\
 	gtkmenuitemprivate.h	\
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index 9b9752b..8e2cbc3 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -29,6 +29,7 @@
 #include <string.h>
 
 #include "gtklabel.h"
+#include "gtklabelaccessible.h"
 #include "gtkaccellabel.h"
 #include "gtkdnd.h"
 #include "gtkmainprivate.h"
@@ -51,6 +52,7 @@
 #include "gtktooltip.h"
 #include "gtkprivate.h"
 #include "gtktypebuiltins.h"
+#include "gtkpango.h"
 
 
 /**
@@ -463,6 +465,8 @@ static void gtk_label_ensure_layout       (GtkLabel *label);
 static void gtk_label_select_region_index (GtkLabel *label,
                                            gint      anchor_index,
                                            gint      end_index);
+static gint gtk_label_get_cursor_position (GtkLabel *label);
+static gint gtk_label_get_selection_bound (GtkLabel *label);
 
 static gboolean gtk_label_mnemonic_activate (GtkWidget         *widget,
 					     gboolean           group_cycling);
@@ -1201,24 +1205,10 @@ gtk_label_get_property (GObject     *object,
       g_value_set_object (value, (GObject*) priv->mnemonic_widget);
       break;
     case PROP_CURSOR_POSITION:
-      if (priv->select_info && priv->select_info->selectable)
-	{
-	  gint offset = g_utf8_pointer_to_offset (priv->text,
-						  priv->text + priv->select_info->selection_end);
-	  g_value_set_int (value, offset);
-	}
-      else
-	g_value_set_int (value, 0);
+      g_value_set_int (value, gtk_label_get_cursor_position (label));
       break;
     case PROP_SELECTION_BOUND:
-      if (priv->select_info && priv->select_info->selectable)
-	{
-	  gint offset = g_utf8_pointer_to_offset (priv->text,
-						  priv->text + priv->select_info->selection_anchor);
-	  g_value_set_int (value, offset);
-	}
-      else
-	g_value_set_int (value, 0);
+      g_value_set_int (value, gtk_label_get_selection_bound (label));
       break;
     case PROP_ELLIPSIZE:
       g_value_set_enum (value, priv->ellipsize);
@@ -5507,10 +5497,10 @@ gtk_label_select_region  (GtkLabel *label,
     {
       if (start_offset < 0)
         start_offset = g_utf8_strlen (priv->text, -1);
-      
+
       if (end_offset < 0)
         end_offset = g_utf8_strlen (priv->text, -1);
-      
+
       gtk_label_select_region_index (label,
                                      g_utf8_offset_to_pointer (priv->text, start_offset) - priv->text,
                                      g_utf8_offset_to_pointer (priv->text, end_offset) - priv->text);
@@ -5522,12 +5512,12 @@ gtk_label_select_region  (GtkLabel *label,
  * @label: a #GtkLabel
  * @start: (out): return location for start of selection, as a character offset
  * @end: (out): return location for end of selection, as a character offset
- * 
+ *
  * Gets the selected range of characters in the label, returning %TRUE
  * if there's a selection.
- * 
+ *
  * Return value: %TRUE if selection is non-empty
- **/
+ */
 gboolean
 gtk_label_get_selection_bounds (GtkLabel  *label,
                                 gint      *start,
@@ -5554,7 +5544,7 @@ gtk_label_get_selection_bounds (GtkLabel  *label,
       gint start_index, end_index;
       gint start_offset, end_offset;
       gint len;
-      
+
       start_index = MIN (priv->select_info->selection_anchor,
                    priv->select_info->selection_end);
       end_index = MAX (priv->select_info->selection_anchor,
@@ -5813,7 +5803,6 @@ get_better_cursor (GtkLabel *label,
     }
 }
 
-
 static gint
 gtk_label_move_logically (GtkLabel *label,
 			  gint      start,
@@ -6647,3 +6636,604 @@ gtk_label_query_tooltip (GtkWidget  *widget,
                                                                    keyboard_tip,
                                                                    tooltip);
 }
+
+static gint
+gtk_label_get_cursor_position (GtkLabel *label)
+{
+  GtkLabelPrivate *priv = label->priv;
+
+  if (priv->select_info && priv->select_info->selectable)
+    return g_utf8_pointer_to_offset (priv->text,
+                                     priv->text + priv->select_info->selection_end);
+
+  return 0;
+}
+
+static gint
+gtk_label_get_selection_bound (GtkLabel *label)
+{
+  GtkLabelPrivate *priv = label->priv;
+
+  if (priv->select_info && priv->select_info->selectable)
+    return g_utf8_pointer_to_offset (priv->text,
+                                     priv->text + priv->select_info->selection_anchor);
+
+  return 0;
+}
+
+
+/* --- Accessibility --- */
+
+#include "gtkaccessibility.h"
+#include "gtklabelaccessible.h"
+
+static void atk_text_interface_init (AtkTextIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkLabelAccessible, gtk_label_accessible, GTK_TYPE_WIDGET_ACCESSIBLE,
+                         G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
+
+static void
+gtk_label_accessible_initialize (AtkObject *obj,
+                                 gpointer   data)
+{
+  GtkWidget *widget;
+
+  ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->initialize (obj, data);
+
+  widget = GTK_WIDGET (data);
+
+ if (GTK_IS_ACCEL_LABEL (widget))
+   atk_object_set_role (obj, ATK_ROLE_ACCEL_LABEL);
+ else
+  atk_object_set_role (obj, ATK_ROLE_LABEL);
+}
+
+static AtkStateSet*
+gtk_label_accessible_ref_state_set (AtkObject *accessible)
+{
+  AtkStateSet *state_set;
+  GtkWidget *widget;
+
+  state_set = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->ref_state_set (accessible);
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (accessible));
+
+  if (widget == NULL)
+    return state_set;
+
+  /* FIXME: only if wrap is true ? */
+  atk_state_set_add_state (state_set, ATK_STATE_MULTI_LINE);
+
+  return state_set;
+}
+
+AtkRelationSet*
+gtk_label_accessible_ref_relation_set (AtkObject *obj)
+{
+  GtkWidget *widget;
+  AtkRelationSet *relation_set;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
+  if (widget == NULL)
+    return NULL;
+
+  relation_set = ATK_OBJECT_CLASS (gtk_label_accessible_parent_class)->ref_relation_set (obj);
+
+  if (!atk_relation_set_contains (relation_set, ATK_RELATION_LABEL_FOR))
+    {
+      /*
+       * Get the mnemonic widget
+       *
+       * The relation set is not updated if the mnemonic widget is changed
+       */
+      GtkWidget *mnemonic_widget;
+
+      mnemonic_widget = gtk_label_get_mnemonic_widget (GTK_LABEL (widget));
+
+      if (mnemonic_widget)
+        {
+          AtkObject *accessible_array[1];
+          AtkRelation* relation;
+
+          accessible_array[0] = gtk_widget_get_accessible (mnemonic_widget);
+          relation = atk_relation_new (accessible_array, 1, ATK_RELATION_LABEL_FOR);
+          atk_relation_set_add (relation_set, relation);
+          /* Unref the relation so that it is not leaked. */
+          g_object_unref (relation);
+        }
+    }
+
+  return relation_set;
+}
+
+/* FIXME: emit text_caret_moved and text_selection_changed when appropriate */
+
+static void
+gtk_label_accessible_class_init (GtkLabelAccessibleClass *klass)
+{
+  AtkObjectClass  *class = ATK_OBJECT_CLASS (klass);
+
+  class->ref_state_set = gtk_label_accessible_ref_state_set;
+  class->ref_relation_set = gtk_label_accessible_ref_relation_set;
+  class->initialize = gtk_label_accessible_initialize;
+}
+
+static void
+gtk_label_accessible_init (GtkLabelAccessible *label)
+{
+}
+
+GTK_ACCESSIBLE_FACTORY(GTK_TYPE_LABEL_ACCESSIBLE, gtk_label_accessible)
+
+
+static gchar *
+gtk_label_accessible_get_text (AtkText *atk_text,
+                               gint     start_pos,
+                               gint     end_pos)
+{
+  GtkWidget *widget;
+  const gchar *text;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
+  if (widget == NULL)
+    return NULL;
+
+  text = gtk_label_get_text (GTK_LABEL (widget));
+
+  if (text)
+    return _g_utf8_substring (text, start_pos, end_pos);
+
+  return NULL;
+}
+
+static gint
+gtk_label_accessible_get_character_count (AtkText *atk_text)
+{
+  GtkWidget *widget;
+  const gchar *text;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
+  if (widget == NULL)
+    return 0;
+
+  text = gtk_label_get_text (GTK_LABEL (widget));
+
+  if (text)
+    return g_utf8_strlen (text, -1);
+
+  return 0;
+}
+
+static gint
+gtk_label_accessible_get_caret_offset (AtkText *text)
+{
+  GtkWidget *widget;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return 0;
+
+  return gtk_label_get_cursor_position (GTK_LABEL (widget));
+}
+
+static gboolean
+gtk_label_accessible_set_caret_offset (AtkText *text,
+                                       gint    offset)
+{
+  GtkWidget *widget;
+  GtkLabel *label;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return 0;
+
+  label = GTK_LABEL (widget);
+
+  if (!gtk_label_get_selectable (label))
+    return FALSE;
+
+  gtk_label_select_region (label, offset, offset);
+
+  return TRUE;
+}
+
+static gint
+gtk_label_accessible_get_n_selections (AtkText *text)
+{
+  GtkWidget *widget;
+  gint start, end;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return 0;
+
+  if (gtk_label_get_selection_bounds (GTK_LABEL (widget), &start, &end))
+    return 1;
+
+ return 0;
+}
+
+static gchar *
+gtk_label_accessible_get_selection (AtkText *text,
+                                    gint     selection_num,
+                                    gint    *start_pos,
+                                    gint    *end_pos)
+{
+  GtkWidget *widget;
+  GtkLabel  *label;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return NULL;
+
+  label = GTK_LABEL (widget);
+
+  if (selection_num != 0)
+    return NULL;
+
+  if (gtk_label_get_selection_bounds (label, start_pos, end_pos))
+    {
+      const gchar *text;
+
+      text = gtk_label_get_text (label);
+
+      if (text)
+        return _g_utf8_substring (text, *start_pos, *end_pos);
+    }
+
+  return NULL;
+}
+
+static gboolean
+gtk_label_accessible_add_selection (AtkText *text,
+                                    gint     start_pos,
+                                    gint     end_pos)
+{
+  GtkWidget *widget;
+  GtkLabel  *label;
+  gint start, end;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return FALSE;
+
+  label = GTK_LABEL (widget);
+
+  if (!gtk_label_get_selectable (label))
+     return FALSE;
+
+  if (!gtk_label_get_selection_bounds (label, &start, &end))
+    {
+      gtk_label_select_region (label, start_pos, end_pos);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static gboolean
+gtk_label_accessible_remove_selection (AtkText *text,
+                                       gint     selection_num)
+{
+  GtkWidget *widget;
+  GtkLabel  *label;
+  gint start, end;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return FALSE;
+
+  label = GTK_LABEL (widget);
+
+  if (selection_num != 0)
+     return FALSE;
+
+  if (!gtk_label_get_selectable (label))
+     return FALSE;
+
+  if (gtk_label_get_selection_bounds (label, &start, &end))
+    {
+      gtk_label_select_region (label, 0, 0);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static gboolean
+gtk_label_accessible_set_selection (AtkText *text,
+                                    gint     selection_num,
+                                    gint     start_pos,
+                                    gint     end_pos)
+{
+  GtkWidget *widget;
+  GtkLabel  *label;
+  gint start, end;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return FALSE;
+
+  if (selection_num != 0)
+     return FALSE;
+
+  label = GTK_LABEL (widget);
+
+  if (!gtk_label_get_selectable (label))
+     return FALSE;
+
+  if (gtk_label_get_selection_bounds (label, &start, &end))
+    {
+      gtk_label_select_region (label, start_pos, end_pos);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static void
+gtk_label_accessible_get_character_extents (AtkText      *atk_text,
+                                            gint          offset,
+                                            gint         *x,
+                                            gint         *y,
+                                            gint         *width,
+                                            gint         *height,
+                                            AtkCoordType  coords)
+{
+  GtkWidget *widget;
+  GtkLabel *label;
+  PangoRectangle char_rect;
+  const gchar *text;
+  gint index, x_layout, y_layout;
+  GdkWindow *window;
+  gint x_window, y_window;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
+
+  if (widget == NULL)
+    return;
+
+  label = GTK_LABEL (widget);
+
+  gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
+  text = gtk_label_get_text (label);
+  index = g_utf8_offset_to_pointer (text, offset) - text;
+  pango_layout_index_to_pos (gtk_label_get_layout (label), index, &char_rect);
+  pango_extents_to_pixels (&char_rect, NULL);
+
+  window = gtk_widget_get_window (widget);
+  gdk_window_get_origin (window, &x_window, &y_window);
+
+  *x = x_window + x_layout + char_rect.x;
+  *y = x_window + y_layout + char_rect.y;
+  *width = char_rect.width;
+  *height = char_rect.height;
+
+  if (coords == ATK_XY_WINDOW)
+    {
+      window = gdk_window_get_toplevel (window);
+      gdk_window_get_origin (window, &x_window, &y_window);
+
+      *x -= x_window;
+      *y -= y_window;
+    }
+}
+
+static gint
+gtk_label_accessible_get_offset_at_point (AtkText      *atk_text,
+                                          gint          x,
+                                          gint          y,
+                                          AtkCoordType  coords)
+{
+  GtkWidget *widget;
+  GtkLabel *label;
+  const gchar *text;
+  gint index, x_layout, y_layout;
+  gint x_window, y_window;
+  gint x_local, y_local;
+  GdkWindow *window;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
+  if (widget == NULL)
+    return -1;
+
+  label = GTK_LABEL (widget);
+
+  gtk_label_get_layout_offsets (label, &x_layout, &y_layout);
+
+  window = gtk_widget_get_window (widget);
+  gdk_window_get_origin (window, &x_window, &y_window);
+
+  x_local = x - x_layout - x_window;
+  y_local = y - y_layout - y_window;
+
+  if (coords == ATK_XY_WINDOW)
+    {
+      window = gdk_window_get_toplevel (window);
+      gdk_window_get_origin (window, &x_window, &y_window);
+
+      x_local += x_window;
+      y_local += y_window;
+    }
+
+  if (!pango_layout_xy_to_index (gtk_label_get_layout (label),
+                                 x_local * PANGO_SCALE,
+                                 y_local * PANGO_SCALE,
+                                 &index, NULL))
+    {
+      if (x_local < 0 || y_local < 0)
+        index = 0;
+      else
+        index = -1;
+    }
+
+  if (index != -1)
+    {
+      text = gtk_label_get_text (label);
+      return g_utf8_pointer_to_offset (text, text + index);
+    }
+
+  return -1;
+}
+
+static AtkAttributeSet *
+add_attribute (AtkAttributeSet  *attributes,
+               AtkTextAttribute  attr,
+               const gchar      *value)
+{
+  AtkAttribute *at;
+
+  at = g_new (AtkAttribute, 1);
+  at->name = g_strdup (atk_text_attribute_get_name (attr));
+  at->value = g_strdup (value);
+
+  return g_slist_prepend (attributes, at);
+}
+
+static AtkAttributeSet*
+gtk_label_accessible_get_run_attributes (AtkText *text,
+                                         gint     offset,
+                                         gint    *start_offset,
+                                         gint    *end_offset)
+{
+  GtkWidget *widget;
+  AtkAttributeSet *attributes;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return NULL;
+
+  attributes = NULL;
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
+                   atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION,
+                                                 gtk_widget_get_direction (widget)));
+  attributes = _gtk_pango_get_run_attributes (attributes,
+                                              gtk_label_get_layout (GTK_LABEL (widget)),
+                                              offset,
+                                              start_offset,
+                                              end_offset);
+
+  return attributes;
+}
+
+static AtkAttributeSet*
+gtk_label_accessible_get_default_attributes (AtkText *text)
+{
+  GtkWidget *widget;
+  AtkAttributeSet *attributes;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return NULL;
+
+  attributes = NULL;
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_DIRECTION,
+                   atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION,
+                                                 gtk_widget_get_direction (widget)));
+  attributes = _gtk_pango_get_default_attributes (attributes,
+                                                  gtk_label_get_layout (GTK_LABEL (widget)));
+  attributes = _gtk_style_context_get_attributes (attributes,
+                                                  gtk_widget_get_style_context (widget),
+                                                  gtk_widget_get_state_flags (widget));
+
+  return attributes;
+}
+
+static gunichar
+gtk_label_accessible_get_character_at_offset (AtkText *atk_text,
+                                              gint     offset)
+{
+  GtkWidget *widget;
+  const gchar *text;
+  gchar *index;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (atk_text));
+  if (widget == NULL)
+    return '\0';
+
+  text = gtk_label_get_text (GTK_LABEL (widget));
+
+  if (offset >= g_utf8_strlen (text, -1))
+    return '\0';
+
+  index = g_utf8_offset_to_pointer (text, offset);
+
+  return g_utf8_get_char (text, index);
+}
+
+static gchar *
+gtk_label_accessible_get_text_before_offset (AtkText         *text,
+                                             gint             offset,
+                                             AtkTextBoundary  boundary_type,
+                                             gint            *start_offset,
+                                             gint            *end_offset)
+{
+  GtkWidget *widget;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return NULL;
+
+  return _gtk_pango_get_text_before (gtk_label_get_layout (GTK_LABEL (widget)),
+                                     boundary_type, offset,
+                                     start_offset, end_offset);
+}
+
+static gchar*
+gtk_label_accessible_get_text_at_offset (AtkText         *text,
+                                         gint             offset,
+                                         AtkTextBoundary  boundary_type,
+                                         gint            *start_offset,
+                                         gint            *end_offset)
+{
+  GtkWidget *widget;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return NULL;
+
+  return _gtk_pango_get_text_at (gtk_label_get_layout (GTK_LABEL (widget)),
+                                 boundary_type, offset,
+                                 start_offset, end_offset);
+}
+
+static gchar*
+gtk_label_accessible_get_text_after_offset (AtkText         *text,
+                                            gint             offset,
+                                            AtkTextBoundary  boundary_type,
+                                            gint            *start_offset,
+                                            gint            *end_offset)
+{
+  GtkWidget *widget;
+
+  widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (text));
+  if (widget == NULL)
+    return NULL;
+
+  return _gtk_pango_get_text_after (gtk_label_get_layout (GTK_LABEL (widget)),
+                                    boundary_type, offset,
+                                    start_offset, end_offset);
+}
+
+
+
+static void
+atk_text_interface_init (AtkTextIface *iface)
+{
+  iface->get_text = gtk_label_accessible_get_text;
+  iface->get_character_at_offset = gtk_label_accessible_get_character_at_offset;
+  iface->get_text_before_offset = gtk_label_accessible_get_text_before_offset;
+  iface->get_text_at_offset = gtk_label_accessible_get_text_at_offset;
+  iface->get_text_after_offset = gtk_label_accessible_get_text_after_offset;
+  iface->get_character_count = gtk_label_accessible_get_character_count;
+  iface->get_caret_offset = gtk_label_accessible_get_caret_offset;
+  iface->set_caret_offset = gtk_label_accessible_set_caret_offset;
+  iface->get_n_selections = gtk_label_accessible_get_n_selections;
+  iface->get_selection = gtk_label_accessible_get_selection;
+  iface->add_selection = gtk_label_accessible_add_selection;
+  iface->remove_selection = gtk_label_accessible_remove_selection;
+  iface->set_selection = gtk_label_accessible_set_selection;
+  iface->get_character_extents = gtk_label_accessible_get_character_extents;
+  iface->get_offset_at_point = gtk_label_accessible_get_offset_at_point;
+  iface->get_run_attributes = gtk_label_accessible_get_run_attributes;
+  iface->get_default_attributes = gtk_label_accessible_get_default_attributes;
+}
diff --git a/gtk/gtkpango.c b/gtk/gtkpango.c
index be6eb4e..de3f3eb 100644
--- a/gtk/gtkpango.c
+++ b/gtk/gtkpango.c
@@ -24,6 +24,7 @@
  */
 
 #include "config.h"
+#include "gtkpango.h"
 #include <pango/pangocairo.h>
 #include "gtkintl.h"
 
@@ -231,3 +232,925 @@ _gtk_pango_fill_layout (cairo_t     *cr,
     cairo_move_to (cr, current_x, current_y);
 }
 
+static AtkAttributeSet *
+add_attribute (AtkAttributeSet  *attributes,
+               AtkTextAttribute  attr,
+               const gchar      *value)
+{
+  AtkAttribute *at;
+
+  at = g_new (AtkAttribute, 1);
+  at->name = g_strdup (atk_text_attribute_get_name (attr));
+  at->value = g_strdup (value);
+
+  return g_slist_prepend (attributes, at);
+}
+
+/**
+ * _gtk_pango_get_default_attributes:
+ * @attributes: a #AtkAttributeSet to add the attributes to
+ * @layout: the #PangoLayout from which to get attributes
+ *
+ * Adds the default text attributes from @layout to @attributes,
+ * after translating them from Pango attributes to ATK attributes.
+ *
+ * This is a convenience function that can be used to implement
+ * support for the #AtkText interface in widgets.
+ *
+ * Returns: the modified @attributes
+ */
+AtkAttributeSet*
+_gtk_pango_get_default_attributes (AtkAttributeSet *attributes,
+                                   PangoLayout     *layout)
+{
+  PangoContext *context;
+  gint i;
+  PangoWrapMode mode;
+
+  context = pango_layout_get_context (layout);
+  if (context)
+    {
+      PangoLanguage *language;
+      PangoFontDescription *font;
+
+      language = pango_context_get_language (context);
+      if (language)
+        attributes = add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE,
+                         pango_language_to_string (language));
+
+      font = pango_context_get_font_description (context);
+      if (font)
+        {
+          gchar buf[60];
+          attributes = add_attribute (attributes, ATK_TEXT_ATTR_STYLE,
+                           atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE,
+                                 pango_font_description_get_style (font)));
+          attributes = add_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
+                           atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT,
+                                 pango_font_description_get_variant (font)));
+          attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
+                           atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH,
+                                 pango_font_description_get_stretch (font)));
+          attributes = add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME,
+                           pango_font_description_get_family (font));
+          g_snprintf (buf, 60, "%d", pango_font_description_get_weight (font));
+          attributes = add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, buf);
+          g_snprintf (buf, 60, "%i", pango_font_description_get_size (font) / PANGO_SCALE);
+          attributes = add_attribute (attributes, ATK_TEXT_ATTR_SIZE, buf);
+        }
+    }
+  if (pango_layout_get_justify (layout))
+    {
+      i = 3;
+    }
+  else
+    {
+      PangoAlignment align;
+
+      align = pango_layout_get_alignment (layout);
+      if (align == PANGO_ALIGN_LEFT)
+        i = 0;
+      else if (align == PANGO_ALIGN_CENTER)
+        i = 2;
+      else /* if (align == PANGO_ALIGN_RIGHT) */
+        i = 1;
+    }
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_JUSTIFICATION,
+                   atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, i));
+  mode = pango_layout_get_wrap (layout);
+  if (mode == PANGO_WRAP_WORD)
+    i = 2;
+  else /* if (mode == PANGO_WRAP_CHAR) */
+    i = 1;
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_WRAP_MODE,
+                   atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, i));
+
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
+                   atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 0));
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
+                   atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, 0));
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_RISE, "0");
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_SCALE, "1.0");
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_FULL_HEIGHT, "0");
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, "0");
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, "0");
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, "0");
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_EDITABLE,
+                   atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0));
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_INVISIBLE,
+                   atk_text_attribute_get_value (ATK_TEXT_ATTR_INVISIBLE, 0));
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_INDENT, "0");
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_RIGHT_MARGIN, "0");
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_LEFT_MARGIN, "0");
+
+  return attributes;
+}
+
+/*
+ * _gtk_pango_get_run_attributes:
+ * @attributes: a #AtkAttributeSet to add attributes to
+ * @layout: the #PangoLayout to get the attributes from
+ * @offset: the offset at which the attributes are wanted
+ * @start_offset: return location for the starting offset
+ *    of the current run
+ * @end_offset: return location for the ending offset of the
+ *    current run
+ *
+ * Finds the 'run' around index (ie the maximal range of characters
+ * where the set of applicable attributes remains constant) and
+ * returns the starting and ending offsets for it. The attributes
+ * for the run are added to @attributes,
+ * after translating them from Pango attributes to ATK attributes.
+ *
+ * This is a convenience function that can be used to implement
+ * support for the #AtkText interface in widgets.
+ *
+ * Returns: the modified #AtkAttributeSet
+ */
+AtkAttributeSet *
+_gtk_pango_get_run_attributes (AtkAttributeSet *attributes,
+                               PangoLayout     *layout,
+                               gint             offset,
+                               gint            *start_offset,
+                               gint            *end_offset)
+{
+  PangoAttrIterator *iter;
+  PangoAttrList *attr;
+  PangoAttrString *pango_string;
+  PangoAttrInt *pango_int;
+  PangoAttrColor *pango_color;
+  PangoAttrLanguage *pango_lang;
+  PangoAttrFloat *pango_float;
+  gint index, start_index, end_index;
+  gboolean is_next;
+  glong len;
+  const gchar *text;
+  gchar *value;
+
+  text = pango_layout_get_text (layout);
+  len = g_utf8_strlen (text, -1);
+
+  /* Grab the attributes of the PangoLayout, if any */
+  attr = pango_layout_get_attributes (layout);
+
+  if (attr == NULL)
+    {
+      *start_offset = 0;
+      *end_offset = len;
+      return attributes;
+    }
+
+  iter = pango_attr_list_get_iterator (attr);
+  /* Get invariant range offsets */
+  /* If offset out of range, set offset in range */
+  if (offset > len)
+    offset = len;
+  else if (offset < 0)
+    offset = 0;
+
+  index = g_utf8_offset_to_pointer (text, offset) - text;
+  pango_attr_iterator_range (iter, &start_index, &end_index);
+  is_next = TRUE;
+  while (is_next)
+    {
+      if (index >= start_index && index < end_index)
+        {
+          *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
+          if (end_index == G_MAXINT) /* Last iterator */
+            end_index = len;
+
+          *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
+          break;
+        }
+      is_next = pango_attr_iterator_next (iter);
+      pango_attr_iterator_range (iter, &start_index, &end_index);
+    }
+
+  /* Get attributes */
+  pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY);
+  if (pango_string != NULL)
+    {
+      value = g_strdup_printf ("%s", pango_string->value);
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME, value);
+      g_free (value);
+    }
+  pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STYLE);
+  if (pango_int != NULL)
+    {
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_STYLE,
+                       atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value));
+    }
+  pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_WEIGHT);
+  if (pango_int != NULL)
+    {
+      value = g_strdup_printf ("%i", pango_int->value);
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, value);
+      g_free (value);
+    }
+  pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_VARIANT);
+  if (pango_int != NULL)
+    {
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
+                       atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value));
+    }
+  pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRETCH);
+  if (pango_int != NULL)
+    {
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
+                       atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value));
+    }
+  pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_SIZE);
+  if (pango_int != NULL)
+    {
+      value = g_strdup_printf ("%i", pango_int->value / PANGO_SCALE);
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_SIZE, value);
+      g_free (value);
+    }
+  pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
+  if (pango_int != NULL)
+    {
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
+                       atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value));
+    }
+  pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRIKETHROUGH);
+  if (pango_int != NULL)
+    {
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
+                       atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value));
+    }
+  pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_RISE);
+  if (pango_int != NULL)
+    {
+      value = g_strdup_printf ("%i", pango_int->value);
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_RISE, value);
+      g_free (value);
+    }
+  pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE);
+  if (pango_lang != NULL)
+    {
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE,
+                                  pango_language_to_string (pango_lang->value));
+    }
+  pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, PANGO_ATTR_SCALE);
+  if (pango_float != NULL)
+    {
+      value = g_strdup_printf ("%g", pango_float->value);
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_SCALE, value);
+      g_free (value);
+    }
+  pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
+  if (pango_color != NULL)
+    {
+      value = g_strdup_printf ("%u,%u,%u",
+                               pango_color->color.red,
+                               pango_color->color.green,
+                               pango_color->color.blue);
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
+      g_free (value);
+    }
+  pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_BACKGROUND);
+  if (pango_color != NULL)
+    {
+      value = g_strdup_printf ("%u,%u,%u",
+                               pango_color->color.red,
+                               pango_color->color.green,
+                               pango_color->color.blue);
+      attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
+      g_free (value);
+    }
+  pango_attr_iterator_destroy (iter);
+
+  return attributes;
+}
+
+gint
+_gtk_pango_move_chars (PangoLayout  *layout,
+                       PangoLogAttr *attrs,
+                       gint          n_attrs,
+                       gint          offset,
+                       gint          count)
+{
+  while (count > 0 && offset < n_attrs - 1)
+    {
+      do
+        offset++;
+      while (offset < n_attrs - 1 && !attrs[offset].is_cursor_position);
+
+      count--;
+    }
+  while (count < 0 && offset > 0)
+    {
+      do
+        offset--;
+      while (offset > 0 && !attrs[offset].is_cursor_position);
+
+      count++;
+    }
+
+  return offset;
+}
+
+gint
+_gtk_pango_move_words (PangoLayout  *layout,
+                       PangoLogAttr *attrs,
+                       gint          n_attrs,
+                       gint          offset,
+                       gint          count)
+{
+  while (count > 0 && offset < n_attrs - 1)
+    {
+      do
+        offset++;
+      while (offset < n_attrs - 1 && !attrs[offset].is_word_end);
+
+      count--;
+    }
+  while (count < 0 && offset > 0)
+    {
+      do
+        offset--;
+      while (offset > 0 && !attrs[offset].is_word_start);
+
+      count++;
+    }
+
+  return offset;
+}
+
+gint
+_gtk_pango_move_sentences (PangoLayout  *layout,
+                           PangoLogAttr *attrs,
+                           gint          n_attrs,
+                           gint          offset,
+                           gint          count)
+{
+  while (count > 0 && offset < n_attrs - 1)
+    {
+      do
+        offset++;
+      while (offset < n_attrs - 1 && !attrs[offset].is_sentence_end);
+
+      count--;
+    }
+  while (count < 0 && offset > 0)
+    {
+      do
+        offset--;
+      while (offset > 0 && !attrs[offset].is_sentence_start);
+
+      count++;
+    }
+
+  return offset;
+}
+
+gint
+_gtk_pango_move_lines (PangoLayout *layout,
+                       gint         offset,
+                       gint         count)
+{
+  GSList *lines, *l;
+  PangoLayoutLine *line;
+  gint num;
+  const gchar *text;
+  gint pos, line_pos;
+  gint index;
+  gint len;
+
+  text = pango_layout_get_text (layout);
+  index = g_utf8_offset_to_pointer (text, offset) - text;
+  lines = pango_layout_get_lines (layout);
+
+  num = 0;
+  for (l = lines; l; l = l->next)
+    {
+      line = l->data;
+      if (index < line->start_index + line->length)
+        break;
+      num++;
+    }
+
+  if (count < 0)
+    {
+      num += count;
+      if (num < 0)
+        num = 0;
+
+      line = g_slist_nth_data (lines, num);
+
+      return g_utf8_pointer_to_offset (text, text + line->start_index);
+    }
+  else
+    {
+      line_pos = index - line->start_index;
+
+      len = g_slist_length (lines);
+      num += count;
+      if (num >= len || (count == 0 && num == len - 1))
+        return g_utf8_strlen (text, -1) - 1;
+
+      line = l->data;
+      pos = line->start_index + line_pos;
+      if (pos >= line->start_index + line->length)
+        pos = line->start_index + line->length - 1;
+
+      return g_utf8_pointer_to_offset (text, text + pos);
+    }
+}
+
+gboolean
+_gtk_pango_is_inside_word (PangoLayout  *layout,
+                           PangoLogAttr *attrs,
+                           gint          n_attrs,
+                           gint          offset)
+{
+  while (offset >= 0 &&
+         !(attrs[offset].is_word_start || attrs[offset].is_word_end))
+    offset--;
+
+  if (offset >= 0)
+    return attrs[offset].is_word_start;
+
+  return FALSE;
+}
+
+gboolean
+_gtk_pango_is_inside_sentence (PangoLayout  *layout,
+                               PangoLogAttr *attrs,
+                               gint          n_attrs,
+                               gint          offset)
+{
+  while (offset >= 0 &&
+         !(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end))
+    offset--;
+
+  if (offset >= 0)
+    return attrs[offset].is_sentence_start;
+
+  return FALSE;
+}
+
+static void
+pango_layout_get_line_before (PangoLayout     *layout,
+                              AtkTextBoundary  boundary_type,
+                              gint             offset,
+                              gint            *start_offset,
+                              gint            *end_offset)
+{
+  PangoLayoutIter *iter;
+  PangoLayoutLine *line, *prev_line = NULL, *prev_prev_line = NULL;
+  gint index, start_index, end_index;
+  const gchar *text;
+  gboolean found = FALSE;
+
+  text = pango_layout_get_text (layout);
+  index = g_utf8_offset_to_pointer (text, offset) - text;
+  iter = pango_layout_get_iter (layout);
+  do
+    {
+      line = pango_layout_iter_get_line (iter);
+      start_index = line->start_index;
+      end_index = start_index + line->length;
+
+      if (index >= start_index && index <= end_index)
+        {
+          /* Found line for offset */
+          if (prev_line)
+            {
+              switch (boundary_type)
+                {
+                case ATK_TEXT_BOUNDARY_LINE_START:
+                  end_index = start_index;
+                  start_index = prev_line->start_index;
+                  break;
+                case ATK_TEXT_BOUNDARY_LINE_END:
+                  if (prev_prev_line)
+                    start_index = prev_prev_line->start_index + prev_prev_line->length;
+                  end_index = prev_line->start_index + prev_line->length;
+                  break;
+                default:
+                  g_assert_not_reached();
+                }
+            }
+          else
+            start_index = end_index = 0;
+
+          found = TRUE;
+          break;
+        }
+
+      prev_prev_line = prev_line;
+      prev_line = line;
+    }
+  while (pango_layout_iter_next_line (iter));
+
+  if (!found)
+    {
+      start_index = prev_line->start_index + prev_line->length;
+      end_index = start_index;
+    }
+  pango_layout_iter_free (iter);
+
+  *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
+  *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
+}
+
+static void
+pango_layout_get_line_at (PangoLayout     *layout,
+                          AtkTextBoundary  boundary_type,
+                          gint             offset,
+                          gint            *start_offset,
+                          gint            *end_offset)
+{
+  PangoLayoutIter *iter;
+  PangoLayoutLine *line, *prev_line = NULL;
+  gint index, start_index, end_index;
+  const gchar *text;
+  gboolean found = FALSE;
+
+  text = pango_layout_get_text (layout);
+  index = g_utf8_offset_to_pointer (text, offset) - text;
+  iter = pango_layout_get_iter (layout);
+  do
+    {
+      line = pango_layout_iter_get_line (iter);
+      start_index = line->start_index;
+      end_index = start_index + line->length;
+
+      if (index >= start_index && index <= end_index)
+        {
+          /* Found line for offset */
+          switch (boundary_type)
+            {
+            case ATK_TEXT_BOUNDARY_LINE_START:
+              if (pango_layout_iter_next_line (iter))
+                end_index = pango_layout_iter_get_line (iter)->start_index;
+              break;
+            case ATK_TEXT_BOUNDARY_LINE_END:
+              if (prev_line)
+                start_index = prev_line->start_index + prev_line->length;
+              break;
+            default:
+              g_assert_not_reached();
+            }
+
+          found = TRUE;
+          break;
+        }
+
+      prev_line = line;
+    }
+  while (pango_layout_iter_next_line (iter));
+
+  if (!found)
+    {
+      start_index = prev_line->start_index + prev_line->length;
+      end_index = start_index;
+    }
+  pango_layout_iter_free (iter);
+
+  *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
+  *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
+}
+
+static void
+pango_layout_get_line_after (PangoLayout     *layout,
+                             AtkTextBoundary  boundary_type,
+                             gint             offset,
+                             gint            *start_offset,
+                             gint            *end_offset)
+{
+  PangoLayoutIter *iter;
+  PangoLayoutLine *line, *prev_line = NULL;
+  gint index, start_index, end_index;
+  const gchar *text;
+  gboolean found = FALSE;
+
+  text = pango_layout_get_text (layout);
+  index = g_utf8_offset_to_pointer (text, offset) - text;
+  iter = pango_layout_get_iter (layout);
+  do
+    {
+      line = pango_layout_iter_get_line (iter);
+      start_index = line->start_index;
+      end_index = start_index + line->length;
+
+      if (index >= start_index && index <= end_index)
+        {
+          /* Found line for offset */
+          if (pango_layout_iter_next_line (iter))
+            {
+              line = pango_layout_iter_get_line (iter);
+              switch (boundary_type)
+                {
+                case ATK_TEXT_BOUNDARY_LINE_START:
+                  start_index = line->start_index;
+                  if (pango_layout_iter_next_line (iter))
+                    end_index = pango_layout_iter_get_line (iter)->start_index;
+                  else
+                    end_index = start_index + line->length;
+                  break;
+                case ATK_TEXT_BOUNDARY_LINE_END:
+                  start_index = end_index;
+                  end_index = line->start_index + line->length;
+                  break;
+                default:
+                  g_assert_not_reached();
+                }
+            }
+          else
+            start_index = end_index;
+
+          found = TRUE;
+          break;
+        }
+
+      prev_line = line;
+    }
+  while (pango_layout_iter_next_line (iter));
+
+  if (!found)
+    {
+      start_index = prev_line->start_index + prev_line->length;
+      end_index = start_index;
+    }
+  pango_layout_iter_free (iter);
+
+  *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
+  *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
+}
+
+gchar *
+_gtk_pango_get_text_before (PangoLayout     *layout,
+                            AtkTextBoundary  boundary_type,
+                            gint             offset,
+                            gint            *start_offset,
+                            gint            *end_offset)
+{
+  const gchar *text;
+  PangoLogAttr *attrs;
+  gint n_attrs;
+  gint start, end;
+
+  text = pango_layout_get_text (layout);
+
+  if (text[0] == 0)
+    {
+      *start_offset = 0;
+      *end_offset = 0;
+      return g_strdup ("");
+    }
+
+  pango_layout_get_log_attrs (layout, &attrs, &n_attrs);
+
+  start = offset;
+  end = start;
+
+  switch (boundary_type)
+    {
+    case ATK_TEXT_BOUNDARY_CHAR:
+      start = _gtk_pango_move_chars (layout, attrs, n_attrs, start, -1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_WORD_START:
+      if (!attrs[start].is_word_start)
+        start = _gtk_pango_move_words (layout, attrs, n_attrs, start, -1);
+      end = start;
+      start = _gtk_pango_move_words (layout, attrs, n_attrs, start, -1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_WORD_END:
+      if (_gtk_pango_is_inside_word (layout, attrs, n_attrs, start) &&
+          !attrs[start].is_word_start)
+        start = _gtk_pango_move_words (layout, attrs, n_attrs, start, -1);
+      while (!attrs[start].is_word_end && start > 0)
+        start = _gtk_pango_move_chars (layout, attrs, n_attrs, start, -1);
+      end = start;
+      start = _gtk_pango_move_words (layout, attrs, n_attrs, start, -1);
+      while (!attrs[start].is_word_end && start > 0)
+        start = _gtk_pango_move_chars (layout, attrs, n_attrs, start, -1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_SENTENCE_START:
+      if (!attrs[start].is_sentence_start)
+        start = _gtk_pango_move_sentences (layout, attrs, n_attrs, start, -1);
+      end = start;
+      start = _gtk_pango_move_sentences (layout, attrs, n_attrs, start, -1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_SENTENCE_END:
+      if (_gtk_pango_is_inside_sentence (layout, attrs, n_attrs, start) &&
+          !attrs[start].is_sentence_start)
+        start = _gtk_pango_move_sentences (layout, attrs, n_attrs, start, -1);
+      while (!attrs[start].is_sentence_end && start > 0)
+        start = _gtk_pango_move_chars (layout, attrs, n_attrs, start, -1);
+      end = start;
+      start = _gtk_pango_move_sentences (layout, attrs, n_attrs, start, -1);
+      while (!attrs[start].is_sentence_end && start > 0)
+        start = _gtk_pango_move_chars (layout, attrs, n_attrs, start, -1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_LINE_START:
+    case ATK_TEXT_BOUNDARY_LINE_END:
+      pango_layout_get_line_before (layout, boundary_type, offset, &start, &end);
+      break;
+    }
+
+  *start_offset = start;
+  *end_offset = end;
+
+  g_free (attrs);
+
+  return _g_utf8_substring (text, start, end);
+}
+
+gchar *
+_gtk_pango_get_text_after (PangoLayout     *layout,
+                           AtkTextBoundary  boundary_type,
+                           gint             offset,
+                           gint            *start_offset,
+                           gint            *end_offset)
+{
+  const gchar *text;
+  PangoLogAttr *attrs;
+  gint n_attrs;
+  gint start, end;
+
+  text = pango_layout_get_text (layout);
+
+  if (text[0] == 0)
+    {
+      *start_offset = 0;
+      *end_offset = 0;
+      return g_strdup ("");
+    }
+
+  pango_layout_get_log_attrs (layout, &attrs, &n_attrs);
+
+  start = offset;
+  end = start;
+
+  switch (boundary_type)
+    {
+    case ATK_TEXT_BOUNDARY_CHAR:
+      start = _gtk_pango_move_chars (layout, attrs, n_attrs, start, 1);
+      end = start;
+      end = _gtk_pango_move_chars (layout, attrs, n_attrs, end, 1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_WORD_START:
+      if (_gtk_pango_is_inside_word (layout, attrs, n_attrs, end))
+        end = _gtk_pango_move_words (layout, attrs, n_attrs, end, 1);
+      while (!attrs[end].is_word_start && end < n_attrs - 1)
+        end = _gtk_pango_move_chars (layout, attrs, n_attrs, end, 1);
+      start = end;
+      if (end < n_attrs - 1)
+        {
+          end = _gtk_pango_move_words (layout, attrs, n_attrs, end, 1);
+          while (!attrs[end].is_word_end && end < n_attrs - 1)
+            end = _gtk_pango_move_chars (layout, attrs, n_attrs, end, 1);
+        }
+      break;
+
+    case ATK_TEXT_BOUNDARY_WORD_END:
+      end = _gtk_pango_move_words (layout, attrs, n_attrs, end, 1);
+      start = end;
+      if (end < n_attrs - 1)
+        end = _gtk_pango_move_words (layout, attrs, n_attrs, end, 1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_SENTENCE_START:
+      if (_gtk_pango_is_inside_sentence (layout, attrs, n_attrs, end))
+        end = _gtk_pango_move_sentences (layout, attrs, n_attrs, end, 1);
+      while (!attrs[end].is_sentence_end && end < n_attrs - 1)
+        end = _gtk_pango_move_chars (layout, attrs, n_attrs, end, 1);
+      start = end;
+      if (end < n_attrs - 1)
+        {
+          end = _gtk_pango_move_sentences (layout, attrs, n_attrs, end, 1);
+          while (!attrs[end].is_sentence_start && end < n_attrs - 1)
+            end = _gtk_pango_move_chars (layout, attrs, n_attrs, end, 1);
+        }
+      break;
+
+    case ATK_TEXT_BOUNDARY_SENTENCE_END:
+      end = _gtk_pango_move_sentences (layout, attrs, n_attrs, end, 1);
+      start = end;
+      if (end < n_attrs - 1)
+        end = _gtk_pango_move_sentences (layout, attrs, n_attrs, end, 1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_LINE_START:
+    case ATK_TEXT_BOUNDARY_LINE_END:
+      pango_layout_get_line_after (layout, boundary_type, offset, &start, &end);
+      break;
+    }
+
+  *start_offset = start;
+  *end_offset = end;
+
+  g_free (attrs);
+
+  return _g_utf8_substring (text, start, end);
+}
+
+gchar *
+_gtk_pango_get_text_at (PangoLayout     *layout,
+                        AtkTextBoundary  boundary_type,
+                        gint             offset,
+                        gint            *start_offset,
+                        gint            *end_offset)
+{
+  const gchar *text;
+  PangoLogAttr *attrs;
+  gint n_attrs;
+  gint start, end;
+
+  text = pango_layout_get_text (layout);
+
+  if (text[0] == 0)
+    {
+      *start_offset = 0;
+      *end_offset = 0;
+      return g_strdup ("");
+    }
+
+  pango_layout_get_log_attrs (layout, &attrs, &n_attrs);
+
+  start = offset;
+  end = start;
+
+  switch (boundary_type)
+    {
+    case ATK_TEXT_BOUNDARY_CHAR:
+      start = _gtk_pango_move_chars (layout, attrs, n_attrs, start, 1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_WORD_START:
+      if (!attrs[start].is_word_start)
+        start = _gtk_pango_move_words (layout, attrs, n_attrs, start, -1);
+      if (_gtk_pango_is_inside_word (layout, attrs, n_attrs, end))
+        end = _gtk_pango_move_words (layout, attrs, n_attrs, end, 1);
+      while (!attrs[end].is_word_start && end < n_attrs - 1)
+        end = _gtk_pango_move_chars (layout, attrs, n_attrs, end, -1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_WORD_END:
+      if (_gtk_pango_is_inside_word (layout, attrs, n_attrs, start) &&
+          !attrs[start].is_word_start)
+        start = _gtk_pango_move_words (layout, attrs, n_attrs, start, -1);
+      while (!attrs[start].is_word_end && start > 0)
+        start = _gtk_pango_move_chars (layout, attrs, n_attrs, start, -1);
+      end = _gtk_pango_move_words (layout, attrs, n_attrs, end, 1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_SENTENCE_START:
+      if (!attrs[start].is_sentence_start)
+        start = _gtk_pango_move_sentences (layout, attrs, n_attrs, start, -1);
+      if (_gtk_pango_is_inside_sentence (layout, attrs, n_attrs, end))
+        end = _gtk_pango_move_sentences (layout, attrs, n_attrs, end, 1);
+      while (!attrs[end].is_word_end && end < n_attrs - 1)
+        end = _gtk_pango_move_chars (layout, attrs, n_attrs, end, 1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_SENTENCE_END:
+      if (_gtk_pango_is_inside_sentence (layout, attrs, n_attrs, start) &&
+          !attrs[start].is_sentence_start)
+        start = _gtk_pango_move_sentences (layout, attrs, n_attrs, start, -1);
+      while (!attrs[start].is_sentence_end && start > 0)
+        start = _gtk_pango_move_chars (layout, attrs, n_attrs, start, -1);
+      end = _gtk_pango_move_sentences (layout, attrs, n_attrs, end, 1);
+      break;
+
+    case ATK_TEXT_BOUNDARY_LINE_START:
+    case ATK_TEXT_BOUNDARY_LINE_END:
+      pango_layout_get_line_at (layout, boundary_type, offset, &start, &end);
+      break;
+    }
+
+  *start_offset = start;
+  *end_offset = end;
+
+  g_free (attrs);
+
+  return _g_utf8_substring (text, start, end);
+}
+
+/* FIXME: move to glib */
+gchar *
+_g_utf8_substring (const gchar *str,
+                   glong        start_pos,
+                   glong        end_pos)
+{
+  gchar *start, *end;
+  gchar *out;
+
+  start = g_utf8_offset_to_pointer (str, start_pos);
+  end = g_utf8_offset_to_pointer (start, end_pos - start_pos);
+
+  out = g_malloc (end - start + 1);
+  memcpy (out, start, end - start);
+  out[end - start] = 0;
+
+  return out;
+}
diff --git a/gtk/gtkpango.h b/gtk/gtkpango.h
index d02c7a6..9848cac 100644
--- a/gtk/gtkpango.h
+++ b/gtk/gtkpango.h
@@ -33,14 +33,71 @@
 
 
 #include <pango/pangocairo.h>
-
+#include <atk/atk.h>
 
 G_BEGIN_DECLS
 
-void
-_gtk_pango_fill_layout (cairo_t     *cr,
-                        PangoLayout *layout);
+void _gtk_pango_fill_layout (cairo_t     *cr,
+                             PangoLayout *layout);
+
+
+AtkAttributeSet* _gtk_pango_get_default_attributes (AtkAttributeSet   *attributes,
+                                                    PangoLayout       *layout);
+
+AtkAttributeSet* _gtk_pango_get_run_attributes (AtkAttributeSet *attributes,
+                                                PangoLayout     *layout,
+                                                gint             offset,
+                                                gint            *start_offset,
+                                                gint            *end_offset);
+
+gint _gtk_pango_move_chars     (PangoLayout  *layout,
+                                PangoLogAttr *attrs,
+                                gint          n_attrs,
+                                gint          offset,
+                                gint          count);
+gint _gtk_pango_move_words     (PangoLayout  *layout,
+                                PangoLogAttr *attrs,
+                                gint          n_attrs,
+                                gint          offset,
+                                gint          count);
+gint _gtk_pango_move_sentences (PangoLayout  *layout,
+                                PangoLogAttr *attrs,
+                                gint          n_attrs,
+                                gint          offset,
+                                gint          count);
+gint _gtk_pango_move_lines     (PangoLayout  *layout,
+                                gint          offset,
+                                gint          count);
+
+gboolean _gtk_pango_is_inside_word     (PangoLayout  *layout,
+                                        PangoLogAttr *attrs,
+                                        gint          n_attrs,
+                                        gint          offset);
+gboolean _gtk_pango_is_inside_sentence (PangoLayout  *layout,
+                                        PangoLogAttr *attrs,
+                                        gint          n_attrs,
+                                        gint          offset);
+
+
+gchar *_gtk_pango_get_text_before (PangoLayout     *layout,
+                                   AtkTextBoundary  boundary_type,
+                                   gint             offset,
+                                   gint            *start_offset,
+                                   gint            *end_offset);
+gchar *_gtk_pango_get_text_at     (PangoLayout     *layout,
+                                   AtkTextBoundary  boundary_type,
+                                   gint             offset,
+                                   gint            *start_offset,
+                                   gint            *end_offset);
+gchar *_gtk_pango_get_text_after  (PangoLayout     *layout,
+                                   AtkTextBoundary  boundary_type,
+                                   gint             offset,
+                                   gint            *start_offset,
+                                   gint            *end_offset);
 
+gchar * _g_utf8_substring (const gchar *str,
+                           glong        start_pos,
+                           glong        end_pos);
 
 G_END_DECLS
 
diff --git a/gtk/gtkstylecontext.c b/gtk/gtkstylecontext.c
index ca0c96e..664daf3 100644
--- a/gtk/gtkstylecontext.c
+++ b/gtk/gtkstylecontext.c
@@ -4395,3 +4395,58 @@ gtk_render_icon_pixbuf (GtkStyleContext     *context,
   _gtk_theming_engine_set_context (priv->theming_engine, context);
   return engine_class->render_icon_pixbuf (priv->theming_engine, source, size);
 }
+
+static AtkAttributeSet *
+add_attribute (AtkAttributeSet  *attributes,
+               AtkTextAttribute  attr,
+               const gchar      *value)
+{
+  AtkAttribute *at;
+
+  at = g_new (AtkAttribute, 1);
+  at->name = g_strdup (atk_text_attribute_get_name (attr));
+  at->value = g_strdup (value);
+
+  return g_slist_prepend (attributes, at);
+}
+
+/**
+ * _gtk_style_context_get_attributes:
+ * @attributes: a #AtkAttributeSet to add attributes to
+ * @context: the #GtkStyleContext to get attributes from
+ * @flags: the state to use with @context
+ *
+ * Adds the foreground and background color from @context to
+ * @attributes, after translating them to ATK attributes.
+ *
+ * This is a convenience function that can be used in
+ * implementing the #AtkText interface in widgets.
+ *
+ * Returns: the modified #AtkAttributeSet
+ */
+AtkAttributeSet *
+_gtk_style_context_get_attributes (AtkAttributeSet *attributes,
+                                   GtkStyleContext *context,
+                                   GtkStateFlags    flags)
+{
+  GdkRGBA color;
+  gchar *value;
+
+  gtk_style_context_get_background_color (context, flags, &color);
+  value = g_strdup_printf ("%u,%u,%u",
+                           (guint) ceil (color.red * 65536 - color.red),
+                           (guint) ceil (color.green * 65536 - color.green),
+                           (guint) ceil (color.blue * 65536 - color.blue));
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
+  g_free (value);
+
+  gtk_style_context_get_color (context, flags, &color);
+  value = g_strdup_printf ("%u,%u,%u",
+                           (guint) ceil (color.red * 65536 - color.red),
+                           (guint) ceil (color.green * 65536 - color.green),
+                           (guint) ceil (color.blue * 65536 - color.blue));
+  attributes = add_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
+  g_free (value);
+
+  return attributes;
+}
diff --git a/gtk/gtkstylecontext.h b/gtk/gtkstylecontext.h
index e687ff1..fc5b584 100644
--- a/gtk/gtkstylecontext.h
+++ b/gtk/gtkstylecontext.h
@@ -28,6 +28,7 @@
 #include <gtk/gtkstyleprovider.h>
 #include <gtk/gtkwidgetpath.h>
 #include <gtk/gtkborder.h>
+#include <atk/atk.h>
 
 G_BEGIN_DECLS
 
@@ -746,6 +747,11 @@ GdkPixbuf * gtk_render_icon_pixbuf (GtkStyleContext     *context,
                                     const GtkIconSource *source,
                                     GtkIconSize          size);
 
+/* Accessibility support */
+AtkAttributeSet *_gtk_style_context_get_attributes (AtkAttributeSet *attributes,
+                                                    GtkStyleContext *context,
+                                                    GtkStateFlags    flags);
+
 G_END_DECLS
 
 #endif /* __GTK_STYLE_CONTEXT_H__ */
diff --git a/modules/other/gail/gail.c b/modules/other/gail/gail.c
index 0345655..78eda3b 100644
--- a/modules/other/gail/gail.c
+++ b/modules/other/gail/gail.c
@@ -31,6 +31,7 @@
 #include <gtk/gtkcontaineraccessible.h>
 #include <gtk/gtkframeaccessible.h>
 #include <gtk/gtkimageaccessible.h>
+#include <gtk/gtklabelaccessible.h>
 #include <gtk/gtkpanedaccessible.h>
 #include <gtk/gtkprogressbaraccessible.h>
 #include <gtk/gtkrangeaccessible.h>
@@ -98,7 +99,6 @@ GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_MENU, GailMenu, gail_menu, GTK_TYPE_MENU)
 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_WINDOW, GailWindow, gail_window, GTK_TYPE_BIN)
 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_SCALE, GailScale, gail_scale, GTK_TYPE_SCALE)
 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_SCALE_BUTTON, GailScaleButton, gail_scale_button, GTK_TYPE_SCALE_BUTTON)
-GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_LABEL, GailLabel, gail_label, GTK_TYPE_LABEL)
 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_STATUSBAR, GailStatusbar, gail_statusbar, GTK_TYPE_STATUSBAR)
 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_NOTEBOOK, GailNotebook, gail_notebook, GTK_TYPE_NOTEBOOK)
 GAIL_IMPLEMENT_FACTORY (GAIL_TYPE_CALENDAR, GailCalendar, gail_calendar, GTK_TYPE_CALENDAR)
@@ -926,6 +926,9 @@ gail_accessibility_module_init (void)
   atk_registry_set_factory_type (atk_get_default_registry (),
                                  GTK_TYPE_COMBO_BOX,
                                  gtk_combo_box_accessible_factory_get_type ());
+  atk_registry_set_factory_type (atk_get_default_registry (),
+                                 GTK_TYPE_LABEL,
+                                 gtk_label_accessible_factory_get_type ());
   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_BUTTON, gail_button);
   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_LINK_BUTTON, gail_link_button);
   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_MENU_ITEM, gail_menu_item);
@@ -937,7 +940,6 @@ gail_accessibility_module_init (void)
   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_WINDOW, gail_window);
   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SCALE, gail_scale);
   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SCALE_BUTTON, gail_scale_button);
-  GAIL_WIDGET_SET_FACTORY (GTK_TYPE_LABEL, gail_label);
   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_STATUSBAR, gail_statusbar);
   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_NOTEBOOK, gail_notebook);
   GAIL_WIDGET_SET_FACTORY (GTK_TYPE_SPIN_BUTTON, gail_spin_button);



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