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