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