[gtk+/drop-gail: 24/24] Move GailLabel to GtkLabelAccessible
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/drop-gail: 24/24] Move GailLabel to GtkLabelAccessible
- Date: Sun, 15 May 2011 02:14:06 +0000 (UTC)
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]