gailmisc.c 38.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* GAIL - The GNOME Accessibility Implementation Library
 * Copyright 2001 Sun Microsystems Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Javier Jardón's avatar
Javier Jardón committed
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 22 23 24 25 26 27 28
#include <stdlib.h>
#include <gtk/gtk.h>
#include "gailmisc.h"

/* IMPORTANT!!! This source file does NOT contain the implementation
 * code for AtkUtil - for that code, please see gail/gail.c.
 */

29 30 31 32 33 34 35 36 37 38 39 40
/**
 * SECTION:gailmisc
 * @Short_description: GailMisc is a set of utility functions which may be
 *   useful to implementors of Atk interfaces for custom widgets.
 * @Title: GailMisc
 *
 * GailMisc is a set of utility function which are used in the implemementation
 * of Atk interfaces for GTK+ widgets. They may be useful to implementors of
 * Atk interfaces for custom widgets.
 */


41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
/**
 * gail_misc_get_extents_from_pango_rectangle:
 * @widget: The widget that contains the PangoLayout, that contains
 *   the PangoRectangle
 * @char_rect: The #PangoRectangle from which to calculate extents
 * @x_layout: The x-offset at which the widget displays the
 *   PangoLayout that contains the PangoRectangle, relative to @widget
 * @y_layout: The y-offset at which the widget displays the
 *   PangoLayout that contains the PangoRectangle, relative to @widget
 * @x: The x-position of the #PangoRectangle relative to @coords
 * @y: The y-position of the #PangoRectangle relative to @coords
 * @width: The width of the #PangoRectangle
 * @height: The  height of the #PangoRectangle
 * @coords: An #AtkCoordType enumeration
 *
 * Gets the extents of @char_rect in device coordinates,
 * relative to either top-level window or screen coordinates as
 * specified by @coords.
 **/
void
gail_misc_get_extents_from_pango_rectangle (GtkWidget      *widget,
                                            PangoRectangle *char_rect,
                                            gint           x_layout,
                                            gint           y_layout,
                                            gint           *x,
                                            gint           *y,
                                            gint           *width,
                                            gint           *height,
                                            AtkCoordType   coords)
{
  gint x_window, y_window, x_toplevel, y_toplevel;

  gail_misc_get_origins (widget, &x_window, &y_window, 
                         &x_toplevel, &y_toplevel);

  *x = (char_rect->x / PANGO_SCALE) + x_layout + x_window;
  *y = (char_rect->y / PANGO_SCALE) + y_layout + y_window;
  if (coords == ATK_XY_WINDOW)
    {
      *x -= x_toplevel;
      *y -= y_toplevel;
    }
  else if (coords != ATK_XY_SCREEN)
    {
      *x = 0;
      *y = 0;
      *height = 0;
      *width = 0;
      return;
    }
  *height = char_rect->height / PANGO_SCALE;
  *width = char_rect->width / PANGO_SCALE;

  return;
}

/**
 * gail_misc_get_index_at_point_in_layout:
 * @widget: A #GtkWidget
 * @layout: The #PangoLayout from which to get the index at the
 *   specified point.
 * @x_layout: The x-offset at which the widget displays the
 *   #PangoLayout, relative to @widget
 * @y_layout: The y-offset at which the widget displays the
 *   #PangoLayout, relative to @widget
 * @x: The x-coordinate relative to @coords at which to
 *   calculate the index
 * @y: The y-coordinate relative to @coords at which to
 *   calculate the index
 * @coords: An #AtkCoordType enumeration
 *
 * Gets the byte offset at the specified @x and @y in a #PangoLayout.
 *
 * Returns: the byte offset at the specified @x and @y in a
 *   #PangoLayout
 **/
gint
gail_misc_get_index_at_point_in_layout (GtkWidget   *widget,
                                        PangoLayout *layout,
                                        gint        x_layout,
                                        gint        y_layout,
                                        gint        x,
                                        gint        y,
                                        AtkCoordType coords)
{
  gint index, x_window, y_window, x_toplevel, y_toplevel;
  gint x_temp, y_temp;
  gboolean ret;

  gail_misc_get_origins (widget, &x_window, &y_window, 
                         &x_toplevel, &y_toplevel);
  x_temp =  x - x_layout - x_window;
  y_temp =  y - y_layout - y_window;
  if (coords == ATK_XY_WINDOW)
    {
      x_temp += x_toplevel;  
      y_temp += y_toplevel;
    }
  else if (coords != ATK_XY_SCREEN)
    return -1;

  ret = pango_layout_xy_to_index (layout, 
                                  x_temp * PANGO_SCALE,
                                  y_temp * PANGO_SCALE,
                                  &index, NULL);
  if (!ret)
    {
      if (x_temp < 0 || y_temp < 0)
        index = 0;
      else
        index = -1; 
    }
  return index;
}

/**
 * gail_misc_add_attribute:
 * @attrib_set: The #AtkAttributeSet to add the attribute to
 * @attr: The AtkTextAttrribute which identifies the attribute to be added
 * @value: The attribute value
 *
 * Creates an #AtkAttribute from @attr and @value, and adds it
 * to @attrib_set. 
 *
 * Returns: A pointer to the new #AtkAttributeSet.
 **/
AtkAttributeSet*
gail_misc_add_attribute (AtkAttributeSet *attrib_set,
                         AtkTextAttribute attr,
                         gchar           *value)
{
  AtkAttributeSet *return_set;
  AtkAttribute *at = g_malloc (sizeof (AtkAttribute));
  at->name = g_strdup (atk_text_attribute_get_name (attr));
  at->value = value;
  return_set = g_slist_prepend(attrib_set, at);
  return return_set;
}

/**
 * gail_misc_layout_get_run_attributes:
 * @attrib_set: The #AtkAttributeSet to add the attribute to
 * @layout: The PangoLayout from which the attributes will be obtained
 * @text: The text 
 * @offset: The offset at which the attributes are required
 * @start_offset: The start offset of the current run
 * @end_offset: The end offset of the current run
 *
 * Adds the attributes for the run starting at offset to the specified
 * attribute set.
 *
 * Returns: A pointer to the #AtkAttributeSet.
 **/
AtkAttributeSet* 
gail_misc_layout_get_run_attributes (AtkAttributeSet *attrib_set,
                                     PangoLayout     *layout,
197
                                     const gchar     *text,
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
                                     gint            offset,
                                     gint            *start_offset,
                                     gint            *end_offset)
{
  PangoAttrIterator *iter;
  PangoAttrList *attr;  
  PangoAttrString *pango_string;
  PangoAttrInt *pango_int;
  PangoAttrColor *pango_color;
  PangoAttrLanguage *pango_lang;
  PangoAttrFloat *pango_float;
  gint index, start_index, end_index;
  gboolean is_next = TRUE;
  gchar *value = NULL;
  glong len;

  len = g_utf8_strlen (text, -1);
  /* Grab the attributes of the PangoLayout, if any */
  if ((attr = pango_layout_get_attributes (layout)) == NULL)
    {
      *start_offset = 0;
      *end_offset = len;
      return attrib_set;
    }
  iter = pango_attr_list_get_iterator (attr);
  /* Get invariant range offsets */
  /* If offset out of range, set offset in range */
  if (offset > len)
    offset = len;
  else if (offset < 0)
    offset = 0;

  index = g_utf8_offset_to_pointer (text, offset) - text;
  pango_attr_iterator_range (iter, &start_index, &end_index);
  while (is_next)
    {
      if (index >= start_index && index < end_index)
        {
          *start_offset = g_utf8_pointer_to_offset (text, 
                                                    text + start_index);  
          if (end_index == G_MAXINT)
          /* Last iterator */
            end_index = len;
      
          *end_offset = g_utf8_pointer_to_offset (text, 
                                                  text + end_index);  
          break;
        }  
      is_next = pango_attr_iterator_next (iter);
      pango_attr_iterator_range (iter, &start_index, &end_index);
    }
  /* Get attributes */
  if ((pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_FAMILY)) != NULL)
    {
      value = g_strdup_printf("%s", pango_string->value);
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_FAMILY_NAME, 
                                            value);
    } 
  if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_STYLE)) != NULL)
    {
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_STYLE, 
      g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value)));
    } 
  if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_WEIGHT)) != NULL)
    {
      value = g_strdup_printf("%i", pango_int->value);
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_WEIGHT, 
                                            value);
    } 
  if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_VARIANT)) != NULL)
    {
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_VARIANT, 
       g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value)));
    } 
  if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_STRETCH)) != NULL)
    {
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_STRETCH, 
       g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value)));
    } 
  if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_SIZE)) != NULL)
    {
      value = g_strdup_printf("%i", pango_int->value / PANGO_SCALE);
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_SIZE,
                                            value);
    } 
  if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_UNDERLINE)) != NULL)
    {
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_UNDERLINE, 
       g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value)));
    } 
  if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_STRIKETHROUGH)) != NULL)
    {
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_STRIKETHROUGH, 
       g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value)));
    } 
  if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_RISE)) != NULL)
    {
      value = g_strdup_printf("%i", pango_int->value);
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_RISE,
                                            value);
    } 
  if ((pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_LANGUAGE)) != NULL)
    {
      value = g_strdup( pango_language_to_string( pango_lang->value));
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_LANGUAGE, 
                                            value);
    } 
  if ((pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, 
                                   PANGO_ATTR_SCALE)) != NULL)
    {
      value = g_strdup_printf("%g", pango_float->value);
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_SCALE, 
                                            value);
    } 
  if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, 
                                    PANGO_ATTR_FOREGROUND)) != NULL)
    {
      value = g_strdup_printf ("%u,%u,%u", 
                               pango_color->color.red, 
                               pango_color->color.green, 
                               pango_color->color.blue);
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_FG_COLOR, 
                                            value);
    } 
  if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, 
                                     PANGO_ATTR_BACKGROUND)) != NULL)
    {
      value = g_strdup_printf ("%u,%u,%u", 
                               pango_color->color.red, 
                               pango_color->color.green, 
                               pango_color->color.blue);
      attrib_set = gail_misc_add_attribute (attrib_set, 
                                            ATK_TEXT_ATTR_BG_COLOR, 
                                            value);
    } 
  pango_attr_iterator_destroy (iter);
  return attrib_set;
}

/**
 * gail_misc_get_default_attributes:
 * @attrib_set: The #AtkAttributeSet to add the attribute to
 * @layout: The PangoLayout from which the attributes will be obtained
 * @widget: The GtkWidget for which the default attributes are required.
 *
 * Adds the default attributes to the specified attribute set.
 *
 * Returns: A pointer to the #AtkAttributeSet.
 **/
AtkAttributeSet* 
gail_misc_get_default_attributes (AtkAttributeSet *attrib_set,
                                  PangoLayout     *layout,
                                  GtkWidget       *widget)
{
  PangoContext *context;
375
  GtkStyleContext *style_context;
376 377
  gint int_value;
  PangoWrapMode mode;
378 379
  GdkRGBA color;
  gchar *value;
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456

  attrib_set = gail_misc_add_attribute (attrib_set, 
                                        ATK_TEXT_ATTR_DIRECTION,
     g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_DIRECTION, 
                                        gtk_widget_get_direction (widget))));

  context = pango_layout_get_context (layout);
  if (context)
    {
      PangoLanguage* language;
      PangoFontDescription* font;

      language = pango_context_get_language (context);
      if (language)
        {
          attrib_set = gail_misc_add_attribute (attrib_set,
                                                ATK_TEXT_ATTR_LANGUAGE,
                      g_strdup (pango_language_to_string (language)));
        }
      font = pango_context_get_font_description (context);
      if (font)
        {
          attrib_set = gail_misc_add_attribute (attrib_set,
                                                ATK_TEXT_ATTR_STYLE,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE,
                                   pango_font_description_get_style (font))));
          attrib_set = gail_misc_add_attribute (attrib_set,
                                                ATK_TEXT_ATTR_VARIANT,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT,
                                   pango_font_description_get_variant (font))));
          attrib_set = gail_misc_add_attribute (attrib_set,
                                                ATK_TEXT_ATTR_STRETCH,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH,
                                   pango_font_description_get_stretch (font))));
          attrib_set = gail_misc_add_attribute (attrib_set,
                                                ATK_TEXT_ATTR_FAMILY_NAME,
              g_strdup (pango_font_description_get_family (font)));
          attrib_set = gail_misc_add_attribute (attrib_set,
                                                ATK_TEXT_ATTR_WEIGHT,
                    g_strdup_printf ("%d",
                                   pango_font_description_get_weight (font)));
          attrib_set = gail_misc_add_attribute (attrib_set,
                                                ATK_TEXT_ATTR_SIZE,
                    g_strdup_printf ("%i",
                                   pango_font_description_get_size (font) / PANGO_SCALE));
        }
    }
  if (pango_layout_get_justify (layout))
    {
      int_value = 3;
    }
  else
    {
      PangoAlignment align;

      align = pango_layout_get_alignment (layout);
      if (align == PANGO_ALIGN_LEFT)
        int_value = 0;
      else if (align == PANGO_ALIGN_CENTER)
        int_value = 2;
      else /* if (align == PANGO_ALIGN_RIGHT) */
        int_value = 1;
    }
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_JUSTIFICATION,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, 
                                                      int_value))); 
  mode = pango_layout_get_wrap (layout);
  if (mode == PANGO_WRAP_WORD)
    int_value = 2;
  else /* if (mode == PANGO_WRAP_CHAR) */
    int_value = 1;
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_WRAP_MODE,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, 
                                                      int_value))); 

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
  style_context = gtk_widget_get_style_context (widget);

  gtk_style_context_get_background_color (style_context, 0, &color);
  value = g_strdup_printf ("%u,%u,%u",
                           (guint) ceil (color.red * 65536 - color.red),
                           (guint) ceil (color.green * 65536 - color.green),
                           (guint) ceil (color.blue * 65536 - color.blue));
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_BG_COLOR,
                                        value); 

  gtk_style_context_get_color (style_context, 0, &color);
  value = g_strdup_printf ("%u,%u,%u",
                           (guint) ceil (color.red * 65536 - color.red),
                           (guint) ceil (color.green * 65536 - color.green),
                           (guint) ceil (color.blue * 65536 - color.blue));
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_FG_COLOR,
                                        value); 
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553

  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_FG_STIPPLE,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_FG_STIPPLE, 
                                                      0))); 
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_BG_STIPPLE,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_BG_STIPPLE, 
                                                      0))); 
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_STRIKETHROUGH,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 
                                                      0))); 
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_UNDERLINE,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, 
                                                      0))); 
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_RISE,
                                               g_strdup_printf ("%i", 0));
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_SCALE,
                                               g_strdup_printf ("%g", 1.0));
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_BG_FULL_HEIGHT,
                                               g_strdup_printf ("%i", 0));
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP,
                                               g_strdup_printf ("%i", 0));
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_PIXELS_BELOW_LINES,
                                        g_strdup_printf ("%i", 0));
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_PIXELS_ABOVE_LINES,
                                        g_strdup_printf ("%i", 0));
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_EDITABLE,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 
                                                      0))); 
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_INVISIBLE,
              g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_INVISIBLE, 
                                                      0))); 
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_INDENT,
                                        g_strdup_printf ("%i", 0));
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_RIGHT_MARGIN,
                                        g_strdup_printf ("%i", 0));
  attrib_set = gail_misc_add_attribute (attrib_set,
                                        ATK_TEXT_ATTR_LEFT_MARGIN,
                                        g_strdup_printf ("%i", 0));
  return attrib_set;
}

/**
 * gail_misc_get_origins:
 * @widget: a #GtkWidget
 * @x_window: the x-origin of the widget->window
 * @y_window: the y-origin of the widget->window
 * @x_toplevel: the x-origin of the toplevel window for widget->window
 * @y_toplevel: the y-origin of the toplevel window for widget->window
 *
 * Gets the origin of the widget window, and the origin of the
 * widgets top-level window.
 **/
void
gail_misc_get_origins (GtkWidget *widget,
                       gint      *x_window,
                       gint      *y_window,
                       gint      *x_toplevel,
                       gint      *y_toplevel)
{
  GdkWindow *window;

  if (GTK_IS_TREE_VIEW (widget))
    window = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget));
  else
554 555
    window = gtk_widget_get_window (widget);

556
  gdk_window_get_origin (window, x_window, y_window);
557
  window = gdk_window_get_toplevel (gtk_widget_get_window (widget));
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
  gdk_window_get_origin (window, x_toplevel, y_toplevel);
}

/**
 * gail_misc_buffer_get_run_attributes:
 * @buffer: The #GtkTextBuffer for which the attributes will be obtained
 * @offset: The offset at which the attributes are required
 * @start_offset: The start offset of the current run
 * @end_offset: The end offset of the current run
 *
 * Creates an AtkAttributeSet which contains the attributes for the 
 * run starting at offset.
 *
 * Returns: A pointer to the #AtkAttributeSet.
 **/
AtkAttributeSet*
gail_misc_buffer_get_run_attributes (GtkTextBuffer *buffer,
                                     gint          offset,
                                     gint	    *start_offset,
                                     gint          *end_offset)
{
  GtkTextIter iter;
  AtkAttributeSet *attrib_set = NULL;
  AtkAttribute *at;
  GSList *tags, *temp_tags;
  gdouble scale = 1;
  gboolean val_set = FALSE;

  gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);

  gtk_text_iter_forward_to_tag_toggle (&iter, NULL);
  *end_offset = gtk_text_iter_get_offset (&iter);

  gtk_text_iter_backward_to_tag_toggle (&iter, NULL);
  *start_offset = gtk_text_iter_get_offset (&iter);

  gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);

  tags = gtk_text_iter_get_tags (&iter);
  tags = g_slist_reverse (tags);

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

604 605
      g_object_get (tag, "style-set", &val_set, NULL);
      if (val_set)
606
        {
607 608 609 610 611
          PangoStyle style;
          gchar *value;

          g_object_get (tag, "style", &style, NULL);
          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 615 616 617 618 619 620 621 622
        }
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

623 624
      g_object_get (tag, "variant-set", &val_set, NULL);
      if (val_set)
625
        {
626 627 628 629 630
          PangoVariant variant;
          gchar *value;

          g_object_get (tag, "variant", &variant, NULL);
          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 634 635 636 637 638 639 640 641
        }
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

642 643
      g_object_get (tag, "stretch-set", &val_set, NULL);
      if (val_set)
644
        {
645 646 647 648 649
          PangoStretch stretch;
          gchar *value;

          g_object_get (tag, "stretch", &stretch, NULL);
          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 653 654 655 656 657 658 659 660
        }
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

661
      g_object_get (tag, "justification-set", &val_set, NULL);
662
      if (val_set)
663 664 665 666 667 668
        {
          GtkJustification justification;
          gchar *value;

          g_object_get (tag, "justification", &justification, NULL);
          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 672 673 674 675 676 677 678
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
679 680 681
      GtkTextDirection direction;

      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 691 692 693 694 695 696 697 698
        }
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

699
      g_object_get (tag, "wrap-mode-set", &val_set, NULL);
700
      if (val_set)
701 702 703 704 705 706
        {
          GtkWrapMode wrap_mode;
          gchar *value;

          g_object_get (tag, "wrap-mode", &wrap_mode, NULL);
          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 710 711 712 713 714 715 716 717
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

718
      g_object_get (tag, "foreground-set", &val_set, NULL);
719
      if (val_set)
720
        {
721
          GdkRGBA *rgba;
722 723
          gchar *value;

724 725 726 727 728 729
          g_object_get (tag, "foreground-rgba", &rgba, NULL);
          value = g_strdup_printf ("%u,%u,%u",
                                   (guint) rgba->red * 65535,
                                   (guint) rgba->green * 65535,
                                   (guint) rgba->blue * 65535);
          gdk_rgba_free (rgba);
730
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FG_COLOR, value);
731
        }
732 733 734 735 736 737 738 739
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      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 746
          gchar *value;

747 748 749 750 751 752
          g_object_get (tag, "background-rgba", &rgba, NULL);
          value = g_strdup_printf ("%u,%u,%u",
                                   (guint) rgba->red * 65535,
                                   (guint) rgba->green * 65535,
                                   (guint) rgba->blue * 65535);
          gdk_rgba_free (rgba);
753
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_BG_COLOR, value);
754
        }
755 756 757 758 759 760 761 762 763
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

764
      g_object_get (tag, "family-set", &val_set, NULL);
765

766
      if (val_set)
767
        {
768 769
          gchar *value;
          g_object_get (tag, "family", &value, NULL);
770
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FAMILY_NAME, value);
771 772 773 774 775 776 777 778 779 780
        }
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

781 782
      g_object_get (tag, "language-set", &val_set, NULL);

783
      if (val_set)
784 785 786
        {
          gchar *value;
          g_object_get (tag, "language", &value, NULL);
787
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_LANGUAGE, value);
788
        }
789 790 791 792 793 794 795 796 797
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

798
      g_object_get (tag, "weight-set", &val_set, NULL);
799

800
      if (val_set)
801
        {
802 803 804 805 806
          gint weight;
          gchar *value;

          g_object_get (tag, "weight", &weight, NULL);
          value = g_strdup_printf ("%d", weight);
807
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_WEIGHT, value);
808 809 810 811 812 813 814 815 816 817 818 819 820 821
        }
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;


  /*
   * scale is special as the scale is the product of all scale values
   * specified.
   */
  temp_tags = tags;
  while (temp_tags)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);
822
      gboolean scale_set;
823

824 825
      g_object_get (tag, "scale-set", &scale_set, NULL);
      if (scale_set)
826
        {
827 828 829
          gdouble font_scale;

          g_object_get (tag, "scale", &font_scale, NULL);
830
          val_set = TRUE;
831
          scale *= font_scale;
832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
        }
      temp_tags = temp_tags->next;
    }
  if (val_set)
    {
      at = g_malloc(sizeof(AtkAttribute));
      at->name = g_strdup(atk_text_attribute_get_name (ATK_TEXT_ATTR_SCALE));
      at->value = g_strdup_printf("%g", scale);
      attrib_set = g_slist_prepend(attrib_set, at);
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

849 850
      g_object_get (tag, "size-set", &val_set, NULL);
      if (val_set)
851
        {
852 853 854 855
          gint size;
          gchar *value;
          g_object_get (tag, "size", &size, NULL);
          value = g_strdup_printf ("%i", size);
856
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_SIZE, value);
857 858 859 860 861 862 863 864 865 866
        }
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

867
      g_object_get (tag, "strikethrough-set", &val_set, NULL);
868
      if (val_set)
869 870 871 872 873
        {
          gboolean strikethrough;
          gchar *value;
          g_object_get (tag, "strikethrough", &strikethrough, NULL);
          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 877 878 879 880 881 882 883 884
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

885
      g_object_get (tag, "underline-set", &val_set, NULL);
886
      if (val_set)
887 888 889 890 891
        {
          PangoUnderline underline;
          gchar *value;
          g_object_get (tag, "underline", &underline, NULL);
          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 895 896 897 898 899 900 901 902
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

903
      g_object_get (tag, "rise-set", &val_set, NULL);
904
      if (val_set)
905 906 907 908 909
        {
          gint rise;
          gchar *value;
          g_object_get (tag, "rise", &rise, NULL);
          value = g_strdup_printf ("%i", rise);
910
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_RISE, value);
911
        }
912 913 914 915 916 917 918 919 920
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

921
      g_object_get (tag, "background-full-height-set", &val_set, NULL);
922
      if (val_set)
923 924 925 926 927
        {
          gboolean bg_full_height;
          gchar *value;
          g_object_get (tag, "background-full-height", &bg_full_height, NULL);
          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 931 932 933 934 935 936 937 938
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

939
      g_object_get (tag, "pixels-inside-wrap-set", &val_set, NULL);
940
      if (val_set)
941 942 943 944 945
        {
          gint pixels;
          gchar *value;
          g_object_get (tag, "pixels-inside-wrap", &pixels, NULL);
          value = g_strdup_printf ("%i", pixels);
946
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, value);
947
        }
948 949 950 951 952 953 954 955 956
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

957
      g_object_get (tag, "pixels-below-lines-set", &val_set, NULL);
958
      if (val_set)
959 960 961 962 963
        {
          gint pixels;
          gchar *value;
          g_object_get (tag, "pixels-below-lines", &pixels, NULL);
          value = g_strdup_printf ("%i", pixels);
964
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, value);
965
        }
966 967 968 969 970 971 972 973 974
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

975
      g_object_get (tag, "pixels-above-lines-set", &val_set, NULL);
976
      if (val_set)
977 978 979 980 981
        {
          gint pixels;
          gchar *value;
          g_object_get (tag, "pixels-above-lines", &pixels, NULL);
          value = g_strdup_printf ("%i", pixels);
982
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, value);
983
        }
984 985 986 987 988 989 990 991 992
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

993
      g_object_get (tag, "editable-set", &val_set, NULL);
994
      if (val_set)
995 996 997 998 999
        {
          gboolean editable;
          gchar *value;
          g_object_get (tag, "editable", &editable, NULL);
          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 1003 1004 1005 1006 1007 1008 1009 1010
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

1011
      g_object_get (tag, "invisible-set", &val_set, NULL);
1012
      if (val_set)
1013 1014 1015 1016 1017
        {
          gboolean invisible;
          gchar *value;
          g_object_get (tag, "invisible", &invisible, NULL);
          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 1021 1022 1023 1024 1025 1026 1027 1028
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

1029
      g_object_get (tag, "indent-set", &val_set, NULL);
1030
      if (val_set)
1031 1032 1033 1034 1035
        {
          gint indent;
          gchar *value;
          g_object_get (tag, "indent", &indent, NULL);
          value = g_strdup_printf ("%i", indent);
1036
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_INDENT, value);
1037
        }
1038 1039 1040 1041 1042 1043 1044 1045 1046
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

1047
      g_object_get (tag, "right-margin-set", &val_set, NULL);
1048
      if (val_set)
1049 1050 1051 1052 1053
        {
          gint margin;
          gchar *value;
          g_object_get (tag, "right-margin", &margin, NULL);
          value = g_strdup_printf ("%i", margin);
1054
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_RIGHT_MARGIN, value);
1055
        }
1056 1057 1058 1059 1060 1061 1062 1063 1064
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  temp_tags = tags;
  while (temp_tags && !val_set)
    {
      GtkTextTag *tag = GTK_TEXT_TAG (temp_tags->data);

1065
      g_object_get (tag, "left-margin-set", &val_set, NULL);
1066
      if (val_set)
1067 1068 1069 1070 1071
        {
          gint margin;
          gchar *value;
          g_object_get (tag, "left-margin", &margin, NULL);
          value = g_strdup_printf ("%i", margin);
1072
          attrib_set = gail_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_LEFT_MARGIN, value);
1073
        }
1074 1075 1076 1077 1078 1079 1080
      temp_tags = temp_tags->next;
    }
  val_set = FALSE;

  g_slist_free (tags);
  return attrib_set;
}