 48c6b3b4f4
			
		
	
	48c6b3b4f4
	
	
	
		
			
			I'm adding a bunch of fixes for gcc complaining about -Wmissing-declarations. This set of patches makes private classes in gtk/*.c that use G_DEFINE_TYPE() safe by adding definitions for the get_type() function that can't be made static.
		
			
				
	
	
		
			1292 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1292 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* gtkpango.c - pango-related utilities
 | |
|  *
 | |
|  * Copyright (c) 2010 Red Hat, Inc.
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
 | |
|  */
 | |
| /*
 | |
|  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 | |
|  * file for a list of people on the GTK+ Team.  See the ChangeLog
 | |
|  * files for a list of changes.  These files are distributed with
 | |
|  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| #include "gtkpango.h"
 | |
| #include <pango/pangocairo.h>
 | |
| #include "gtkintl.h"
 | |
| 
 | |
| #define GTK_TYPE_FILL_LAYOUT_RENDERER            (_gtk_fill_layout_renderer_get_type())
 | |
| #define GTK_FILL_LAYOUT_RENDERER(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_FILL_LAYOUT_RENDERER, GtkFillLayoutRenderer))
 | |
| #define GTK_IS_FILL_LAYOUT_RENDERER(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_FILL_LAYOUT_RENDERER))
 | |
| #define GTK_FILL_LAYOUT_RENDERER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILL_LAYOUT_RENDERER, GtkFillLayoutRendererClass))
 | |
| #define GTK_IS_FILL_LAYOUT_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILL_LAYOUT_RENDERER))
 | |
| #define GTK_FILL_LAYOUT_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILL_LAYOUT_RENDERER, GtkFillLayoutRendererClass))
 | |
| 
 | |
| typedef struct _GtkFillLayoutRenderer      GtkFillLayoutRenderer;
 | |
| typedef struct _GtkFillLayoutRendererClass GtkFillLayoutRendererClass;
 | |
| 
 | |
| struct _GtkFillLayoutRenderer
 | |
| {
 | |
|   PangoRenderer parent_instance;
 | |
| 
 | |
|   cairo_t *cr;
 | |
| };
 | |
| 
 | |
| struct _GtkFillLayoutRendererClass
 | |
| {
 | |
|   PangoRendererClass parent_class;
 | |
| };
 | |
| 
 | |
| GType _gtk_fill_layout_renderer_get_type (void);
 | |
| 
 | |
| G_DEFINE_TYPE (GtkFillLayoutRenderer, _gtk_fill_layout_renderer, PANGO_TYPE_RENDERER)
 | |
| 
 | |
| static void
 | |
| gtk_fill_layout_renderer_draw_glyphs (PangoRenderer     *renderer,
 | |
|                                       PangoFont         *font,
 | |
|                                       PangoGlyphString  *glyphs,
 | |
|                                       int                x,
 | |
|                                       int                y)
 | |
| {
 | |
|   GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
 | |
| 
 | |
|   cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
 | |
|   pango_cairo_show_glyph_string (text_renderer->cr, font, glyphs);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_fill_layout_renderer_draw_glyph_item (PangoRenderer     *renderer,
 | |
|                                           const char        *text,
 | |
|                                           PangoGlyphItem    *glyph_item,
 | |
|                                           int                x,
 | |
|                                           int                y)
 | |
| {
 | |
|   GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
 | |
| 
 | |
|   cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
 | |
|   pango_cairo_show_glyph_item (text_renderer->cr, text, glyph_item);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_fill_layout_renderer_draw_rectangle (PangoRenderer     *renderer,
 | |
|                                          PangoRenderPart    part,
 | |
|                                          int                x,
 | |
|                                          int                y,
 | |
|                                          int                width,
 | |
|                                          int                height)
 | |
| {
 | |
|   GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
 | |
| 
 | |
|   if (part == PANGO_RENDER_PART_BACKGROUND)
 | |
|     return;
 | |
| 
 | |
|   cairo_rectangle (text_renderer->cr,
 | |
|                    (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
 | |
| 		   (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
 | |
|   cairo_fill (text_renderer->cr);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_fill_layout_renderer_draw_trapezoid (PangoRenderer     *renderer,
 | |
|                                          PangoRenderPart    part,
 | |
|                                          double             y1_,
 | |
|                                          double             x11,
 | |
|                                          double             x21,
 | |
|                                          double             y2,
 | |
|                                          double             x12,
 | |
|                                          double             x22)
 | |
| {
 | |
|   GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
 | |
|   cairo_matrix_t matrix;
 | |
|   cairo_t *cr;
 | |
| 
 | |
|   cr = text_renderer->cr;
 | |
| 
 | |
|   cairo_save (cr);
 | |
| 
 | |
|   /* use identity scale, but keep translation */
 | |
|   cairo_get_matrix (cr, &matrix);
 | |
|   matrix.xx = matrix.yy = 1;
 | |
|   matrix.xy = matrix.yx = 0;
 | |
|   cairo_set_matrix (cr, &matrix);
 | |
| 
 | |
|   cairo_move_to (cr, x11, y1_);
 | |
|   cairo_line_to (cr, x21, y1_);
 | |
|   cairo_line_to (cr, x22, y2);
 | |
|   cairo_line_to (cr, x12, y2);
 | |
|   cairo_close_path (cr);
 | |
| 
 | |
|   cairo_fill (cr);
 | |
| 
 | |
|   cairo_restore (cr);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_fill_layout_renderer_draw_error_underline (PangoRenderer *renderer,
 | |
|                                                int            x,
 | |
|                                                int            y,
 | |
|                                                int            width,
 | |
|                                                int            height)
 | |
| {
 | |
|   GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
 | |
| 
 | |
|   pango_cairo_show_error_underline (text_renderer->cr,
 | |
|                                     (double)x / PANGO_SCALE, (double)y / PANGO_SCALE,
 | |
|                                     (double)width / PANGO_SCALE, (double)height / PANGO_SCALE);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_fill_layout_renderer_draw_shape (PangoRenderer   *renderer,
 | |
|                                      PangoAttrShape  *attr,
 | |
|                                      int              x,
 | |
|                                      int              y)
 | |
| {
 | |
|   GtkFillLayoutRenderer *text_renderer = GTK_FILL_LAYOUT_RENDERER (renderer);
 | |
|   cairo_t *cr = text_renderer->cr;
 | |
|   PangoLayout *layout;
 | |
|   PangoCairoShapeRendererFunc shape_renderer;
 | |
|   gpointer                    shape_renderer_data;
 | |
| 
 | |
|   layout = pango_renderer_get_layout (renderer);
 | |
| 
 | |
|   if (!layout)
 | |
|   	return;
 | |
| 
 | |
|   shape_renderer = pango_cairo_context_get_shape_renderer (pango_layout_get_context (layout),
 | |
| 							   &shape_renderer_data);
 | |
| 
 | |
|   if (!shape_renderer)
 | |
|     return;
 | |
| 
 | |
|   cairo_save (cr);
 | |
| 
 | |
|   cairo_move_to (cr, (double)x / PANGO_SCALE, (double)y / PANGO_SCALE);
 | |
| 
 | |
|   shape_renderer (cr, attr, FALSE, shape_renderer_data);
 | |
| 
 | |
|   cairo_restore (cr);
 | |
| }
 | |
| 
 | |
| static void
 | |
| gtk_fill_layout_renderer_finalize (GObject *object)
 | |
| {
 | |
|   G_OBJECT_CLASS (_gtk_fill_layout_renderer_parent_class)->finalize (object);
 | |
| }
 | |
| 
 | |
| static void
 | |
| _gtk_fill_layout_renderer_init (GtkFillLayoutRenderer *renderer)
 | |
| {
 | |
| }
 | |
| 
 | |
| static void
 | |
| _gtk_fill_layout_renderer_class_init (GtkFillLayoutRendererClass *klass)
 | |
| {
 | |
|   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 | |
|   
 | |
|   PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass);
 | |
|   
 | |
|   renderer_class->draw_glyphs = gtk_fill_layout_renderer_draw_glyphs;
 | |
|   renderer_class->draw_glyph_item = gtk_fill_layout_renderer_draw_glyph_item;
 | |
|   renderer_class->draw_rectangle = gtk_fill_layout_renderer_draw_rectangle;
 | |
|   renderer_class->draw_trapezoid = gtk_fill_layout_renderer_draw_trapezoid;
 | |
|   renderer_class->draw_error_underline = gtk_fill_layout_renderer_draw_error_underline;
 | |
|   renderer_class->draw_shape = gtk_fill_layout_renderer_draw_shape;
 | |
| 
 | |
|   object_class->finalize = gtk_fill_layout_renderer_finalize;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gtk_pango_fill_layout (cairo_t     *cr,
 | |
|                         PangoLayout *layout)
 | |
| {
 | |
|   static GtkFillLayoutRenderer *renderer = NULL;
 | |
|   gboolean has_current_point;
 | |
|   double current_x, current_y;
 | |
| 
 | |
|   has_current_point = cairo_has_current_point (cr);
 | |
|   cairo_get_current_point (cr, ¤t_x, ¤t_y);
 | |
| 
 | |
|   if (renderer == NULL)
 | |
|     renderer = g_object_new (GTK_TYPE_FILL_LAYOUT_RENDERER, NULL);
 | |
| 
 | |
|   cairo_save (cr);
 | |
|   cairo_translate (cr, current_x, current_y);
 | |
| 
 | |
|   renderer->cr = cr;
 | |
|   pango_renderer_draw_layout (PANGO_RENDERER (renderer), layout, 0, 0);
 | |
| 
 | |
|   cairo_restore (cr);
 | |
| 
 | |
|   if (has_current_point)
 | |
|     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 using Pango
 | |
|  * layouts.
 | |
|  *
 | |
|  * 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   /* 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   /* 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");
 | |
|   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 (i.e. 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 using Pango
 | |
|  * layouts.
 | |
|  *
 | |
|  * 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;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _gtk_pango_move_chars:
 | |
|  * @layout: a #PangoLayout
 | |
|  * @offset: a character offset in @layout
 | |
|  * @count: the number of characters to move from @offset
 | |
|  *
 | |
|  * Returns the position that is @count characters from the
 | |
|  * given @offset. @count may be positive or negative.
 | |
|  *
 | |
|  * For the purpose of this function, characters are defined
 | |
|  * by what Pango considers cursor positions.
 | |
|  *
 | |
|  * Returns: the new position
 | |
|  */
 | |
| gint
 | |
| _gtk_pango_move_chars (PangoLayout *layout,
 | |
|                        gint         offset,
 | |
|                        gint         count)
 | |
| {
 | |
|   const PangoLogAttr *attrs;
 | |
|   gint n_attrs;
 | |
| 
 | |
|   attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _gtk_pango_move_words:
 | |
|  * @layout: a #PangoLayout
 | |
|  * @offset: a character offset in @layout
 | |
|  * @count: the number of words to move from @offset
 | |
|  *
 | |
|  * Returns the position that is @count words from the
 | |
|  * given @offset. @count may be positive or negative.
 | |
|  *
 | |
|  * If @count is positive, the returned position will
 | |
|  * be a word end, otherwise it will be a word start.
 | |
|  * See the Pango documentation for details on how
 | |
|  * word starts and ends are defined.
 | |
|  *
 | |
|  * Returns: the new position
 | |
|  */
 | |
| gint
 | |
| _gtk_pango_move_words (PangoLayout  *layout,
 | |
|                        gint          offset,
 | |
|                        gint          count)
 | |
| {
 | |
|   const PangoLogAttr *attrs;
 | |
|   gint n_attrs;
 | |
| 
 | |
|   attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _gtk_pango_move_sentences:
 | |
|  * @layout: a #PangoLayout
 | |
|  * @offset: a character offset in @layout
 | |
|  * @count: the number of sentences to move from @offset
 | |
|  *
 | |
|  * Returns the position that is @count sentences from the
 | |
|  * given @offset. @count may be positive or negative.
 | |
|  *
 | |
|  * If @count is positive, the returned position will
 | |
|  * be a sentence end, otherwise it will be a sentence start.
 | |
|  * See the Pango documentation for details on how
 | |
|  * sentence starts and ends are defined.
 | |
|  *
 | |
|  * Returns: the new position
 | |
|  */
 | |
| gint
 | |
| _gtk_pango_move_sentences (PangoLayout  *layout,
 | |
|                            gint          offset,
 | |
|                            gint          count)
 | |
| {
 | |
|   const PangoLogAttr *attrs;
 | |
|   gint n_attrs;
 | |
| 
 | |
|   attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _gtk_pango_move_lines:
 | |
|  * @layout: a #PangoLayout
 | |
|  * @offset: a character offset in @layout
 | |
|  * @count: the number of lines to move from @offset
 | |
|  *
 | |
|  * Returns the position that is @count lines from the
 | |
|  * given @offset. @count may be positive or negative.
 | |
|  *
 | |
|  * If @count is negative, the returned position will
 | |
|  * be the start of a line, else it will be the end of
 | |
|  * line.
 | |
|  *
 | |
|  * Returns: the new position
 | |
|  */
 | |
| 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);
 | |
|   line = NULL;
 | |
| 
 | |
|   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);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _gtk_pango_is_inside_word:
 | |
|  * @layout: a #PangoLayout
 | |
|  * @offset: a character offset in @layout
 | |
|  *
 | |
|  * Returns whether the given position is inside
 | |
|  * a word.
 | |
|  *
 | |
|  * Returns: %TRUE if @offset is inside a word
 | |
|  */
 | |
| gboolean
 | |
| _gtk_pango_is_inside_word (PangoLayout  *layout,
 | |
|                            gint          offset)
 | |
| {
 | |
|   const PangoLogAttr *attrs;
 | |
|   gint n_attrs;
 | |
| 
 | |
|   attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _gtk_pango_is_inside_sentence:
 | |
|  * @layout: a #PangoLayout
 | |
|  * @offset: a character offset in @layout
 | |
|  *
 | |
|  * Returns whether the given position is inside
 | |
|  * a sentence.
 | |
|  *
 | |
|  * Returns: %TRUE if @offset is inside a sentence
 | |
|  */
 | |
| gboolean
 | |
| _gtk_pango_is_inside_sentence (PangoLayout  *layout,
 | |
|                                gint          offset)
 | |
| {
 | |
|   const PangoLogAttr *attrs;
 | |
|   gint n_attrs;
 | |
| 
 | |
|   attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
 | |
| 
 | |
|   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;
 | |
|                   else
 | |
|                     start_index = 0;
 | |
|                   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);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _gtk_pango_get_text_before:
 | |
|  * @layout: a #PangoLayout
 | |
|  * @boundary_type: a #AtkTextBoundary
 | |
|  * @offset: a character offset in @layout
 | |
|  * @start_offset: return location for the start of the returned text
 | |
|  * @end_offset: return location for the end of the return text
 | |
|  *
 | |
|  * Gets a slice of the text from @layout before @offset.
 | |
|  *
 | |
|  * The @boundary_type determines the size of the returned slice of
 | |
|  * text. For the exact semantics of this function, see
 | |
|  * atk_text_get_text_before_offset().
 | |
|  *
 | |
|  * Returns: a newly allocated string containing a slice of text
 | |
|  *     from layout. Free with g_free().
 | |
|  */
 | |
| gchar *
 | |
| _gtk_pango_get_text_before (PangoLayout     *layout,
 | |
|                             AtkTextBoundary  boundary_type,
 | |
|                             gint             offset,
 | |
|                             gint            *start_offset,
 | |
|                             gint            *end_offset)
 | |
| {
 | |
|   const gchar *text;
 | |
|   gint start, end;
 | |
|   const PangoLogAttr *attrs;
 | |
|   gint n_attrs;
 | |
| 
 | |
|   text = pango_layout_get_text (layout);
 | |
| 
 | |
|   if (text[0] == 0)
 | |
|     {
 | |
|       *start_offset = 0;
 | |
|       *end_offset = 0;
 | |
|       return g_strdup ("");
 | |
|     }
 | |
| 
 | |
|   attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
 | |
| 
 | |
|   start = offset;
 | |
|   end = start;
 | |
| 
 | |
|   switch (boundary_type)
 | |
|     {
 | |
|     case ATK_TEXT_BOUNDARY_CHAR:
 | |
|       start = _gtk_pango_move_chars (layout, start, -1);
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_WORD_START:
 | |
|       if (!attrs[start].is_word_start)
 | |
|         start = _gtk_pango_move_words (layout, start, -1);
 | |
|       end = start;
 | |
|       start = _gtk_pango_move_words (layout, start, -1);
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_WORD_END:
 | |
|       if (_gtk_pango_is_inside_word (layout, start) &&
 | |
|           !attrs[start].is_word_start)
 | |
|         start = _gtk_pango_move_words (layout, start, -1);
 | |
|       while (!attrs[start].is_word_end && start > 0)
 | |
|         start = _gtk_pango_move_chars (layout, start, -1);
 | |
|       end = start;
 | |
|       start = _gtk_pango_move_words (layout, start, -1);
 | |
|       while (!attrs[start].is_word_end && start > 0)
 | |
|         start = _gtk_pango_move_chars (layout, start, -1);
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_SENTENCE_START:
 | |
|       if (!attrs[start].is_sentence_start)
 | |
|         start = _gtk_pango_move_sentences (layout, start, -1);
 | |
|       end = start;
 | |
|       start = _gtk_pango_move_sentences (layout, start, -1);
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_SENTENCE_END:
 | |
|       if (_gtk_pango_is_inside_sentence (layout, start) &&
 | |
|           !attrs[start].is_sentence_start)
 | |
|         start = _gtk_pango_move_sentences (layout, start, -1);
 | |
|       while (!attrs[start].is_sentence_end && start > 0)
 | |
|         start = _gtk_pango_move_chars (layout, start, -1);
 | |
|       end = start;
 | |
|       start = _gtk_pango_move_sentences (layout, start, -1);
 | |
|       while (!attrs[start].is_sentence_end && start > 0)
 | |
|         start = _gtk_pango_move_chars (layout, 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_assert (start <= end);
 | |
| 
 | |
|   return g_utf8_substring (text, start, end);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _gtk_pango_get_text_after:
 | |
|  * @layout: a #PangoLayout
 | |
|  * @boundary_type: a #AtkTextBoundary
 | |
|  * @offset: a character offset in @layout
 | |
|  * @start_offset: return location for the start of the returned text
 | |
|  * @end_offset: return location for the end of the return text
 | |
|  *
 | |
|  * Gets a slice of the text from @layout after @offset.
 | |
|  *
 | |
|  * The @boundary_type determines the size of the returned slice of
 | |
|  * text. For the exact semantics of this function, see
 | |
|  * atk_text_get_text_after_offset().
 | |
|  *
 | |
|  * Returns: a newly allocated string containing a slice of text
 | |
|  *     from layout. Free with g_free().
 | |
|  */
 | |
| gchar *
 | |
| _gtk_pango_get_text_after (PangoLayout     *layout,
 | |
|                            AtkTextBoundary  boundary_type,
 | |
|                            gint             offset,
 | |
|                            gint            *start_offset,
 | |
|                            gint            *end_offset)
 | |
| {
 | |
|   const gchar *text;
 | |
|   gint start, end;
 | |
|   const PangoLogAttr *attrs;
 | |
|   gint n_attrs;
 | |
| 
 | |
|   text = pango_layout_get_text (layout);
 | |
| 
 | |
|   if (text[0] == 0)
 | |
|     {
 | |
|       *start_offset = 0;
 | |
|       *end_offset = 0;
 | |
|       return g_strdup ("");
 | |
|     }
 | |
| 
 | |
|   attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
 | |
| 
 | |
|   start = offset;
 | |
|   end = start;
 | |
| 
 | |
|   switch (boundary_type)
 | |
|     {
 | |
|     case ATK_TEXT_BOUNDARY_CHAR:
 | |
|       start = _gtk_pango_move_chars (layout, start, 1);
 | |
|       end = start;
 | |
|       end = _gtk_pango_move_chars (layout, end, 1);
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_WORD_START:
 | |
|       if (_gtk_pango_is_inside_word (layout, end))
 | |
|         end = _gtk_pango_move_words (layout, end, 1);
 | |
|       while (!attrs[end].is_word_start && end < n_attrs - 1)
 | |
|         end = _gtk_pango_move_chars (layout, end, 1);
 | |
|       start = end;
 | |
|       if (end < n_attrs - 1)
 | |
|         {
 | |
|           end = _gtk_pango_move_words (layout, end, 1);
 | |
|           while (!attrs[end].is_word_start && end < n_attrs - 1)
 | |
|             end = _gtk_pango_move_chars (layout, end, 1);
 | |
|         }
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_WORD_END:
 | |
|       end = _gtk_pango_move_words (layout, end, 1);
 | |
|       start = end;
 | |
|       if (end < n_attrs - 1)
 | |
|         end = _gtk_pango_move_words (layout, end, 1);
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_SENTENCE_START:
 | |
|       if (_gtk_pango_is_inside_sentence (layout, end))
 | |
|         end = _gtk_pango_move_sentences (layout, end, 1);
 | |
|       while (!attrs[end].is_sentence_start && end < n_attrs - 1)
 | |
|         end = _gtk_pango_move_chars (layout, end, 1);
 | |
|       start = end;
 | |
|       if (end < n_attrs - 1)
 | |
|         {
 | |
|           end = _gtk_pango_move_sentences (layout, end, 1);
 | |
|           while (!attrs[end].is_sentence_start && end < n_attrs - 1)
 | |
|             end = _gtk_pango_move_chars (layout, end, 1);
 | |
|         }
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_SENTENCE_END:
 | |
|       end = _gtk_pango_move_sentences (layout, end, 1);
 | |
|       start = end;
 | |
|       if (end < n_attrs - 1)
 | |
|         end = _gtk_pango_move_sentences (layout, 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_assert (start <= end);
 | |
| 
 | |
|   return g_utf8_substring (text, start, end);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * _gtk_pango_get_text_at:
 | |
|  * @layout: a #PangoLayout
 | |
|  * @boundary_type: a #AtkTextBoundary
 | |
|  * @offset: a character offset in @layout
 | |
|  * @start_offset: return location for the start of the returned text
 | |
|  * @end_offset: return location for the end of the return text
 | |
|  *
 | |
|  * Gets a slice of the text from @layout at @offset.
 | |
|  *
 | |
|  * The @boundary_type determines the size of the returned slice of
 | |
|  * text. For the exact semantics of this function, see
 | |
|  * atk_text_get_text_after_offset().
 | |
|  *
 | |
|  * Returns: a newly allocated string containing a slice of text
 | |
|  *     from layout. Free with g_free().
 | |
|  */
 | |
| gchar *
 | |
| _gtk_pango_get_text_at (PangoLayout     *layout,
 | |
|                         AtkTextBoundary  boundary_type,
 | |
|                         gint             offset,
 | |
|                         gint            *start_offset,
 | |
|                         gint            *end_offset)
 | |
| {
 | |
|   const gchar *text;
 | |
|   gint start, end;
 | |
|   const PangoLogAttr *attrs;
 | |
|   gint n_attrs;
 | |
| 
 | |
|   text = pango_layout_get_text (layout);
 | |
| 
 | |
|   if (text[0] == 0)
 | |
|     {
 | |
|       *start_offset = 0;
 | |
|       *end_offset = 0;
 | |
|       return g_strdup ("");
 | |
|     }
 | |
| 
 | |
|   attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
 | |
| 
 | |
|   start = offset;
 | |
|   end = start;
 | |
| 
 | |
|   switch (boundary_type)
 | |
|     {
 | |
|     case ATK_TEXT_BOUNDARY_CHAR:
 | |
|       end = _gtk_pango_move_chars (layout, end, 1);
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_WORD_START:
 | |
|       if (!attrs[start].is_word_start)
 | |
|         start = _gtk_pango_move_words (layout, start, -1);
 | |
|       if (_gtk_pango_is_inside_word (layout, end))
 | |
|         end = _gtk_pango_move_words (layout, end, 1);
 | |
|       while (!attrs[end].is_word_start && end < n_attrs - 1)
 | |
|         end = _gtk_pango_move_chars (layout, end, 1);
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_WORD_END:
 | |
|       if (_gtk_pango_is_inside_word (layout, start) &&
 | |
|           !attrs[start].is_word_start)
 | |
|         start = _gtk_pango_move_words (layout, start, -1);
 | |
|       while (!attrs[start].is_word_end && start > 0)
 | |
|         start = _gtk_pango_move_chars (layout, start, -1);
 | |
|       end = _gtk_pango_move_words (layout, end, 1);
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_SENTENCE_START:
 | |
|       if (!attrs[start].is_sentence_start)
 | |
|         start = _gtk_pango_move_sentences (layout, start, -1);
 | |
|       if (_gtk_pango_is_inside_sentence (layout, end))
 | |
|         end = _gtk_pango_move_sentences (layout, end, 1);
 | |
|       while (!attrs[end].is_sentence_start && end < n_attrs - 1)
 | |
|         end = _gtk_pango_move_chars (layout, end, 1);
 | |
|       break;
 | |
| 
 | |
|     case ATK_TEXT_BOUNDARY_SENTENCE_END:
 | |
|       if (_gtk_pango_is_inside_sentence (layout, start) &&
 | |
|           !attrs[start].is_sentence_start)
 | |
|         start = _gtk_pango_move_sentences (layout, start, -1);
 | |
|       while (!attrs[start].is_sentence_end && start > 0)
 | |
|         start = _gtk_pango_move_chars (layout, start, -1);
 | |
|       end = _gtk_pango_move_sentences (layout, 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_assert (start <= end);
 | |
| 
 | |
|   return g_utf8_substring (text, start, end);
 | |
| }
 |