]> Pileus Git - ~andy/gtk/blob - gtk/a11y/gtktextcellaccessible.c
textview: Use GtkSelectionWindow for touch text selection
[~andy/gtk] / gtk / a11y / gtktextcellaccessible.c
1 /* GTK+ - accessibility implementations
2  * Copyright 2001 Sun Microsystems Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include <gtk/gtk.h>
21 #include "../gtkpango.h"
22 #include "gtktextcellaccessible.h"
23 #include "gtkcontainercellaccessible.h"
24 #include "gtkcellaccessibleparent.h"
25
26 struct _GtkTextCellAccessiblePrivate
27 {
28   gchar *cell_text;
29   gint caret_pos;
30   gint cell_length;
31 };
32
33 static const gchar* gtk_text_cell_accessible_get_name    (AtkObject      *atk_obj);
34
35
36 /* atktext.h */
37
38 static gchar*    gtk_text_cell_accessible_get_text                (AtkText        *text,
39                                                         gint            start_pos,
40                                                         gint            end_pos);
41 static gunichar gtk_text_cell_accessible_get_character_at_offset  (AtkText        *text,
42                                                          gint           offset);
43 static gchar*   gtk_text_cell_accessible_get_text_before_offset   (AtkText        *text,
44                                                          gint           offset,
45                                                          AtkTextBoundary boundary_type,
46                                                          gint           *start_offset,
47                                                          gint           *end_offset);
48 static gchar*   gtk_text_cell_accessible_get_text_at_offset       (AtkText        *text,
49                                                          gint           offset,
50                                                          AtkTextBoundary boundary_type,
51                                                          gint           *start_offset,
52                                                          gint           *end_offset);
53 static gchar*   gtk_text_cell_accessible_get_text_after_offset    (AtkText        *text,
54                                                          gint           offset,
55                                                          AtkTextBoundary boundary_type,
56                                                          gint           *start_offset,
57                                                          gint           *end_offset);
58 static gint      gtk_text_cell_accessible_get_character_count     (AtkText        *text);
59 static gint      gtk_text_cell_accessible_get_caret_offset        (AtkText        *text);
60 static gboolean  gtk_text_cell_accessible_set_caret_offset        (AtkText        *text,
61                                                          gint           offset);
62 static void      gtk_text_cell_accessible_get_character_extents   (AtkText        *text,
63                                                          gint           offset,
64                                                          gint           *x,
65                                                          gint           *y,
66                                                          gint           *width,
67                                                          gint           *height,
68                                                          AtkCoordType   coords);
69 static gint      gtk_text_cell_accessible_get_offset_at_point     (AtkText        *text,
70                                                          gint           x,
71                                                          gint           y,
72                                                          AtkCoordType   coords);
73 static AtkAttributeSet* gtk_text_cell_accessible_get_run_attributes 
74                                                         (AtkText        *text,
75                                                          gint           offset,
76                                                          gint           *start_offset,      
77                                                          gint           *end_offset); 
78 static AtkAttributeSet* gtk_text_cell_accessible_get_default_attributes 
79                                                         (AtkText        *text);
80
81 static GtkWidget*       get_widget                      (GtkTextCellAccessible *cell);
82 static PangoLayout*     create_pango_layout             (GtkTextCellAccessible *cell);
83 static void             add_attr                        (PangoAttrList  *attr_list,
84                                                          PangoAttribute *attr);
85
86 /* Misc */
87
88 static void gtk_text_cell_accessible_update_cache       (GtkCellAccessible *cell);
89
90 static void atk_text_interface_init (AtkTextIface *iface);
91
92 G_DEFINE_TYPE_WITH_CODE (GtkTextCellAccessible, gtk_text_cell_accessible, GTK_TYPE_RENDERER_CELL_ACCESSIBLE,
93                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, atk_text_interface_init))
94
95 static AtkStateSet *
96 gtk_text_cell_accessible_ref_state_set (AtkObject *accessible)
97 {
98   AtkStateSet *state_set;
99
100   state_set = ATK_OBJECT_CLASS (gtk_text_cell_accessible_parent_class)->ref_state_set (accessible);
101
102   atk_state_set_add_state (state_set, ATK_STATE_SINGLE_LINE);
103
104   return state_set;
105 }
106
107 static void
108 gtk_text_cell_accessible_finalize (GObject *object)
109 {
110   GtkTextCellAccessible *text_cell = GTK_TEXT_CELL_ACCESSIBLE (object);
111
112   g_free (text_cell->priv->cell_text);
113
114   G_OBJECT_CLASS (gtk_text_cell_accessible_parent_class)->finalize (object);
115 }
116
117 static const gchar *
118 gtk_text_cell_accessible_get_name (AtkObject *atk_obj)
119 {
120   GtkTextCellAccessible *text_cell = GTK_TEXT_CELL_ACCESSIBLE (atk_obj);
121
122   if (atk_obj->name)
123     return atk_obj->name;
124
125   return text_cell->priv->cell_text;
126 }
127
128 static void
129 gtk_text_cell_accessible_update_cache (GtkCellAccessible *cell)
130 {
131   GtkTextCellAccessible *text_cell = GTK_TEXT_CELL_ACCESSIBLE (cell);
132   AtkObject *obj = ATK_OBJECT (cell);
133   gboolean rv = FALSE;
134   gint temp_length;
135   gchar *text;
136   GtkCellRenderer *renderer;
137
138   g_object_get (cell, "renderer", &renderer, NULL);
139   g_object_get (renderer, "text", &text, NULL);
140   g_object_unref (renderer);
141
142   if (text_cell->priv->cell_text)
143     {
144       if (text == NULL || g_strcmp0 (text_cell->priv->cell_text, text) != 0)
145         {
146           g_free (text_cell->priv->cell_text);
147           temp_length = text_cell->priv->cell_length;
148           text_cell->priv->cell_text = NULL;
149           text_cell->priv->cell_length = 0;
150           g_signal_emit_by_name (cell, "text-changed::delete", 0, temp_length);
151           if (obj->name == NULL)
152             g_object_notify (G_OBJECT (obj), "accessible-name");
153           if (text)
154             rv = TRUE;
155         }
156     }
157   else
158     rv = TRUE;
159
160   if (rv)
161     {
162       if (text == NULL)
163         {
164           text_cell->priv->cell_text = g_strdup ("");
165           text_cell->priv->cell_length = 0;
166         }
167       else
168         {
169           text_cell->priv->cell_text = g_strdup (text);
170           text_cell->priv->cell_length = g_utf8_strlen (text, -1);
171         }
172     }
173
174   g_free (text);
175
176   if (rv)
177     {
178       g_signal_emit_by_name (cell, "text-changed::insert",
179                              0, text_cell->priv->cell_length);
180
181       if (obj->name == NULL)
182         g_object_notify (G_OBJECT (obj), "accessible-name");
183     }
184 }
185
186 static void
187 gtk_text_cell_accessible_class_init (GtkTextCellAccessibleClass *klass)
188 {
189   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
190   AtkObjectClass *atk_object_class = ATK_OBJECT_CLASS (klass);
191   GtkCellAccessibleClass *cell_class = GTK_CELL_ACCESSIBLE_CLASS (klass);
192
193   cell_class->update_cache = gtk_text_cell_accessible_update_cache;
194
195   atk_object_class->get_name = gtk_text_cell_accessible_get_name;
196   atk_object_class->ref_state_set = gtk_text_cell_accessible_ref_state_set;
197
198   gobject_class->finalize = gtk_text_cell_accessible_finalize;
199
200   g_type_class_add_private (klass, sizeof (GtkTextCellAccessiblePrivate));
201 }
202
203 static void
204 gtk_text_cell_accessible_init (GtkTextCellAccessible *text_cell)
205 {
206   text_cell->priv = G_TYPE_INSTANCE_GET_PRIVATE (text_cell,
207                                                  GTK_TYPE_TEXT_CELL_ACCESSIBLE,
208                                                  GtkTextCellAccessiblePrivate);
209 }
210
211 static gchar *
212 gtk_text_cell_accessible_get_text (AtkText *atk_text,
213                                    gint     start_pos,
214                                    gint     end_pos)
215 {
216   gchar *text;
217
218   text = GTK_TEXT_CELL_ACCESSIBLE (atk_text)->priv->cell_text;
219   if (text)
220     return g_utf8_substring (text, start_pos, end_pos > -1 ? end_pos : g_utf8_strlen (text, -1));
221   else
222     return g_strdup ("");
223 }
224
225 static gchar *
226 gtk_text_cell_accessible_get_text_before_offset (AtkText         *atk_text,
227                                                  gint             offset,
228                                                  AtkTextBoundary  boundary_type,
229                                                  gint            *start_offset,
230                                                  gint            *end_offset)
231 {
232   PangoLayout *layout;
233   gchar *text;
234
235   layout = create_pango_layout (GTK_TEXT_CELL_ACCESSIBLE (atk_text));
236   text = _gtk_pango_get_text_before (layout, boundary_type, offset, start_offset, end_offset);
237   g_object_unref (layout);
238
239   return text;
240 }
241
242 static gchar *
243 gtk_text_cell_accessible_get_text_at_offset (AtkText         *atk_text,
244                                              gint             offset,
245                                              AtkTextBoundary  boundary_type,
246                                              gint            *start_offset,
247                                              gint            *end_offset)
248 {
249   PangoLayout *layout;
250   gchar *text;
251
252   layout = create_pango_layout (GTK_TEXT_CELL_ACCESSIBLE (atk_text));
253   text = _gtk_pango_get_text_at (layout, boundary_type, offset, start_offset, end_offset);
254   g_object_unref (layout);
255
256   return text;
257 }
258
259 static gchar *
260 gtk_text_cell_accessible_get_text_after_offset (AtkText         *atk_text,
261                                                 gint             offset,
262                                                 AtkTextBoundary  boundary_type,
263                                                 gint            *start_offset,
264                                                 gint            *end_offset)
265 {
266   PangoLayout *layout;
267   gchar *text;
268
269   layout = create_pango_layout (GTK_TEXT_CELL_ACCESSIBLE (atk_text));
270   text = _gtk_pango_get_text_after (layout, boundary_type, offset, start_offset, end_offset);
271   g_object_unref (layout);
272
273   return text;
274 }
275
276 static gint
277 gtk_text_cell_accessible_get_character_count (AtkText *text)
278 {
279   if (GTK_TEXT_CELL_ACCESSIBLE (text)->priv->cell_text != NULL)
280     return GTK_TEXT_CELL_ACCESSIBLE (text)->priv->cell_length;
281   else
282     return 0;
283 }
284
285 static gint
286 gtk_text_cell_accessible_get_caret_offset (AtkText *text)
287 {
288   return GTK_TEXT_CELL_ACCESSIBLE (text)->priv->caret_pos;
289 }
290
291 static gboolean
292 gtk_text_cell_accessible_set_caret_offset (AtkText *text,
293                                            gint     offset)
294 {
295   GtkTextCellAccessible *text_cell = GTK_TEXT_CELL_ACCESSIBLE (text);
296
297   if (text_cell->priv->cell_text == NULL)
298     return FALSE;
299   else
300     {
301       /* Only set the caret within the bounds and if it is to a new position. */
302       if (offset >= 0 &&
303           offset <= text_cell->priv->cell_length &&
304           offset != text_cell->priv->caret_pos)
305         {
306           text_cell->priv->caret_pos = offset;
307
308           /* emit the signal */
309           g_signal_emit_by_name (text, "text-caret-moved", offset);
310           return TRUE;
311         }
312       else
313         return FALSE;
314     }
315 }
316
317 static AtkAttributeSet *
318 gtk_text_cell_accessible_get_run_attributes (AtkText *text,
319                                              gint     offset,
320                                              gint    *start_offset,
321                                              gint    *end_offset)
322 {
323   AtkAttributeSet *attrib_set = NULL;
324   PangoLayout *layout;
325
326   layout = create_pango_layout (GTK_TEXT_CELL_ACCESSIBLE (text));
327   attrib_set = _gtk_pango_get_run_attributes  (NULL, layout, offset, start_offset, end_offset);
328   g_object_unref (G_OBJECT (layout));
329
330   return attrib_set;
331 }
332
333 static AtkAttributeSet *
334 add_attribute (AtkAttributeSet  *attributes,
335                AtkTextAttribute  attr,
336                const gchar      *value)
337 {
338   AtkAttribute *at;
339
340   at = g_new (AtkAttribute, 1);
341   at->name = g_strdup (atk_text_attribute_get_name (attr));
342   at->value = g_strdup (value);
343
344   return g_slist_prepend (attributes, at);
345 }
346
347 static AtkAttributeSet *
348 gtk_text_cell_accessible_get_default_attributes (AtkText *text)
349 {
350   AtkAttributeSet *attrib_set = NULL;
351   PangoLayout *layout;
352   GtkWidget *widget;
353
354   layout = create_pango_layout (GTK_TEXT_CELL_ACCESSIBLE (text));
355   widget = get_widget (GTK_TEXT_CELL_ACCESSIBLE (text));
356
357   attrib_set = add_attribute (attrib_set, ATK_TEXT_ATTR_DIRECTION,
358                    atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION,
359                                                  gtk_widget_get_direction (widget)));
360   attrib_set = _gtk_pango_get_default_attributes (NULL, layout);
361
362   attrib_set = _gtk_style_context_get_attributes (attrib_set,
363                                                   gtk_widget_get_style_context (widget),
364                                                   gtk_widget_get_state_flags (widget));
365
366   g_object_unref (G_OBJECT (layout));
367
368   return attrib_set;
369 }
370
371 GtkWidget *
372 get_widget (GtkTextCellAccessible *text)
373 {
374   AtkObject *parent;
375
376   parent = atk_object_get_parent (ATK_OBJECT (text));
377   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
378     parent = atk_object_get_parent (parent);
379
380   return gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
381 }
382
383 /* This function is used by gtk_text_cell_accessible_get_offset_at_point()
384  * and gtk_text_cell_accessible_get_character_extents(). There is no
385  * cached PangoLayout so we must create a temporary one using this function.
386  */
387 static PangoLayout *
388 create_pango_layout (GtkTextCellAccessible *text)
389 {
390   GdkRGBA *foreground_rgba;
391   PangoAttrList *attr_list, *attributes;
392   PangoLayout *layout;
393   PangoUnderline uline, underline;
394   PangoFontMask mask;
395   PangoFontDescription *font_desc;
396   gboolean foreground_set, strikethrough_set, strikethrough;
397   gboolean scale_set, underline_set, rise_set;
398   gchar *renderer_text;
399   gdouble scale;
400   gint rise;
401   GtkRendererCellAccessible *gail_renderer;
402   GtkCellRendererText *gtk_renderer;
403
404   gail_renderer = GTK_RENDERER_CELL_ACCESSIBLE (text);
405   g_object_get (gail_renderer, "renderer", &gtk_renderer, NULL);
406
407   g_object_get (gtk_renderer,
408                 "text", &renderer_text,
409                 "attributes", &attributes,
410                 "foreground-set", &foreground_set,
411                 "foreground-rgba", &foreground_rgba,
412                 "strikethrough-set", &strikethrough_set,
413                 "strikethrough", &strikethrough,
414                 "font-desc", &font_desc,
415                 "scale-set", &scale_set,
416                 "scale", &scale,
417                 "underline-set", &underline_set,
418                 "underline", &underline,
419                 "rise-set", &rise_set,
420                 "rise", &rise,
421                 NULL);
422   g_object_unref (gtk_renderer);
423
424   layout = gtk_widget_create_pango_layout (get_widget (text), renderer_text);
425
426   if (attributes)
427     attr_list = pango_attr_list_copy (attributes);
428   else
429     attr_list = pango_attr_list_new ();
430
431   if (foreground_set)
432     {
433       add_attr (attr_list, pango_attr_foreground_new (foreground_rgba->red * 65535,
434                                                       foreground_rgba->green * 65535,
435                                                       foreground_rgba->blue * 65535));
436     }
437
438   if (strikethrough_set)
439     add_attr (attr_list,
440               pango_attr_strikethrough_new (strikethrough));
441
442   mask = pango_font_description_get_set_fields (font_desc);
443
444   if (mask & PANGO_FONT_MASK_FAMILY)
445     add_attr (attr_list,
446       pango_attr_family_new (pango_font_description_get_family (font_desc)));
447
448   if (mask & PANGO_FONT_MASK_STYLE)
449     add_attr (attr_list, pango_attr_style_new (pango_font_description_get_style (font_desc)));
450
451   if (mask & PANGO_FONT_MASK_VARIANT)
452     add_attr (attr_list, pango_attr_variant_new (pango_font_description_get_variant (font_desc)));
453
454   if (mask & PANGO_FONT_MASK_WEIGHT)
455     add_attr (attr_list, pango_attr_weight_new (pango_font_description_get_weight (font_desc)));
456
457   if (mask & PANGO_FONT_MASK_STRETCH)
458     add_attr (attr_list, pango_attr_stretch_new (pango_font_description_get_stretch (font_desc)));
459
460   if (mask & PANGO_FONT_MASK_SIZE)
461     add_attr (attr_list, pango_attr_size_new (pango_font_description_get_size (font_desc)));
462
463   if (scale_set && scale != 1.0)
464     add_attr (attr_list, pango_attr_scale_new (scale));
465
466   if (underline_set)
467     uline = underline;
468   else
469     uline = PANGO_UNDERLINE_NONE;
470
471   if (uline != PANGO_UNDERLINE_NONE)
472     add_attr (attr_list,
473               pango_attr_underline_new (underline));
474
475   if (rise_set)
476     add_attr (attr_list, pango_attr_rise_new (rise));
477
478   pango_layout_set_attributes (layout, attr_list);
479   pango_layout_set_width (layout, -1);
480   pango_attr_list_unref (attr_list);
481
482   pango_font_description_free (font_desc);
483   pango_attr_list_unref (attributes);
484   g_free (renderer_text);
485   gdk_rgba_free (foreground_rgba);
486
487   return layout;
488 }
489
490 static void
491 add_attr (PangoAttrList *attr_list,
492          PangoAttribute *attr)
493 {
494   attr->start_index = 0;
495   attr->end_index = G_MAXINT;
496   pango_attr_list_insert (attr_list, attr);
497 }
498
499
500 static void
501 get_origins (GtkWidget *widget,
502              gint      *x_window,
503              gint      *y_window,
504              gint      *x_toplevel,
505              gint      *y_toplevel)
506 {
507   GdkWindow *window;
508
509   if (GTK_IS_TREE_VIEW (widget))
510     window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget));
511   else
512     window = gtk_widget_get_window (widget);
513
514   gdk_window_get_origin (window, x_window, y_window);
515   window = gdk_window_get_toplevel (gtk_widget_get_window (widget));
516   gdk_window_get_origin (window, x_toplevel, y_toplevel);
517 }
518
519 static void
520 gtk_text_cell_accessible_get_character_extents (AtkText      *text,
521                                                 gint          offset,
522                                                 gint         *x,
523                                                 gint         *y,
524                                                 gint         *width,
525                                                 gint         *height,
526                                                 AtkCoordType  coords)
527 {
528   GtkRendererCellAccessible *gail_renderer;
529   GtkRequisition min_size;
530   GtkCellRendererText *gtk_renderer;
531   GdkRectangle rendered_rect;
532   GtkWidget *widget;
533   AtkObject *parent;
534   PangoRectangle char_rect;
535   PangoLayout *layout;
536   gchar *renderer_text;
537   gfloat xalign, yalign;
538   gint x_offset, y_offset, index;
539   gint xpad, ypad;
540   gint x_window, y_window, x_toplevel, y_toplevel;
541
542   if (!GTK_TEXT_CELL_ACCESSIBLE (text)->priv->cell_text)
543     {
544       *x = *y = *height = *width = 0;
545       return;
546     }
547   if (offset < 0 || offset >= GTK_TEXT_CELL_ACCESSIBLE (text)->priv->cell_length)
548     {
549       *x = *y = *height = *width = 0;
550       return;
551     }
552   gail_renderer = GTK_RENDERER_CELL_ACCESSIBLE (text);
553   g_object_get (gail_renderer, "renderer", &gtk_renderer, NULL);
554   g_object_get (gtk_renderer, "text", &renderer_text, NULL);
555   if (renderer_text == NULL)
556     {
557       g_object_unref (gtk_renderer);
558       return;
559     }
560
561   parent = atk_object_get_parent (ATK_OBJECT (text));
562   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
563     parent = atk_object_get_parent (parent);
564   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
565   g_return_if_fail (GTK_IS_CELL_ACCESSIBLE_PARENT (parent));
566   gtk_cell_accessible_parent_get_cell_area (GTK_CELL_ACCESSIBLE_PARENT (parent),
567                                             GTK_CELL_ACCESSIBLE (text),
568                                             &rendered_rect);
569
570   gtk_cell_renderer_get_preferred_size (GTK_CELL_RENDERER (gtk_renderer),
571                                         widget,
572                                         &min_size, NULL);
573
574   gtk_cell_renderer_get_alignment (GTK_CELL_RENDERER (gtk_renderer), &xalign, &yalign);
575   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
576     xalign = 1.0 - xalign;
577   x_offset = MAX (0, xalign * (rendered_rect.width - min_size.width));
578   y_offset = MAX (0, yalign * (rendered_rect.height - min_size.height));
579
580   layout = create_pango_layout (GTK_TEXT_CELL_ACCESSIBLE (text));
581
582   index = g_utf8_offset_to_pointer (renderer_text, offset) - renderer_text;
583   pango_layout_index_to_pos (layout, index, &char_rect);
584
585   gtk_cell_renderer_get_padding (GTK_CELL_RENDERER (gtk_renderer), &xpad, &ypad);
586
587   get_origins (widget, &x_window, &y_window, &x_toplevel, &y_toplevel);
588
589   *x = (char_rect.x / PANGO_SCALE) + x_offset + rendered_rect.x + xpad + x_window;
590   *y = (char_rect.y / PANGO_SCALE) + y_offset + rendered_rect.y + ypad + y_window;
591   *height = char_rect.height / PANGO_SCALE;
592   *width = char_rect.width / PANGO_SCALE;
593
594   if (coords == ATK_XY_WINDOW)
595     {
596       *x -= x_toplevel;
597       *y -= y_toplevel;
598     }
599   else if (coords != ATK_XY_SCREEN)
600     {
601       *x = 0;
602       *y = 0;
603       *height = 0;
604       *width = 0;
605     }
606
607   g_free (renderer_text);
608   g_object_unref (layout);
609   g_object_unref (gtk_renderer);
610 }
611
612 static gint
613 gtk_text_cell_accessible_get_offset_at_point (AtkText      *text,
614                                               gint          x,
615                                               gint          y,
616                                               AtkCoordType  coords)
617 {
618   AtkObject *parent;
619   GtkRendererCellAccessible *gail_renderer;
620   GtkCellRendererText *gtk_renderer;
621   GtkRequisition min_size;
622   GtkWidget *widget;
623   GdkRectangle rendered_rect;
624   PangoLayout *layout;
625   gchar *renderer_text;
626   gfloat xalign, yalign;
627   gint x_offset, y_offset, index;
628   gint xpad, ypad;
629   gint x_window, y_window, x_toplevel, y_toplevel;
630   gint x_temp, y_temp;
631   gboolean ret;
632
633   if (!GTK_TEXT_CELL_ACCESSIBLE (text)->priv->cell_text)
634     return -1;
635
636   gail_renderer = GTK_RENDERER_CELL_ACCESSIBLE (text);
637   g_object_get (gail_renderer, "renderer", &gtk_renderer, NULL);
638   parent = atk_object_get_parent (ATK_OBJECT (text));
639
640   g_object_get (gtk_renderer, "text", &renderer_text, NULL);
641   if (text == NULL)
642     {
643       g_object_unref (gtk_renderer);
644       g_free (renderer_text);
645       return -1;
646     }
647
648   if (GTK_IS_CONTAINER_CELL_ACCESSIBLE (parent))
649     parent = atk_object_get_parent (parent);
650
651   widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (parent));
652
653   g_return_val_if_fail (GTK_IS_CELL_ACCESSIBLE_PARENT (parent), -1);
654   gtk_cell_accessible_parent_get_cell_area (GTK_CELL_ACCESSIBLE_PARENT (parent),
655                                             GTK_CELL_ACCESSIBLE (text),
656                                             &rendered_rect);
657
658   gtk_cell_renderer_get_preferred_size (GTK_CELL_RENDERER (gtk_renderer),
659                                         widget,
660                                         &min_size, NULL);
661   gtk_cell_renderer_get_alignment (GTK_CELL_RENDERER (gtk_renderer), &xalign, &yalign);
662   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
663     xalign = 1.0 - xalign;
664   x_offset = MAX (0, xalign * (rendered_rect.width - min_size.width));
665   y_offset = MAX (0, yalign * (rendered_rect.height - min_size.height));
666
667   layout = create_pango_layout (GTK_TEXT_CELL_ACCESSIBLE (text));
668
669   gtk_cell_renderer_get_padding (GTK_CELL_RENDERER (gtk_renderer), &xpad, &ypad);
670
671   get_origins (widget, &x_window, &y_window, &x_toplevel, &y_toplevel);
672
673   x_temp =  x - (x_offset + rendered_rect.x + xpad) - x_window;
674   y_temp =  y - (y_offset + rendered_rect.y + ypad) - y_window;
675   if (coords == ATK_XY_WINDOW)
676     {
677       x_temp += x_toplevel;
678       y_temp += y_toplevel;
679     }
680   else if (coords != ATK_XY_SCREEN)
681     index = -1;
682
683   ret = pango_layout_xy_to_index (layout,
684                                   x_temp * PANGO_SCALE,
685                                   y_temp * PANGO_SCALE,
686                                   &index, NULL);
687   if (!ret)
688     {
689       if (x_temp < 0 || y_temp < 0)
690         index = 0;
691       else
692         index = -1;
693     }
694
695   g_object_unref (layout);
696   g_object_unref (gtk_renderer);
697
698   if (index == -1)
699     {
700       if (coords == ATK_XY_WINDOW || coords == ATK_XY_SCREEN)
701         {
702           glong length;
703
704           length = g_utf8_strlen (renderer_text, -1);
705           g_free (renderer_text);
706
707           return length;
708         }
709
710       g_free (renderer_text);
711
712       return index;
713     }
714   else
715     {
716       glong offset;
717
718       offset = g_utf8_pointer_to_offset (renderer_text,
719                                          renderer_text + index);
720       g_free (renderer_text);
721
722       return offset;
723     }
724 }
725
726 static gunichar
727 gtk_text_cell_accessible_get_character_at_offset (AtkText *text,
728                                                   gint     offset)
729 {
730   gchar *index;
731   gchar *string;
732
733   string = GTK_TEXT_CELL_ACCESSIBLE(text)->priv->cell_text;
734
735   if (!string)
736     return '\0';
737
738   if (offset >= g_utf8_strlen (string, -1))
739     return '\0';
740
741   index = g_utf8_offset_to_pointer (string, offset);
742
743   return g_utf8_get_char (index);
744 }
745
746 static void
747 atk_text_interface_init (AtkTextIface *iface)
748 {
749   iface->get_text = gtk_text_cell_accessible_get_text;
750   iface->get_character_at_offset = gtk_text_cell_accessible_get_character_at_offset;
751   iface->get_text_before_offset = gtk_text_cell_accessible_get_text_before_offset;
752   iface->get_text_at_offset = gtk_text_cell_accessible_get_text_at_offset;
753   iface->get_text_after_offset = gtk_text_cell_accessible_get_text_after_offset;
754   iface->get_character_count = gtk_text_cell_accessible_get_character_count;
755   iface->get_caret_offset = gtk_text_cell_accessible_get_caret_offset;
756   iface->set_caret_offset = gtk_text_cell_accessible_set_caret_offset;
757   iface->get_run_attributes = gtk_text_cell_accessible_get_run_attributes;
758   iface->get_default_attributes = gtk_text_cell_accessible_get_default_attributes;
759   iface->get_character_extents = gtk_text_cell_accessible_get_character_extents;
760   iface->get_offset_at_point = gtk_text_cell_accessible_get_offset_at_point;
761 }