]> Pileus Git - ~andy/gtk/blob - modules/other/gail/libgail-util/gailmisc.c
Integrate gail into gtk+. Bug #169488.
[~andy/gtk] / modules / other / gail / libgail-util / gailmisc.c
1 /* GAIL - The GNOME Accessibility Implementation Library
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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <stdlib.h>
21 #include <gtk/gtk.h>
22 #include "gailmisc.h"
23
24 /* IMPORTANT!!! This source file does NOT contain the implementation
25  * code for AtkUtil - for that code, please see gail/gail.c.
26  */
27
28 /**
29  * gail_misc_get_extents_from_pango_rectangle:
30  * @widget: The widget that contains the PangoLayout, that contains
31  *   the PangoRectangle
32  * @char_rect: The #PangoRectangle from which to calculate extents
33  * @x_layout: The x-offset at which the widget displays the
34  *   PangoLayout that contains the PangoRectangle, relative to @widget
35  * @y_layout: The y-offset at which the widget displays the
36  *   PangoLayout that contains the PangoRectangle, relative to @widget
37  * @x: The x-position of the #PangoRectangle relative to @coords
38  * @y: The y-position of the #PangoRectangle relative to @coords
39  * @width: The width of the #PangoRectangle
40  * @height: The  height of the #PangoRectangle
41  * @coords: An #AtkCoordType enumeration
42  *
43  * Gets the extents of @char_rect in device coordinates,
44  * relative to either top-level window or screen coordinates as
45  * specified by @coords.
46  **/
47 void
48 gail_misc_get_extents_from_pango_rectangle (GtkWidget      *widget,
49                                             PangoRectangle *char_rect,
50                                             gint           x_layout,
51                                             gint           y_layout,
52                                             gint           *x,
53                                             gint           *y,
54                                             gint           *width,
55                                             gint           *height,
56                                             AtkCoordType   coords)
57 {
58   gint x_window, y_window, x_toplevel, y_toplevel;
59
60   gail_misc_get_origins (widget, &x_window, &y_window, 
61                          &x_toplevel, &y_toplevel);
62
63   *x = (char_rect->x / PANGO_SCALE) + x_layout + x_window;
64   *y = (char_rect->y / PANGO_SCALE) + y_layout + y_window;
65   if (coords == ATK_XY_WINDOW)
66     {
67       *x -= x_toplevel;
68       *y -= y_toplevel;
69     }
70   else if (coords != ATK_XY_SCREEN)
71     {
72       *x = 0;
73       *y = 0;
74       *height = 0;
75       *width = 0;
76       return;
77     }
78   *height = char_rect->height / PANGO_SCALE;
79   *width = char_rect->width / PANGO_SCALE;
80
81   return;
82 }
83
84 /**
85  * gail_misc_get_index_at_point_in_layout:
86  * @widget: A #GtkWidget
87  * @layout: The #PangoLayout from which to get the index at the
88  *   specified point.
89  * @x_layout: The x-offset at which the widget displays the
90  *   #PangoLayout, relative to @widget
91  * @y_layout: The y-offset at which the widget displays the
92  *   #PangoLayout, relative to @widget
93  * @x: The x-coordinate relative to @coords at which to
94  *   calculate the index
95  * @y: The y-coordinate relative to @coords at which to
96  *   calculate the index
97  * @coords: An #AtkCoordType enumeration
98  *
99  * Gets the byte offset at the specified @x and @y in a #PangoLayout.
100  *
101  * Returns: the byte offset at the specified @x and @y in a
102  *   #PangoLayout
103  **/
104 gint
105 gail_misc_get_index_at_point_in_layout (GtkWidget   *widget,
106                                         PangoLayout *layout,
107                                         gint        x_layout,
108                                         gint        y_layout,
109                                         gint        x,
110                                         gint        y,
111                                         AtkCoordType coords)
112 {
113   gint index, x_window, y_window, x_toplevel, y_toplevel;
114   gint x_temp, y_temp;
115   gboolean ret;
116
117   gail_misc_get_origins (widget, &x_window, &y_window, 
118                          &x_toplevel, &y_toplevel);
119   x_temp =  x - x_layout - x_window;
120   y_temp =  y - y_layout - y_window;
121   if (coords == ATK_XY_WINDOW)
122     {
123       x_temp += x_toplevel;  
124       y_temp += y_toplevel;
125     }
126   else if (coords != ATK_XY_SCREEN)
127     return -1;
128
129   ret = pango_layout_xy_to_index (layout, 
130                                   x_temp * PANGO_SCALE,
131                                   y_temp * PANGO_SCALE,
132                                   &index, NULL);
133   if (!ret)
134     {
135       if (x_temp < 0 || y_temp < 0)
136         index = 0;
137       else
138         index = -1; 
139     }
140   return index;
141 }
142
143 /**
144  * gail_misc_add_attribute:
145  * @attrib_set: The #AtkAttributeSet to add the attribute to
146  * @attr: The AtkTextAttrribute which identifies the attribute to be added
147  * @value: The attribute value
148  *
149  * Creates an #AtkAttribute from @attr and @value, and adds it
150  * to @attrib_set. 
151  *
152  * Returns: A pointer to the new #AtkAttributeSet.
153  **/
154 AtkAttributeSet*
155 gail_misc_add_attribute (AtkAttributeSet *attrib_set,
156                          AtkTextAttribute attr,
157                          gchar           *value)
158 {
159   AtkAttributeSet *return_set;
160   AtkAttribute *at = g_malloc (sizeof (AtkAttribute));
161   at->name = g_strdup (atk_text_attribute_get_name (attr));
162   at->value = value;
163   return_set = g_slist_prepend(attrib_set, at);
164   return return_set;
165 }
166
167 /**
168  * gail_misc_layout_get_run_attributes:
169  * @attrib_set: The #AtkAttributeSet to add the attribute to
170  * @layout: The PangoLayout from which the attributes will be obtained
171  * @text: The text 
172  * @offset: The offset at which the attributes are required
173  * @start_offset: The start offset of the current run
174  * @end_offset: The end offset of the current run
175  *
176  * Adds the attributes for the run starting at offset to the specified
177  * attribute set.
178  *
179  * Returns: A pointer to the #AtkAttributeSet.
180  **/
181 AtkAttributeSet* 
182 gail_misc_layout_get_run_attributes (AtkAttributeSet *attrib_set,
183                                      PangoLayout     *layout,
184                                      gchar           *text,
185                                      gint            offset,
186                                      gint            *start_offset,
187                                      gint            *end_offset)
188 {
189   PangoAttrIterator *iter;
190   PangoAttrList *attr;  
191   PangoAttrString *pango_string;
192   PangoAttrInt *pango_int;
193   PangoAttrColor *pango_color;
194   PangoAttrLanguage *pango_lang;
195   PangoAttrFloat *pango_float;
196   gint index, start_index, end_index;
197   gboolean is_next = TRUE;
198   gchar *value = NULL;
199   glong len;
200
201   len = g_utf8_strlen (text, -1);
202   /* Grab the attributes of the PangoLayout, if any */
203   if ((attr = pango_layout_get_attributes (layout)) == NULL)
204     {
205       *start_offset = 0;
206       *end_offset = len;
207       return attrib_set;
208     }
209   iter = pango_attr_list_get_iterator (attr);
210   /* Get invariant range offsets */
211   /* If offset out of range, set offset in range */
212   if (offset > len)
213     offset = len;
214   else if (offset < 0)
215     offset = 0;
216
217   index = g_utf8_offset_to_pointer (text, offset) - text;
218   pango_attr_iterator_range (iter, &start_index, &end_index);
219   while (is_next)
220     {
221       if (index >= start_index && index < end_index)
222         {
223           *start_offset = g_utf8_pointer_to_offset (text, 
224                                                     text + start_index);  
225           if (end_index == G_MAXINT)
226           /* Last iterator */
227             end_index = len;
228       
229           *end_offset = g_utf8_pointer_to_offset (text, 
230                                                   text + end_index);  
231           break;
232         }  
233       is_next = pango_attr_iterator_next (iter);
234       pango_attr_iterator_range (iter, &start_index, &end_index);
235     }
236   /* Get attributes */
237   if ((pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, 
238                                    PANGO_ATTR_FAMILY)) != NULL)
239     {
240       value = g_strdup_printf("%s", pango_string->value);
241       attrib_set = gail_misc_add_attribute (attrib_set, 
242                                             ATK_TEXT_ATTR_FAMILY_NAME, 
243                                             value);
244     } 
245   if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
246                                    PANGO_ATTR_STYLE)) != NULL)
247     {
248       attrib_set = gail_misc_add_attribute (attrib_set, 
249                                             ATK_TEXT_ATTR_STYLE, 
250       g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value)));
251     } 
252   if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
253                                    PANGO_ATTR_WEIGHT)) != NULL)
254     {
255       value = g_strdup_printf("%i", pango_int->value);
256       attrib_set = gail_misc_add_attribute (attrib_set, 
257                                             ATK_TEXT_ATTR_WEIGHT, 
258                                             value);
259     } 
260   if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
261                                    PANGO_ATTR_VARIANT)) != NULL)
262     {
263       attrib_set = gail_misc_add_attribute (attrib_set, 
264                                             ATK_TEXT_ATTR_VARIANT, 
265        g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value)));
266     } 
267   if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
268                                    PANGO_ATTR_STRETCH)) != NULL)
269     {
270       attrib_set = gail_misc_add_attribute (attrib_set, 
271                                             ATK_TEXT_ATTR_STRETCH, 
272        g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value)));
273     } 
274   if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
275                                    PANGO_ATTR_SIZE)) != NULL)
276     {
277       value = g_strdup_printf("%i", pango_int->value / PANGO_SCALE);
278       attrib_set = gail_misc_add_attribute (attrib_set, 
279                                             ATK_TEXT_ATTR_SIZE,
280                                             value);
281     } 
282   if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
283                                    PANGO_ATTR_UNDERLINE)) != NULL)
284     {
285       attrib_set = gail_misc_add_attribute (attrib_set, 
286                                             ATK_TEXT_ATTR_UNDERLINE, 
287        g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value)));
288     } 
289   if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
290                                    PANGO_ATTR_STRIKETHROUGH)) != NULL)
291     {
292       attrib_set = gail_misc_add_attribute (attrib_set, 
293                                             ATK_TEXT_ATTR_STRIKETHROUGH, 
294        g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value)));
295     } 
296   if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
297                                    PANGO_ATTR_RISE)) != NULL)
298     {
299       value = g_strdup_printf("%i", pango_int->value);
300       attrib_set = gail_misc_add_attribute (attrib_set, 
301                                             ATK_TEXT_ATTR_RISE,
302                                             value);
303     } 
304   if ((pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, 
305                                    PANGO_ATTR_LANGUAGE)) != NULL)
306     {
307       value = g_strdup( pango_language_to_string( pango_lang->value));
308       attrib_set = gail_misc_add_attribute (attrib_set, 
309                                             ATK_TEXT_ATTR_LANGUAGE, 
310                                             value);
311     } 
312   if ((pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, 
313                                    PANGO_ATTR_SCALE)) != NULL)
314     {
315       value = g_strdup_printf("%g", pango_float->value);
316       attrib_set = gail_misc_add_attribute (attrib_set, 
317                                             ATK_TEXT_ATTR_SCALE, 
318                                             value);
319     } 
320   if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, 
321                                     PANGO_ATTR_FOREGROUND)) != NULL)
322     {
323       value = g_strdup_printf ("%u,%u,%u", 
324                                pango_color->color.red, 
325                                pango_color->color.green, 
326                                pango_color->color.blue);
327       attrib_set = gail_misc_add_attribute (attrib_set, 
328                                             ATK_TEXT_ATTR_FG_COLOR, 
329                                             value);
330     } 
331   if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, 
332                                      PANGO_ATTR_BACKGROUND)) != NULL)
333     {
334       value = g_strdup_printf ("%u,%u,%u", 
335                                pango_color->color.red, 
336                                pango_color->color.green, 
337                                pango_color->color.blue);
338       attrib_set = gail_misc_add_attribute (attrib_set, 
339                                             ATK_TEXT_ATTR_BG_COLOR, 
340                                             value);
341     } 
342   pango_attr_iterator_destroy (iter);
343   return attrib_set;
344 }
345
346 /**
347  * gail_misc_get_default_attributes:
348  * @attrib_set: The #AtkAttributeSet to add the attribute to
349  * @layout: The PangoLayout from which the attributes will be obtained
350  * @widget: The GtkWidget for which the default attributes are required.
351  *
352  * Adds the default attributes to the specified attribute set.
353  *
354  * Returns: A pointer to the #AtkAttributeSet.
355  **/
356 AtkAttributeSet* 
357 gail_misc_get_default_attributes (AtkAttributeSet *attrib_set,
358                                   PangoLayout     *layout,
359                                   GtkWidget       *widget)
360 {
361   PangoContext *context;
362   GtkStyle *style_value;
363   gint int_value;
364   PangoWrapMode mode;
365
366   attrib_set = gail_misc_add_attribute (attrib_set, 
367                                         ATK_TEXT_ATTR_DIRECTION,
368      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, 
369                                         gtk_widget_get_direction (widget))));
370
371   context = pango_layout_get_context (layout);
372   if (context)
373     {
374       PangoLanguage* language;
375       PangoFontDescription* font;
376
377       language = pango_context_get_language (context);
378       if (language)
379         {
380           attrib_set = gail_misc_add_attribute (attrib_set,
381                                                 ATK_TEXT_ATTR_LANGUAGE,
382                       g_strdup (pango_language_to_string (language)));
383         }
384       font = pango_context_get_font_description (context);
385       if (font)
386         {
387           attrib_set = gail_misc_add_attribute (attrib_set,
388                                                 ATK_TEXT_ATTR_STYLE,
389               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE,
390                                    pango_font_description_get_style (font))));
391           attrib_set = gail_misc_add_attribute (attrib_set,
392                                                 ATK_TEXT_ATTR_VARIANT,
393               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT,
394                                    pango_font_description_get_variant (font))));
395           attrib_set = gail_misc_add_attribute (attrib_set,
396                                                 ATK_TEXT_ATTR_STRETCH,
397               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH,
398                                    pango_font_description_get_stretch (font))));
399           attrib_set = gail_misc_add_attribute (attrib_set,
400                                                 ATK_TEXT_ATTR_FAMILY_NAME,
401               g_strdup (pango_font_description_get_family (font)));
402           attrib_set = gail_misc_add_attribute (attrib_set,
403                                                 ATK_TEXT_ATTR_WEIGHT,
404                     g_strdup_printf ("%d",
405                                    pango_font_description_get_weight (font)));
406           attrib_set = gail_misc_add_attribute (attrib_set,
407                                                 ATK_TEXT_ATTR_SIZE,
408                     g_strdup_printf ("%i",
409                                    pango_font_description_get_size (font) / PANGO_SCALE));
410         }
411     }
412   if (pango_layout_get_justify (layout))
413     {
414       int_value = 3;
415     }
416   else
417     {
418       PangoAlignment align;
419
420       align = pango_layout_get_alignment (layout);
421       if (align == PANGO_ALIGN_LEFT)
422         int_value = 0;
423       else if (align == PANGO_ALIGN_CENTER)
424         int_value = 2;
425       else /* if (align == PANGO_ALIGN_RIGHT) */
426         int_value = 1;
427     }
428   attrib_set = gail_misc_add_attribute (attrib_set,
429                                         ATK_TEXT_ATTR_JUSTIFICATION,
430               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, 
431                                                       int_value))); 
432   mode = pango_layout_get_wrap (layout);
433   if (mode == PANGO_WRAP_WORD)
434     int_value = 2;
435   else /* if (mode == PANGO_WRAP_CHAR) */
436     int_value = 1;
437   attrib_set = gail_misc_add_attribute (attrib_set,
438                                         ATK_TEXT_ATTR_WRAP_MODE,
439               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, 
440                                                       int_value))); 
441
442   style_value = gtk_widget_get_style (widget);
443   if (style_value)
444     {
445       GdkColor color;
446       gchar *value;
447
448       color = style_value->base[GTK_STATE_NORMAL];
449       value = g_strdup_printf ("%u,%u,%u",
450                                color.red, color.green, color.blue);
451       attrib_set = gail_misc_add_attribute (attrib_set,
452                                             ATK_TEXT_ATTR_BG_COLOR,
453                                             value); 
454       color = style_value->text[GTK_STATE_NORMAL];
455       value = g_strdup_printf ("%u,%u,%u",
456                                color.red, color.green, color.blue);
457       attrib_set = gail_misc_add_attribute (attrib_set,
458                                             ATK_TEXT_ATTR_FG_COLOR,
459                                             value); 
460     }
461   attrib_set = gail_misc_add_attribute (attrib_set,
462                                         ATK_TEXT_ATTR_FG_STIPPLE,
463               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_FG_STIPPLE, 
464                                                       0))); 
465   attrib_set = gail_misc_add_attribute (attrib_set,
466                                         ATK_TEXT_ATTR_BG_STIPPLE,
467               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_BG_STIPPLE, 
468                                                       0))); 
469   attrib_set = gail_misc_add_attribute (attrib_set,
470                                         ATK_TEXT_ATTR_STRIKETHROUGH,
471               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 
472                                                       0))); 
473   attrib_set = gail_misc_add_attribute (attrib_set,
474                                         ATK_TEXT_ATTR_UNDERLINE,
475               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, 
476                                                       0))); 
477   attrib_set = gail_misc_add_attribute (attrib_set,
478                                         ATK_TEXT_ATTR_RISE,
479                                                g_strdup_printf ("%i", 0));
480   attrib_set = gail_misc_add_attribute (attrib_set,
481                                         ATK_TEXT_ATTR_SCALE,
482                                                g_strdup_printf ("%g", 1.0));
483   attrib_set = gail_misc_add_attribute (attrib_set,
484                                         ATK_TEXT_ATTR_BG_FULL_HEIGHT,
485                                                g_strdup_printf ("%i", 0));
486   attrib_set = gail_misc_add_attribute (attrib_set,
487                                         ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP,
488                                                g_strdup_printf ("%i", 0));
489   attrib_set = gail_misc_add_attribute (attrib_set,
490                                         ATK_TEXT_ATTR_PIXELS_BELOW_LINES,
491                                         g_strdup_printf ("%i", 0));
492   attrib_set = gail_misc_add_attribute (attrib_set,
493                                         ATK_TEXT_ATTR_PIXELS_ABOVE_LINES,
494                                         g_strdup_printf ("%i", 0));
495   attrib_set = gail_misc_add_attribute (attrib_set,
496                                         ATK_TEXT_ATTR_EDITABLE,
497               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 
498                                                       0))); 
499   attrib_set = gail_misc_add_attribute (attrib_set,
500                                         ATK_TEXT_ATTR_INVISIBLE,
501               g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_INVISIBLE, 
502                                                       0))); 
503   attrib_set = gail_misc_add_attribute (attrib_set,
504                                         ATK_TEXT_ATTR_INDENT,
505                                         g_strdup_printf ("%i", 0));
506   attrib_set = gail_misc_add_attribute (attrib_set,
507                                         ATK_TEXT_ATTR_RIGHT_MARGIN,
508                                         g_strdup_printf ("%i", 0));
509   attrib_set = gail_misc_add_attribute (attrib_set,
510                                         ATK_TEXT_ATTR_LEFT_MARGIN,
511                                         g_strdup_printf ("%i", 0));
512   return attrib_set;
513 }
514
515 /**
516  * gail_misc_get_origins:
517  * @widget: a #GtkWidget
518  * @x_window: the x-origin of the widget->window
519  * @y_window: the y-origin of the widget->window
520  * @x_toplevel: the x-origin of the toplevel window for widget->window
521  * @y_toplevel: the y-origin of the toplevel window for widget->window
522  *
523  * Gets the origin of the widget window, and the origin of the
524  * widgets top-level window.
525  **/
526 void
527 gail_misc_get_origins (GtkWidget *widget,
528                        gint      *x_window,
529                        gint      *y_window,
530                        gint      *x_toplevel,
531                        gint      *y_toplevel)
532 {
533   GdkWindow *window;
534
535   if (GTK_IS_TREE_VIEW (widget))
536     window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget));
537   else
538     window = widget->window;
539   gdk_window_get_origin (window, x_window, y_window);
540   window = gdk_window_get_toplevel (widget->window);
541   gdk_window_get_origin (window, x_toplevel, y_toplevel);
542 }
543
544 /**
545  * gail_misc_add_to_attr_set:
546  * @attrib_set: An #AtkAttributeSet
547  * @attrs: The #GtkTextAttributes containing the attribute value
548  * @attr: The #AtkTextAttribute to be added
549  *
550  * Gets the value for the AtkTextAttribute from the GtkTextAttributes
551  * and adds it to the AttributeSet.
552  *
553  * Returns: A pointer to the updated #AtkAttributeSet.
554  **/
555 AtkAttributeSet*
556 gail_misc_add_to_attr_set (AtkAttributeSet   *attrib_set,
557                            GtkTextAttributes *attrs,
558                            AtkTextAttribute  attr)
559 {
560   gchar *value;
561
562   switch (attr)
563     {
564     case ATK_TEXT_ATTR_LEFT_MARGIN:
565       value = g_strdup_printf ("%i", attrs->left_margin);
566       break;
567     case ATK_TEXT_ATTR_RIGHT_MARGIN:
568       value = g_strdup_printf ("%i", attrs->right_margin);
569       break;
570     case ATK_TEXT_ATTR_INDENT:
571       value = g_strdup_printf ("%i", attrs->indent);
572       break;
573     case ATK_TEXT_ATTR_INVISIBLE:
574       value = g_strdup (atk_text_attribute_get_value (attr, attrs->invisible));
575       break;
576     case ATK_TEXT_ATTR_EDITABLE:
577       value = g_strdup (atk_text_attribute_get_value (attr, attrs->editable));
578       break;
579     case ATK_TEXT_ATTR_PIXELS_ABOVE_LINES:
580       value = g_strdup_printf ("%i", attrs->pixels_above_lines);
581       break;
582     case ATK_TEXT_ATTR_PIXELS_BELOW_LINES:
583       value = g_strdup_printf ("%i", attrs->pixels_below_lines);
584       break;
585     case ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP:
586       value = g_strdup_printf ("%i", attrs->pixels_inside_wrap);
587       break;
588     case ATK_TEXT_ATTR_BG_FULL_HEIGHT:
589       value = g_strdup (atk_text_attribute_get_value (attr, attrs->bg_full_height));
590       break;
591     case ATK_TEXT_ATTR_RISE:
592       value = g_strdup_printf ("%i", attrs->appearance.rise);
593       break;
594     case ATK_TEXT_ATTR_UNDERLINE:
595       value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.underline));
596       break;
597     case ATK_TEXT_ATTR_STRIKETHROUGH:
598       value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.strikethrough));
599       break;
600     case ATK_TEXT_ATTR_SIZE:
601       value = g_strdup_printf ("%i", 
602                               pango_font_description_get_size (attrs->font) / PANGO_SCALE);
603       break;
604     case ATK_TEXT_ATTR_SCALE:
605       value = g_strdup_printf ("%g", attrs->font_scale);
606       break;
607     case ATK_TEXT_ATTR_WEIGHT:
608       value = g_strdup_printf ("%d", 
609                               pango_font_description_get_weight (attrs->font));
610       break;
611     case ATK_TEXT_ATTR_LANGUAGE:
612       value = g_strdup ((gchar *)(attrs->language));
613       break;
614     case ATK_TEXT_ATTR_FAMILY_NAME:
615       value = g_strdup (pango_font_description_get_family (attrs->font));
616       break;
617     case ATK_TEXT_ATTR_BG_COLOR:
618       value = g_strdup_printf ("%u,%u,%u",
619                                attrs->appearance.bg_color.red,
620                                attrs->appearance.bg_color.green,
621                                attrs->appearance.bg_color.blue);
622       break;
623     case ATK_TEXT_ATTR_FG_COLOR:
624       value = g_strdup_printf ("%u,%u,%u",
625                                attrs->appearance.fg_color.red,
626                                attrs->appearance.fg_color.green,
627                                attrs->appearance.fg_color.blue);
628       break;
629     case ATK_TEXT_ATTR_BG_STIPPLE:
630       value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.bg_stipple ? 1 : 0));
631       break;
632     case ATK_TEXT_ATTR_FG_STIPPLE:
633       value = g_strdup (atk_text_attribute_get_value (attr, attrs->appearance.fg_stipple ? 1 : 0));
634       break;
635     case ATK_TEXT_ATTR_WRAP_MODE:
636       value = g_strdup (atk_text_attribute_get_value (attr, attrs->wrap_mode));
637       break;
638     case ATK_TEXT_ATTR_DIRECTION:
639       value = g_strdup (atk_text_attribute_get_value (attr, attrs->direction));
640       break;
641     case ATK_TEXT_ATTR_JUSTIFICATION:
642       value = g_strdup (atk_text_attribute_get_value (attr, attrs->justification));
643       break;
644     case ATK_TEXT_ATTR_STRETCH:
645       value = g_strdup (atk_text_attribute_get_value (attr, 
646                         pango_font_description_get_stretch (attrs->font)));
647       break;
648     case ATK_TEXT_ATTR_VARIANT:
649       value = g_strdup (atk_text_attribute_get_value (attr, 
650                         pango_font_description_get_variant (attrs->font)));
651       break;
652     case ATK_TEXT_ATTR_STYLE:
653       value = g_strdup (atk_text_attribute_get_value (attr, 
654                         pango_font_description_get_style (attrs->font)));
655       break;
656     default:
657       value = NULL;
658       break;
659     }
660   return gail_misc_add_attribute (attrib_set, attr, value);
661 }
662
663 /**
664  * gail_misc_buffer_get_run_attributes:
665  * @buffer: The #GtkTextBuffer for which the attributes will be obtained
666  * @offset: The offset at which the attributes are required
667  * @start_offset: The start offset of the current run
668  * @end_offset: The end offset of the current run
669  *
670  * Creates an AtkAttributeSet which contains the attributes for the 
671  * run starting at offset.
672  *
673  * Returns: A pointer to the #AtkAttributeSet.
674  **/
675 AtkAttributeSet*
676 gail_misc_buffer_get_run_attributes (GtkTextBuffer *buffer,
677                                      gint          offset,
678                                      gint           *start_offset,
679                                      gint          *end_offset)
680 {
681   GtkTextIter iter;
682   AtkAttributeSet *attrib_set = NULL;
683   AtkAttribute *at;
684   GSList *tags, *temp_tags;
685   gdouble scale = 1;
686   gboolean val_set = FALSE;
687   PangoFontMask mask;
688
689   gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
690
691   gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
692   *end_offset = gtk_text_iter_get_offset (&iter);
693
694   gtk_text_iter_backward_to_tag_toggle (&iter, NULL);
695   *start_offset = gtk_text_iter_get_offset (&iter);
696
697   gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
698
699   tags = gtk_text_iter_get_tags (&iter);
700   tags = g_slist_reverse (tags);
701
702   temp_tags = tags;
703   while (temp_tags && !val_set)
704     {
705       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
706       PangoFontDescription *font;
707
708       font = tag->values->font;
709
710       if (font)
711         {
712           mask = pango_font_description_get_set_fields (font);
713           val_set = mask & PANGO_FONT_MASK_STYLE;
714           if (val_set)
715             attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
716                                                     ATK_TEXT_ATTR_STYLE);
717         }
718       temp_tags = temp_tags->next;
719     }
720   val_set = FALSE;
721
722   temp_tags = tags;
723   while (temp_tags && !val_set)
724     {
725       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
726       PangoFontDescription *font;
727
728       font = tag->values->font;
729
730       if (font)
731         {
732           mask = pango_font_description_get_set_fields (font);
733           val_set = mask & PANGO_FONT_MASK_VARIANT;
734           if (val_set)
735             attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
736                                                     ATK_TEXT_ATTR_VARIANT);
737         }
738       temp_tags = temp_tags->next;
739     }
740   val_set = FALSE;
741
742   temp_tags = tags;
743   while (temp_tags && !val_set)
744     {
745       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
746       PangoFontDescription *font;
747
748       font = tag->values->font;
749
750       if (font)
751         {
752           mask = pango_font_description_get_set_fields (font);
753           val_set = mask & PANGO_FONT_MASK_STRETCH;
754           if (val_set)
755             attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
756                                                     ATK_TEXT_ATTR_STRETCH);
757         }
758       temp_tags = temp_tags->next;
759     }
760   val_set = FALSE;
761
762   temp_tags = tags;
763   while (temp_tags && !val_set)
764     {
765       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
766
767       val_set = tag->justification_set;
768       if (val_set)
769         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
770                                                 ATK_TEXT_ATTR_JUSTIFICATION);
771       temp_tags = temp_tags->next;
772     }
773   val_set = FALSE;
774
775   temp_tags = tags;
776   while (temp_tags && !val_set)
777     {
778       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
779
780       if (tag->values->direction != GTK_TEXT_DIR_NONE)
781         {
782           val_set = TRUE;
783           attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
784                                                   ATK_TEXT_ATTR_DIRECTION);
785         }
786       temp_tags = temp_tags->next;
787     }
788   val_set = FALSE;
789
790   temp_tags = tags;
791   while (temp_tags && !val_set)
792     {
793       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
794
795       val_set = tag->wrap_mode_set;
796       if (val_set)
797         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
798                                                 ATK_TEXT_ATTR_WRAP_MODE);
799       temp_tags = temp_tags->next;
800     }
801   val_set = FALSE;
802
803   temp_tags = tags;
804   while (temp_tags && !val_set)
805     {
806       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
807
808       val_set = tag->fg_stipple_set;
809       if (val_set)
810         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
811                                                 ATK_TEXT_ATTR_FG_STIPPLE);
812       temp_tags = temp_tags->next;
813     }
814   val_set = FALSE;
815
816   temp_tags = tags;
817   while (temp_tags && !val_set)
818     {
819       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
820
821       val_set = tag->bg_stipple_set;
822       if (val_set)
823         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
824                                                 ATK_TEXT_ATTR_BG_STIPPLE);
825       temp_tags = temp_tags->next;
826     }
827   val_set = FALSE;
828
829   temp_tags = tags;
830   while (temp_tags && !val_set)
831     {
832       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
833
834       val_set = tag->fg_color_set;
835       if (val_set)
836         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
837                                                 ATK_TEXT_ATTR_FG_COLOR);
838       temp_tags = temp_tags->next;
839     }
840   val_set = FALSE;
841
842   temp_tags = tags;
843   while (temp_tags && !val_set)
844     {
845       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
846   
847       val_set = tag->bg_color_set;
848       if (val_set)
849         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
850                                                 ATK_TEXT_ATTR_BG_COLOR);
851       temp_tags = temp_tags->next;
852     }
853   val_set = FALSE;
854
855   temp_tags = tags;
856   while (temp_tags && !val_set)
857     {
858       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
859       PangoFontDescription *font;
860
861       font = tag->values->font;
862
863       if (font)
864         {
865           mask = pango_font_description_get_set_fields (font);
866           val_set = mask & PANGO_FONT_MASK_FAMILY;
867           if (val_set)
868             attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
869                                                     ATK_TEXT_ATTR_FAMILY_NAME);
870         }
871       temp_tags = temp_tags->next;
872     }
873   val_set = FALSE;
874
875   temp_tags = tags;
876   while (temp_tags && !val_set)
877     {
878       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
879
880       val_set = tag->language_set;
881       if (val_set)
882         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
883                                                 ATK_TEXT_ATTR_LANGUAGE);
884       temp_tags = temp_tags->next;
885     }
886   val_set = FALSE;
887
888   temp_tags = tags;
889   while (temp_tags && !val_set)
890     {
891       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
892       PangoFontDescription *font;
893
894       font = tag->values->font;
895
896       if (font)
897         {
898           mask = pango_font_description_get_set_fields (font);
899           val_set = mask & PANGO_FONT_MASK_WEIGHT;
900           if (val_set)
901             attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
902                                                     ATK_TEXT_ATTR_WEIGHT);
903         }
904       temp_tags = temp_tags->next;
905     }
906   val_set = FALSE;
907
908
909   /*
910    * scale is special as the scale is the product of all scale values
911    * specified.
912    */
913   temp_tags = tags;
914   while (temp_tags)
915     {
916       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
917
918       if (tag->scale_set)
919         {
920           val_set = TRUE;
921           scale *= tag->values->font_scale;
922         }
923       temp_tags = temp_tags->next;
924     }
925   if (val_set)
926     {
927       at = g_malloc(sizeof(AtkAttribute));
928       at->name = g_strdup(atk_text_attribute_get_name (ATK_TEXT_ATTR_SCALE));
929       at->value = g_strdup_printf("%g", scale);
930       attrib_set = g_slist_prepend(attrib_set, at);
931     }
932   val_set = FALSE;
933
934   temp_tags = tags;
935   while (temp_tags && !val_set)
936     {
937       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
938       PangoFontDescription *font;
939
940       font = tag->values->font;
941
942       if (font)
943         {
944           mask = pango_font_description_get_set_fields (font);
945           val_set = mask & PANGO_FONT_MASK_SIZE;
946           if (val_set)
947             attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
948                                                     ATK_TEXT_ATTR_SIZE);
949         }
950       temp_tags = temp_tags->next;
951     }
952   val_set = FALSE;
953
954   temp_tags = tags;
955   while (temp_tags && !val_set)
956     {
957       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
958
959       val_set = tag->strikethrough_set;
960       if (val_set)
961         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
962                                                 ATK_TEXT_ATTR_STRIKETHROUGH);
963       temp_tags = temp_tags->next;
964     }
965   val_set = FALSE;
966
967   temp_tags = tags;
968   while (temp_tags && !val_set)
969     {
970       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
971
972       val_set = tag->underline_set;
973       if (val_set)
974         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
975                                                 ATK_TEXT_ATTR_UNDERLINE);
976       temp_tags = temp_tags->next;
977     }
978   val_set = FALSE;
979
980   temp_tags = tags;
981   while (temp_tags && !val_set)
982     {
983       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
984
985       val_set = tag->rise_set;
986       if (val_set)
987         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
988                                                 ATK_TEXT_ATTR_RISE);
989       temp_tags = temp_tags->next;
990     }
991   val_set = FALSE;
992
993   temp_tags = tags;
994   while (temp_tags && !val_set)
995     {
996       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
997
998       val_set = tag->bg_full_height_set;
999       if (val_set)
1000         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
1001                                                 ATK_TEXT_ATTR_BG_FULL_HEIGHT);
1002       temp_tags = temp_tags->next;
1003     }
1004   val_set = FALSE;
1005
1006   temp_tags = tags;
1007   while (temp_tags && !val_set)
1008     {
1009       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1010
1011       val_set = tag->pixels_inside_wrap_set;
1012       if (val_set)
1013         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
1014                                                 ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP);
1015       temp_tags = temp_tags->next;
1016     }
1017   val_set = FALSE;
1018
1019   temp_tags = tags;
1020   while (temp_tags && !val_set)
1021     {
1022       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1023
1024       val_set = tag->pixels_below_lines_set;
1025       if (val_set)
1026         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
1027                                                 ATK_TEXT_ATTR_PIXELS_BELOW_LINES);
1028       temp_tags = temp_tags->next;
1029     }
1030   val_set = FALSE;
1031
1032   temp_tags = tags;
1033   while (temp_tags && !val_set)
1034     {
1035       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1036
1037       val_set = tag->pixels_above_lines_set;
1038       if (val_set)
1039         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
1040                                                 ATK_TEXT_ATTR_PIXELS_ABOVE_LINES);
1041       temp_tags = temp_tags->next;
1042     }
1043   val_set = FALSE;
1044
1045   temp_tags = tags;
1046   while (temp_tags && !val_set)
1047     {
1048       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1049
1050       val_set = tag->editable_set;
1051       if (val_set)
1052         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
1053                                                 ATK_TEXT_ATTR_EDITABLE);
1054       temp_tags = temp_tags->next;
1055     }
1056   val_set = FALSE;
1057
1058   temp_tags = tags;
1059   while (temp_tags && !val_set)
1060     {
1061       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1062
1063       val_set = tag->invisible_set;
1064       if (val_set)
1065         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
1066                                                 ATK_TEXT_ATTR_INVISIBLE);
1067       temp_tags = temp_tags->next;
1068     }
1069   val_set = FALSE;
1070
1071   temp_tags = tags;
1072   while (temp_tags && !val_set)
1073     {
1074       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1075
1076       val_set = tag->indent_set;
1077       if (val_set)
1078         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
1079                                                 ATK_TEXT_ATTR_INDENT);
1080       temp_tags = temp_tags->next;
1081     }
1082   val_set = FALSE;
1083
1084   temp_tags = tags;
1085   while (temp_tags && !val_set)
1086     {
1087       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1088
1089       val_set = tag->right_margin_set;
1090       if (val_set)
1091         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
1092                                                 ATK_TEXT_ATTR_RIGHT_MARGIN);
1093       temp_tags = temp_tags->next;
1094     }
1095   val_set = FALSE;
1096
1097   temp_tags = tags;
1098   while (temp_tags && !val_set)
1099     {
1100       GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
1101
1102       val_set = tag->left_margin_set;
1103       if (val_set)
1104         attrib_set = gail_misc_add_to_attr_set (attrib_set, tag->values, 
1105                                                 ATK_TEXT_ATTR_LEFT_MARGIN);
1106       temp_tags = temp_tags->next;
1107     }
1108   val_set = FALSE;
1109
1110   g_slist_free (tags);
1111   return attrib_set;
1112 }