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