gtkwidgetpath.c 32.3 KB
Newer Older
Carlos Garnacho's avatar
Carlos Garnacho committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/* GTK - The GIMP Toolkit
 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"

#include <string.h>

#include "gtkwidget.h"
#include "gtkwidgetpath.h"
26
#include "gtkstylecontextprivate.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
27

Carlos Garnacho's avatar
Carlos Garnacho committed
28 29 30 31 32 33
/**
 * SECTION:gtkwidgetpath
 * @Short_description: Widget path abstraction
 * @Title: GtkWidgetPath
 * @See_also: #GtkStyleContext
 *
Matthias Clasen's avatar
Matthias Clasen committed
34
 * GtkWidgetPath is a boxed type that represents a widget hierarchy from
Carlos Garnacho's avatar
Carlos Garnacho committed
35 36 37 38
 * the topmost widget, typically a toplevel, to any child. This widget
 * path abstraction is used in #GtkStyleContext on behalf of the real
 * widget in order to query style information.
 *
39 40 41 42
 * If you are using GTK+ widgets, you probably will not need to use
 * this API directly, as there is gtk_widget_get_path(), and the style
 * context returned by gtk_widget_get_style_context() will be automatically
 * updated on widget hierarchy changes.
Carlos Garnacho's avatar
Carlos Garnacho committed
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
 *
 * The widget path generation is generally simple:
 * <example>
 * <title>Defining a button within a window</title>
 * <programlisting>
 * {
 *   GtkWidgetPath *path;
 *
 *   path = gtk_widget_path_new ();
 *   gtk_widget_path_append_type (path, GTK_TYPE_WINDOW);
 *   gtk_widget_path_append_type (path, GTK_TYPE_BUTTON);
 * }
 * </programlisting>
 * </example>
 *
 * Although more complex information, such as widget names, or
 * different classes (property that may be used by other widget
 * types) and intermediate regions may be included:
 *
 * <example>
 * <title>Defining the first tab widget in a notebook</title>
 * <programlisting>
 * {
 *   GtkWidgetPath *path;
 *   guint pos;
 *
 *   path = gtk_widget_path_new ();
 *
 *   pos = gtk_widget_path_append_type (path, GTK_TYPE_NOTEBOOK);
 *   gtk_widget_path_iter_add_region (path, pos, "tab", GTK_REGION_EVEN | GTK_REGION_FIRST);
 *
 *   pos = gtk_widget_path_append_type (path, GTK_TYPE_LABEL);
 *   gtk_widget_path_iter_set_name (path, pos, "first tab label");
 * }
 * </programlisting>
 * </example>
 *
 * All this information will be used to match the style information
 * that applies to the described widget.
 **/

84
G_DEFINE_BOXED_TYPE (GtkWidgetPath, gtk_widget_path,
85
		     gtk_widget_path_ref, gtk_widget_path_unref)
86 87


Carlos Garnacho's avatar
Carlos Garnacho committed
88 89 90 91 92
typedef struct GtkPathElement GtkPathElement;

struct GtkPathElement
{
  GType type;
93
  GQuark name;
94
  GHashTable *regions;
95
  GArray *classes;
96 97
  GtkWidgetPath *siblings;
  guint sibling_index;
Carlos Garnacho's avatar
Carlos Garnacho committed
98 99
};

Carlos Garnacho's avatar
Carlos Garnacho committed
100
struct _GtkWidgetPath
Carlos Garnacho's avatar
Carlos Garnacho committed
101
{
102 103
  volatile guint ref_count;

104
  GArray *elems; /* First element contains the described widget */
Carlos Garnacho's avatar
Carlos Garnacho committed
105 106
};

Carlos Garnacho's avatar
Carlos Garnacho committed
107 108 109 110 111 112 113 114 115
/**
 * gtk_widget_path_new:
 *
 * Returns an empty widget path.
 *
 * Returns: (transfer full): A newly created, empty, #GtkWidgetPath
 *
 * Since: 3.0
 **/
Carlos Garnacho's avatar
Carlos Garnacho committed
116 117 118 119 120 121
GtkWidgetPath *
gtk_widget_path_new (void)
{
  GtkWidgetPath *path;

  path = g_slice_new0 (GtkWidgetPath);
122
  path->elems = g_array_new (FALSE, TRUE, sizeof (GtkPathElement));
123
  path->ref_count = 1;
Carlos Garnacho's avatar
Carlos Garnacho committed
124 125 126 127

  return path;
}

128 129 130 131 132 133 134 135
static void
gtk_path_element_copy (GtkPathElement       *dest,
                       const GtkPathElement *src)
{
  memset (dest, 0, sizeof (GtkPathElement));

  dest->type = src->type;
  dest->name = src->name;
136 137 138
  if (src->siblings)
    dest->siblings = gtk_widget_path_ref (src->siblings);
  dest->sibling_index = src->sibling_index;
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

  if (src->regions)
    {
      GHashTableIter iter;
      gpointer key, value;

      g_hash_table_iter_init (&iter, src->regions);
      dest->regions = g_hash_table_new (NULL, NULL);

      while (g_hash_table_iter_next (&iter, &key, &value))
        g_hash_table_insert (dest->regions, key, value);
    }

  if (src->classes)
    {
      dest->classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
      g_array_append_vals (dest->classes, src->classes->data, src->classes->len);
    }
}

Carlos Garnacho's avatar
Carlos Garnacho committed
159 160 161 162 163 164 165 166 167 168
/**
 * gtk_widget_path_copy:
 * @path: a #GtkWidgetPath
 *
 * Returns a copy of @path
 *
 * Returns: (transfer full): a copy of @path
 *
 * Since: 3.0
 **/
169 170
GtkWidgetPath *
gtk_widget_path_copy (const GtkWidgetPath *path)
Carlos Garnacho's avatar
Carlos Garnacho committed
171
{
172 173
  GtkWidgetPath *new_path;
  guint i;
Carlos Garnacho's avatar
Carlos Garnacho committed
174

175
  g_return_val_if_fail (path != NULL, NULL);
Carlos Garnacho's avatar
Carlos Garnacho committed
176

177
  new_path = gtk_widget_path_new ();
Carlos Garnacho's avatar
Carlos Garnacho committed
178

179 180
  for (i = 0; i < path->elems->len; i++)
    {
181
      GtkPathElement *elem, new;
182 183 184

      elem = &g_array_index (path->elems, GtkPathElement, i);

185
      gtk_path_element_copy (&new, elem);
186

187 188 189 190
      g_array_append_val (new_path->elems, new);
    }

  return new_path;
Carlos Garnacho's avatar
Carlos Garnacho committed
191 192
}

Carlos Garnacho's avatar
Carlos Garnacho committed
193
/**
194
 * gtk_widget_path_ref:
Carlos Garnacho's avatar
Carlos Garnacho committed
195 196
 * @path: a #GtkWidgetPath
 *
197
 * Increments the reference count on @path.
Carlos Garnacho's avatar
Carlos Garnacho committed
198
 *
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
 * Returns: @path itself.
 *
 * Since: 3.2
 **/
GtkWidgetPath *
gtk_widget_path_ref (GtkWidgetPath *path)
{
  g_return_val_if_fail (path != NULL, path);

  g_atomic_int_add (&path->ref_count, 1);

  return path;
}

/**
 * gtk_widget_path_unref:
 * @path: a #GtkWidgetPath
 *
 * Decrements the reference count on @path, freeing the structure
 * if the reference count reaches 0.
 *
 * Since: 3.2
Carlos Garnacho's avatar
Carlos Garnacho committed
221
 **/
Carlos Garnacho's avatar
Carlos Garnacho committed
222
void
223
gtk_widget_path_unref (GtkWidgetPath *path)
Carlos Garnacho's avatar
Carlos Garnacho committed
224
{
225 226
  guint i;

Carlos Garnacho's avatar
Carlos Garnacho committed
227 228
  g_return_if_fail (path != NULL);

229 230 231
  if (!g_atomic_int_dec_and_test (&path->ref_count))
    return;

232
  for (i = 0; i < path->elems->len; i++)
Carlos Garnacho's avatar
Carlos Garnacho committed
233
    {
234 235 236
      GtkPathElement *elem;

      elem = &g_array_index (path->elems, GtkPathElement, i);
237 238 239

      if (elem->regions)
        g_hash_table_destroy (elem->regions);
Carlos Garnacho's avatar
Carlos Garnacho committed
240 241 242

      if (elem->classes)
        g_array_free (elem->classes, TRUE);
243 244 245

      if (elem->siblings)
        gtk_widget_path_unref (elem->siblings);
Carlos Garnacho's avatar
Carlos Garnacho committed
246
    }
247 248 249

  g_array_free (path->elems, TRUE);
  g_slice_free (GtkWidgetPath, path);
Carlos Garnacho's avatar
Carlos Garnacho committed
250 251
}

252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
/**
 * gtk_widget_path_free:
 * @path: a #GtkWidgetPath
 *
 * Decrements the reference count on @path, freeing the structure
 * if the reference count reaches 0.
 *
 * Since: 3.0
 **/
void
gtk_widget_path_free (GtkWidgetPath *path)
{
  g_return_if_fail (path != NULL);

  gtk_widget_path_unref (path);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
269 270 271 272 273 274 275 276 277 278 279
/**
 * gtk_widget_path_length:
 * @path: a #GtkWidgetPath
 *
 * Returns the number of #GtkWidget #GTypes between the represented
 * widget and its topmost container.
 *
 * Returns: the number of elements in the path
 *
 * Since: 3.0
 **/
280
gint
281
gtk_widget_path_length (const GtkWidgetPath *path)
Carlos Garnacho's avatar
Carlos Garnacho committed
282
{
283
  g_return_val_if_fail (path != NULL, 0);
Carlos Garnacho's avatar
Carlos Garnacho committed
284

285 286
  return path->elems->len;
}
Carlos Garnacho's avatar
Carlos Garnacho committed
287

288 289 290 291 292 293 294
/**
 * gtk_widget_path_to_string:
 * @path: the path
 *
 * Dumps the widget path into a string representation. It tries to match
 * the CSS style as closely as possible (Note that there might be paths
 * that cannot be represented in CSS).
295
 *
296 297 298 299
 * The main use of this code is for debugging purposes, so that you can
 * g_print() the path or dump it in a gdb session.
 *
 * Returns: A new string describing @path.
300 301
 *
 * Since: 3.2
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
 **/
char *
gtk_widget_path_to_string (const GtkWidgetPath *path)
{
  GString *string;
  guint i, j;

  g_return_val_if_fail (path != NULL, NULL);

  string = g_string_new ("");

  for (i = 0; i < path->elems->len; i++)
    {
      GtkPathElement *elem;

      elem = &g_array_index (path->elems, GtkPathElement, i);

      if (i > 0)
        g_string_append_c (string, ' ');

      g_string_append (string, g_type_name (elem->type));

      if (elem->name)
        {
          g_string_append_c (string, '(');
          g_string_append (string, g_quark_to_string (elem->name));
          g_string_append_c (string, ')');
        }

331 332 333

      if (elem->siblings)
        g_string_append_printf (string, "[%d/%d]",
334
                                elem->sibling_index + 1,
335 336
                                gtk_widget_path_length (elem->siblings));

337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
      if (elem->classes)
        {
          for (j = 0; j < elem->classes->len; j++)
            {
              g_string_append_c (string, '.');
              g_string_append (string, g_quark_to_string (g_array_index (elem->classes, GQuark, j)));
            }
        }

      if (elem->regions)
        {
          GHashTableIter iter;
          gpointer key, value;

          g_hash_table_iter_init (&iter, elem->regions);
          while (g_hash_table_iter_next (&iter, &key, &value))
            {
              GtkRegionFlags flags = GPOINTER_TO_UINT (value);
              static const char *flag_names[] = {
                "even",
                "odd",
                "first",
                "last",
360
                "only",
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
                "sorted"
              };

              g_string_append_c (string, ' ');
              g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (key)));
              for (j = 0; j < G_N_ELEMENTS(flag_names); j++)
                {
                  if (flags & (1 << j))
                    {
                      g_string_append_c (string, ':');
                      g_string_append (string, flag_names[j]);
                    }
                }
            }
        }
    }

  return g_string_free (string, FALSE);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
381 382 383 384 385 386 387 388 389
/**
 * gtk_widget_path_prepend_type:
 * @path: a #GtkWidgetPath
 * @type: widget type to prepend
 *
 * Prepends a widget type to the widget hierachy represented by @path.
 *
 * Since: 3.0
 **/
390
void
391 392 393 394
gtk_widget_path_prepend_type (GtkWidgetPath *path,
                              GType          type)
{
  GtkPathElement new = { 0 };
Carlos Garnacho's avatar
Carlos Garnacho committed
395

396
  g_return_if_fail (path != NULL);
Carlos Garnacho's avatar
Carlos Garnacho committed
397

398 399 400 401
  new.type = type;
  g_array_prepend_val (path->elems, new);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
402 403 404 405 406
/**
 * gtk_widget_path_append_type:
 * @path: a #GtkWidgetPath
 * @type: widget type to append
 *
Benjamin Otte's avatar
Benjamin Otte committed
407
 * Appends a widget type to the widget hierarchy represented by @path.
Carlos Garnacho's avatar
Carlos Garnacho committed
408 409 410 411 412
 *
 * Returns: the position where the element was inserted
 *
 * Since: 3.0
 **/
413
gint
414 415 416 417 418 419 420
gtk_widget_path_append_type (GtkWidgetPath *path,
                             GType          type)
{
  GtkPathElement new = { 0 };

  g_return_val_if_fail (path != NULL, 0);

421 422
  new.type = type;
  g_array_append_val (path->elems, new);
Carlos Garnacho's avatar
Carlos Garnacho committed
423

424
  return path->elems->len - 1;
Carlos Garnacho's avatar
Carlos Garnacho committed
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 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 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
/**
 * gtk_widget_path_append_with_siblings:
 * @path: the widget path to append to
 * @siblings: a widget path describing a list of siblings. This path
 *   may not contain any siblings itself and it must not be modified
 *   afterwards.
 * @sibling_index: index into @siblings for where the added element is
 *   positioned.
 *
 * Appends a widget type with all its siblings to the widget hierarchy
 * represented by @path. Using this function instead of
 * gtk_widget_path_append_type() will allow the CSS theming to use
 * sibling matches in selectors and apply :nth-child() pseudo classes.
 * In turn, it requires a lot more care in widget implementations as
 * widgets need to make sure to call gtk_widget_reset_style() on all
 * involved widgets when the @siblings path changes.
 *
 * Returns: the position where the element was inserted.
 *
 * Since: 3.2
 **/
gint
gtk_widget_path_append_with_siblings (GtkWidgetPath *path,
                                      GtkWidgetPath *siblings,
                                      guint          sibling_index)
{
  GtkPathElement new;

  g_return_val_if_fail (path != NULL, 0);
  g_return_val_if_fail (siblings != NULL, 0);
  g_return_val_if_fail (sibling_index < gtk_widget_path_length (siblings), 0);

  gtk_path_element_copy (&new, &g_array_index (siblings->elems, GtkPathElement, sibling_index));
  new.siblings = gtk_widget_path_ref (siblings);
  new.sibling_index = sibling_index;
  g_array_append_val (path->elems, new);

  return path->elems->len - 1;
}

/**
 * gtk_widget_path_iter_get_siblings:
 * @path: a #GtkWidgetPath
 * @pos: position to get the siblings for, -1 for the path head
 *
 * Returns the list of siblings for the element at @pos. If the element
 * was not added with siblings, %NULL is returned.
 *
 * Returns: %NULL or the list of siblings for the element at @pos.
 **/
const GtkWidgetPath *
gtk_widget_path_iter_get_siblings (const GtkWidgetPath *path,
                                   gint                 pos)
{
  GtkPathElement *elem;

  g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
  g_return_val_if_fail (path->elems->len != 0, G_TYPE_INVALID);

  if (pos < 0 || pos >= path->elems->len)
    pos = path->elems->len - 1;

  elem = &g_array_index (path->elems, GtkPathElement, pos);
  return elem->siblings;
}

/**
 * gtk_widget_path_iter_get_sibling_index:
 * @path: a #GtkWidgetPath
 * @pos: position to get the sibling index for, -1 for the path head
 *
 * Returns the index into the list of siblings for the element at @pos as
 * returned by gtk_widget_path_iter_get_siblings(). If that function would
 * return %NULL because the element at @pos has no siblings, this function
 * will return 0.
 *
 * Returns: 0 or the index into the list of siblings for the element at @pos.
 **/
guint
gtk_widget_path_iter_get_sibling_index (const GtkWidgetPath *path,
                                        gint                 pos)
{
  GtkPathElement *elem;

  g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
  g_return_val_if_fail (path->elems->len != 0, G_TYPE_INVALID);

  if (pos < 0 || pos >= path->elems->len)
    pos = path->elems->len - 1;

  elem = &g_array_index (path->elems, GtkPathElement, pos);
  return elem->sibling_index;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
521
/**
522
 * gtk_widget_path_iter_get_object_type:
Carlos Garnacho's avatar
Carlos Garnacho committed
523
 * @path: a #GtkWidgetPath
524
 * @pos: position to get the object type for, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
525
 *
526
 * Returns the object #GType that is at position @pos in the widget
Carlos Garnacho's avatar
Carlos Garnacho committed
527 528 529 530 531 532
 * hierarchy defined in @path.
 *
 * Returns: a widget type
 *
 * Since: 3.0
 **/
533
GType
534
gtk_widget_path_iter_get_object_type (const GtkWidgetPath *path,
535
                                      gint                 pos)
Carlos Garnacho's avatar
Carlos Garnacho committed
536
{
537
  GtkPathElement *elem;
Carlos Garnacho's avatar
Carlos Garnacho committed
538

539
  g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
540 541
  g_return_val_if_fail (path->elems->len != 0, G_TYPE_INVALID);

542
  if (pos < 0 || pos >= path->elems->len)
543
    pos = path->elems->len - 1;
Carlos Garnacho's avatar
Carlos Garnacho committed
544

545 546
  elem = &g_array_index (path->elems, GtkPathElement, pos);
  return elem->type;
Carlos Garnacho's avatar
Carlos Garnacho committed
547 548
}

Carlos Garnacho's avatar
Carlos Garnacho committed
549
/**
550
 * gtk_widget_path_iter_set_object_type:
Carlos Garnacho's avatar
Carlos Garnacho committed
551
 * @path: a #GtkWidgetPath
552
 * @pos: position to modify, -1 for the path head
553
 * @type: object type to set
Carlos Garnacho's avatar
Carlos Garnacho committed
554
 *
555 556
 * Sets the object type for a given position in the widget hierarchy
 * defined by @path.
Carlos Garnacho's avatar
Carlos Garnacho committed
557 558 559
 *
 * Since: 3.0
 **/
560
void
561
gtk_widget_path_iter_set_object_type (GtkWidgetPath *path,
562
                                      gint           pos,
563
                                      GType          type)
Carlos Garnacho's avatar
Carlos Garnacho committed
564
{
565
  GtkPathElement *elem;
Carlos Garnacho's avatar
Carlos Garnacho committed
566

567
  g_return_if_fail (path != NULL);
568
  g_return_if_fail (path->elems->len != 0);
Carlos Garnacho's avatar
Carlos Garnacho committed
569

570
  if (pos < 0 || pos >= path->elems->len)
571 572
    pos = path->elems->len - 1;

573 574 575
  elem = &g_array_index (path->elems, GtkPathElement, pos);
  elem->type = type;
}
Carlos Garnacho's avatar
Carlos Garnacho committed
576

Carlos Garnacho's avatar
Carlos Garnacho committed
577 578 579
/**
 * gtk_widget_path_iter_get_name:
 * @path: a #GtkWidgetPath
580
 * @pos: position to get the widget name for, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
581 582 583 584 585 586 587
 *
 * Returns the name corresponding to the widget found at
 * the position @pos in the widget hierarchy defined by
 * @path
 *
 * Returns: The widget name, or %NULL if none was set.
 **/
588
const gchar *
589
gtk_widget_path_iter_get_name (const GtkWidgetPath *path,
590
                               gint                 pos)
591 592
{
  GtkPathElement *elem;
Carlos Garnacho's avatar
Carlos Garnacho committed
593

594
  g_return_val_if_fail (path != NULL, NULL);
595 596
  g_return_val_if_fail (path->elems->len != 0, NULL);

597
  if (pos < 0 || pos >= path->elems->len)
598
    pos = path->elems->len - 1;
Carlos Garnacho's avatar
Carlos Garnacho committed
599

600
  elem = &g_array_index (path->elems, GtkPathElement, pos);
601
  return g_quark_to_string (elem->name);
Carlos Garnacho's avatar
Carlos Garnacho committed
602 603
}

Carlos Garnacho's avatar
Carlos Garnacho committed
604 605 606
/**
 * gtk_widget_path_iter_set_name:
 * @path: a #GtkWidgetPath
607
 * @pos: position to modify, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
608 609 610 611 612 613 614
 * @name: widget name
 *
 * Sets the widget name for the widget found at position @pos
 * in the widget hierarchy defined by @path.
 *
 * Since: 3.0
 **/
Carlos Garnacho's avatar
Carlos Garnacho committed
615
void
616
gtk_widget_path_iter_set_name (GtkWidgetPath *path,
617
                               gint           pos,
618
                               const gchar   *name)
Carlos Garnacho's avatar
Carlos Garnacho committed
619
{
620
  GtkPathElement *elem;
Carlos Garnacho's avatar
Carlos Garnacho committed
621 622

  g_return_if_fail (path != NULL);
623
  g_return_if_fail (path->elems->len != 0);
624 625
  g_return_if_fail (name != NULL);

626
  if (pos < 0 || pos >= path->elems->len)
627 628
    pos = path->elems->len - 1;

629 630
  elem = &g_array_index (path->elems, GtkPathElement, pos);

631 632 633
  elem->name = g_quark_from_string (name);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
634 635 636
/**
 * gtk_widget_path_iter_has_qname:
 * @path: a #GtkWidgetPath
637
 * @pos: position to query, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
638 639 640 641 642 643 644 645 646
 * @qname: widget name as a #GQuark
 *
 * See gtk_widget_path_iter_has_name(). This is a version
 * that operates on #GQuark<!-- -->s.
 *
 * Returns: %TRUE if the widget at @pos has this name
 *
 * Since: 3.0
 **/
647 648
gboolean
gtk_widget_path_iter_has_qname (const GtkWidgetPath *path,
649
                                gint                 pos,
650 651 652 653 654
                                GQuark               qname)
{
  GtkPathElement *elem;

  g_return_val_if_fail (path != NULL, FALSE);
655
  g_return_val_if_fail (path->elems->len != 0, FALSE);
656
  g_return_val_if_fail (qname != 0, FALSE);
657

658
  if (pos < 0 || pos >= path->elems->len)
659
    pos = path->elems->len - 1;
660 661 662 663 664 665

  elem = &g_array_index (path->elems, GtkPathElement, pos);

  return (elem->name == qname);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
666 667 668
/**
 * gtk_widget_path_iter_has_name:
 * @path: a #GtkWidgetPath
669
 * @pos: position to query, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
670 671 672 673 674 675 676 677 678
 * @name: a widget name
 *
 * Returns %TRUE if the widget at position @pos has the name @name,
 * %FALSE otherwise.
 *
 * Returns: %TRUE if the widget at @pos has this name
 *
 * Since: 3.0
 **/
679 680
gboolean
gtk_widget_path_iter_has_name (const GtkWidgetPath *path,
681
                               gint                 pos,
682 683 684 685 686
                               const gchar         *name)
{
  GQuark qname;

  g_return_val_if_fail (path != NULL, FALSE);
687 688
  g_return_val_if_fail (path->elems->len != 0, FALSE);

689
  if (pos < 0 || pos >= path->elems->len)
690
    pos = path->elems->len - 1;
691 692 693 694 695

  qname = g_quark_try_string (name);

  if (qname == 0)
    return FALSE;
696

697
  return gtk_widget_path_iter_has_qname (path, pos, qname);
698 699
}

Carlos Garnacho's avatar
Carlos Garnacho committed
700 701 702
/**
 * gtk_widget_path_iter_add_class:
 * @path: a #GtkWidget
703
 * @pos: position to modify, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
704 705 706 707
 * @name: a class name
 *
 * Adds the class @name to the widget at position @pos in
 * the hierarchy defined in @path. See
708
 * gtk_style_context_add_class().
Carlos Garnacho's avatar
Carlos Garnacho committed
709 710 711
 *
 * Since: 3.0
 **/
712 713
void
gtk_widget_path_iter_add_class (GtkWidgetPath *path,
714
                                gint           pos,
715 716 717 718 719 720 721 722
                                const gchar   *name)
{
  GtkPathElement *elem;
  gboolean added = FALSE;
  GQuark qname;
  guint i;

  g_return_if_fail (path != NULL);
723
  g_return_if_fail (path->elems->len != 0);
724 725
  g_return_if_fail (name != NULL);

726
  if (pos < 0 || pos >= path->elems->len)
727 728
    pos = path->elems->len - 1;

729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
  elem = &g_array_index (path->elems, GtkPathElement, pos);
  qname = g_quark_from_string (name);

  if (!elem->classes)
    elem->classes = g_array_new (FALSE, FALSE, sizeof (GQuark));

  for (i = 0; i < elem->classes->len; i++)
    {
      GQuark quark;

      quark = g_array_index (elem->classes, GQuark, i);

      if (qname == quark)
        {
          /* Already there */
          added = TRUE;
          break;
        }
      if (qname < quark)
        {
          g_array_insert_val (elem->classes, i, qname);
          added = TRUE;
          break;
        }
    }

  if (!added)
    g_array_append_val (elem->classes, qname);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
759 760 761
/**
 * gtk_widget_path_iter_remove_class:
 * @path: a #GtkWidgetPath
762
 * @pos: position to modify, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
763 764 765 766 767 768 769
 * @name: class name
 *
 * Removes the class @name from the widget at position @pos in
 * the hierarchy defined in @path.
 *
 * Since: 3.0
 **/
770 771
void
gtk_widget_path_iter_remove_class (GtkWidgetPath *path,
772
                                   gint           pos,
773 774 775 776 777 778 779
                                   const gchar   *name)
{
  GtkPathElement *elem;
  GQuark qname;
  guint i;

  g_return_if_fail (path != NULL);
780
  g_return_if_fail (path->elems->len != 0);
781 782
  g_return_if_fail (name != NULL);

783
  if (pos < 0 || pos >= path->elems->len)
784 785
    pos = path->elems->len - 1;

786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
  qname = g_quark_try_string (name);

  if (qname == 0)
    return;

  elem = &g_array_index (path->elems, GtkPathElement, pos);

  if (!elem->classes)
    return;

  for (i = 0; i < elem->classes->len; i++)
    {
      GQuark quark;

      quark = g_array_index (elem->classes, GQuark, i);

      if (quark > qname)
        break;
      else if (quark == qname)
        {
          g_array_remove_index (elem->classes, i);
          break;
        }
    }
}

Carlos Garnacho's avatar
Carlos Garnacho committed
812 813 814
/**
 * gtk_widget_path_iter_clear_classes:
 * @path: a #GtkWidget
815
 * @pos: position to modify, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
816 817 818 819 820 821
 *
 * Removes all classes from the widget at position @pos in the
 * hierarchy defined in @path.
 *
 * Since: 3.0
 **/
822 823
void
gtk_widget_path_iter_clear_classes (GtkWidgetPath *path,
824
                                    gint           pos)
825 826 827 828
{
  GtkPathElement *elem;

  g_return_if_fail (path != NULL);
829 830
  g_return_if_fail (path->elems->len != 0);

831
  if (pos < 0 || pos >= path->elems->len)
832
    pos = path->elems->len - 1;
833 834 835 836 837 838 839 840 841 842

  elem = &g_array_index (path->elems, GtkPathElement, pos);

  if (!elem->classes)
    return;

  if (elem->classes->len > 0)
    g_array_remove_range (elem->classes, 0, elem->classes->len);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
843 844 845
/**
 * gtk_widget_path_iter_list_classes:
 * @path: a #GtkWidgetPath
846
 * @pos: position to query, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
847 848 849 850
 *
 * Returns a list with all the class names defined for the widget
 * at position @pos in the hierarchy defined in @path.
 *
851 852 853
 * Returns: (transfer container) (element-type utf8): The list of
 *          classes, This is a list of strings, the #GSList contents
 *          are owned by GTK+, but you should use g_slist_free() to
Carlos Garnacho's avatar
Carlos Garnacho committed
854 855 856 857
 *          free the list itself.
 *
 * Since: 3.0
 **/
858 859
GSList *
gtk_widget_path_iter_list_classes (const GtkWidgetPath *path,
860
                                   gint                 pos)
861 862 863 864 865 866
{
  GtkPathElement *elem;
  GSList *list = NULL;
  guint i;

  g_return_val_if_fail (path != NULL, NULL);
867 868
  g_return_val_if_fail (path->elems->len != 0, NULL);

869
  if (pos < 0 || pos >= path->elems->len)
870
    pos = path->elems->len - 1;
871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887

  elem = &g_array_index (path->elems, GtkPathElement, pos);

  if (!elem->classes)
    return NULL;

  for (i = 0; i < elem->classes->len; i++)
    {
      GQuark quark;

      quark = g_array_index (elem->classes, GQuark, i);
      list = g_slist_prepend (list, (gchar *) g_quark_to_string (quark));
    }

  return g_slist_reverse (list);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
888 889 890
/**
 * gtk_widget_path_iter_has_qclass:
 * @path: a #GtkWidgetPath
891
 * @pos: position to query, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
892 893 894 895 896 897 898 899 900
 * @qname: class name as a #GQuark
 *
 * See gtk_widget_path_iter_has_class(). This is a version that operates
 * with GQuark<!-- -->s.
 *
 * Returns: %TRUE if the widget at @pos has the class defined.
 *
 * Since: 3.0
 **/
901 902
gboolean
gtk_widget_path_iter_has_qclass (const GtkWidgetPath *path,
903
                                 gint                 pos,
904 905 906 907 908 909
                                 GQuark               qname)
{
  GtkPathElement *elem;
  guint i;

  g_return_val_if_fail (path != NULL, FALSE);
910
  g_return_val_if_fail (path->elems->len != 0, FALSE);
911 912
  g_return_val_if_fail (qname != 0, FALSE);

913
  if (pos < 0 || pos >= path->elems->len)
914 915
    pos = path->elems->len - 1;

916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935
  elem = &g_array_index (path->elems, GtkPathElement, pos);

  if (!elem->classes)
    return FALSE;

  for (i = 0; i < elem->classes->len; i++)
    {
      GQuark quark;

      quark = g_array_index (elem->classes, GQuark, i);

      if (quark == qname)
        return TRUE;
      else if (quark > qname)
        break;
    }

  return FALSE;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
936 937 938
/**
 * gtk_widget_path_iter_has_class:
 * @path: a #GtkWidgetPath
939
 * @pos: position to query, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
940 941 942 943 944 945 946 947 948
 * @name: class name
 *
 * Returns %TRUE if the widget at position @pos has the class @name
 * defined, %FALSE otherwise.
 *
 * Returns: %TRUE if the class @name is defined for the widget at @pos
 *
 * Since: 3.0
 **/
949 950
gboolean
gtk_widget_path_iter_has_class (const GtkWidgetPath *path,
951
                                gint                 pos,
952 953 954 955 956
                                const gchar         *name)
{
  GQuark qname;

  g_return_val_if_fail (path != NULL, FALSE);
957
  g_return_val_if_fail (path->elems->len != 0, FALSE);
958 959
  g_return_val_if_fail (name != NULL, FALSE);

960
  if (pos < 0 || pos >= path->elems->len)
961 962
    pos = path->elems->len - 1;

963 964 965 966 967 968 969 970
  qname = g_quark_try_string (name);

  if (qname == 0)
    return FALSE;

  return gtk_widget_path_iter_has_qclass (path, pos, qname);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
971 972 973
/**
 * gtk_widget_path_iter_add_region:
 * @path: a #GtkWidgetPath
974
 * @pos: position to modify, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
975 976 977 978 979
 * @name: region name
 * @flags: flags affecting the region
 *
 * Adds the region @name to the widget at position @pos in
 * the hierarchy defined in @path. See
980
 * gtk_style_context_add_region().
Carlos Garnacho's avatar
Carlos Garnacho committed
981
 *
982 983 984
 * <note><para>Region names must only contain lowercase letters
 * and '-', starting always with a lowercase letter.</para></note>
 *
Carlos Garnacho's avatar
Carlos Garnacho committed
985 986
 * Since: 3.0
 **/
987
void
988
gtk_widget_path_iter_add_region (GtkWidgetPath  *path,
989
                                 gint            pos,
990 991
                                 const gchar    *name,
                                 GtkRegionFlags  flags)
992 993
{
  GtkPathElement *elem;
994
  GQuark qname;
995 996

  g_return_if_fail (path != NULL);
997
  g_return_if_fail (path->elems->len != 0);
998
  g_return_if_fail (name != NULL);
999
  g_return_if_fail (_gtk_style_context_check_region_name (name));
1000

1001
  if (pos < 0 || pos >= path->elems->len)
1002 1003
    pos = path->elems->len - 1;

1004
  elem = &g_array_index (path->elems, GtkPathElement, pos);
1005
  qname = g_quark_from_string (name);
1006 1007

  if (!elem->regions)
1008
    elem->regions = g_hash_table_new (NULL, NULL);
1009 1010

  g_hash_table_insert (elem->regions,
1011
                       GUINT_TO_POINTER (qname),
1012 1013 1014
                       GUINT_TO_POINTER (flags));
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1015 1016 1017
/**
 * gtk_widget_path_iter_remove_region:
 * @path: a #GtkWidgetPath
1018
 * @pos: position to modify, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
1019 1020 1021 1022 1023 1024 1025
 * @name: region name
 *
 * Removes the region @name from the widget at position @pos in
 * the hierarchy defined in @path.
 *
 * Since: 3.0
 **/
1026 1027
void
gtk_widget_path_iter_remove_region (GtkWidgetPath *path,
1028
                                    gint           pos,
1029 1030 1031
                                    const gchar   *name)
{
  GtkPathElement *elem;
1032
  GQuark qname;
1033 1034

  g_return_if_fail (path != NULL);
1035
  g_return_if_fail (path->elems->len != 0);
1036 1037
  g_return_if_fail (name != NULL);

1038
  if (pos < 0 || pos >= path->elems->len)
1039 1040
    pos = path->elems->len - 1;

1041 1042 1043 1044 1045
  qname = g_quark_try_string (name);

  if (qname == 0)
    return;

1046 1047 1048
  elem = &g_array_index (path->elems, GtkPathElement, pos);

  if (elem->regions)
1049
    g_hash_table_remove (elem->regions, GUINT_TO_POINTER (qname));
1050 1051
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1052 1053 1054
/**
 * gtk_widget_path_iter_clear_regions:
 * @path: a #GtkWidgetPath
1055
 * @pos: position to modify, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
1056 1057 1058 1059 1060 1061
 *
 * Removes all regions from the widget at position @pos in the
 * hierarchy defined in @path.
 *
 * Since: 3.0
 **/
1062 1063
void
gtk_widget_path_iter_clear_regions (GtkWidgetPath *path,
1064
                                    gint           pos)
1065 1066 1067 1068
{
  GtkPathElement *elem;

  g_return_if_fail (path != NULL);
1069 1070
  g_return_if_fail (path->elems->len != 0);

1071
  if (pos < 0 || pos >= path->elems->len)
1072
    pos = path->elems->len - 1;
1073 1074 1075 1076 1077 1078 1079

  elem = &g_array_index (path->elems, GtkPathElement, pos);

  if (elem->regions)
    g_hash_table_remove_all (elem->regions);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1080 1081 1082
/**
 * gtk_widget_path_iter_list_regions:
 * @path: a #GtkWidgetPath
1083
 * @pos: position to query, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
1084 1085 1086 1087
 *
 * Returns a list with all the region names defined for the widget
 * at position @pos in the hierarchy defined in @path.
 *
1088 1089 1090
 * Returns: (transfer container) (element-type utf8): The list of
 *          regions, This is a list of strings, the #GSList contents
 *          are owned by GTK+, but you should use g_slist_free() to
Carlos Garnacho's avatar
Carlos Garnacho committed
1091 1092 1093 1094
 *          free the list itself.
 *
 * Since: 3.0
 **/
1095
GSList *
1096
gtk_widget_path_iter_list_regions (const GtkWidgetPath *path,
1097
                                   gint                 pos)
1098 1099 1100 1101 1102 1103 1104
{
  GtkPathElement *elem;
  GHashTableIter iter;
  GSList *list = NULL;
  gpointer key;

  g_return_val_if_fail (path != NULL, NULL);
1105 1106
  g_return_val_if_fail (path->elems->len != 0, NULL);

1107
  if (pos < 0 || pos >= path->elems->len)
1108
    pos = path->elems->len - 1;
1109 1110 1111 1112 1113 1114 1115 1116 1117

  elem = &g_array_index (path->elems, GtkPathElement, pos);

  if (!elem->regions)
    return NULL;

  g_hash_table_iter_init (&iter, elem->regions);

  while (g_hash_table_iter_next (&iter, &key, NULL))
1118 1119 1120 1121 1122 1123
    {
      GQuark qname;

      qname = GPOINTER_TO_UINT (key);
      list = g_slist_prepend (list, (gchar *) g_quark_to_string (qname));
    }
1124 1125 1126 1127

  return list;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1128 1129 1130
/**
 * gtk_widget_path_iter_has_qregion:
 * @path: a #GtkWidgetPath
1131
 * @pos: position to query, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
 * @qname: region name as a #GQuark
 * @flags: (out): return location for the region flags
 *
 * See gtk_widget_path_iter_has_region(). This is a version that operates
 * with GQuark<!-- -->s.
 *
 * Returns: %TRUE if the widget at @pos has the region defined.
 *
 * Since: 3.0
 **/
1142
gboolean
1143
gtk_widget_path_iter_has_qregion (const GtkWidgetPath *path,
1144
                                  gint                 pos,
1145
                                  GQuark               qname,
1146
                                  GtkRegionFlags      *flags)
1147 1148 1149 1150 1151
{
  GtkPathElement *elem;
  gpointer value;

  g_return_val_if_fail (path != NULL, FALSE);
1152
  g_return_val_if_fail (path->elems->len != 0, FALSE);
1153
  g_return_val_if_fail (qname != 0, FALSE);
1154

1155
  if (pos < 0 || pos >= path->elems->len)
1156 1157
    pos = path->elems->len - 1;

1158 1159 1160 1161 1162
  elem = &g_array_index (path->elems, GtkPathElement, pos);

  if (!elem->regions)
    return FALSE;

1163 1164 1165
  if (!g_hash_table_lookup_extended (elem->regions,
                                     GUINT_TO_POINTER (qname),
                                     NULL, &value))
1166 1167 1168 1169 1170 1171 1172 1173
    return FALSE;

  if (flags)
    *flags = GPOINTER_TO_UINT (value);

  return TRUE;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1174 1175 1176
/**
 * gtk_widget_path_iter_has_region:
 * @path: a #GtkWidgetPath
1177
 * @pos: position to query, -1 for the path head
Carlos Garnacho's avatar
Carlos Garnacho committed
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
 * @name: region name
 * @flags: (out): return location for the region flags
 *
 * Returns %TRUE if the widget at position @pos has the class @name
 * defined, %FALSE otherwise.
 *
 * Returns: %TRUE if the class @name is defined for the widget at @pos
 *
 * Since: 3.0
 **/
1188 1189
gboolean
gtk_widget_path_iter_has_region (const GtkWidgetPath *path,
1190
                                 gint                 pos,
1191
                                 const gchar         *name,
1192
                                 GtkRegionFlags      *flags)
1193 1194 1195 1196
{
  GQuark qname;

  g_return_val_if_fail (path != NULL, FALSE);
1197
  g_return_val_if_fail (path->elems->len != 0, FALSE);
1198 1199
  g_return_val_if_fail (name != NULL, FALSE);

1200
  if (pos < 0 || pos >= path->elems->len)
1201 1202
    pos = path->elems->len - 1;

1203 1204 1205 1206 1207 1208 1209 1210
  qname = g_quark_try_string (name);

  if (qname == 0)
    return FALSE;

  return gtk_widget_path_iter_has_qregion (path, pos, qname, flags);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1211
/**
1212
 * gtk_widget_path_get_object_type:
Carlos Garnacho's avatar
Carlos Garnacho committed
1213 1214
 * @path: a #GtkWidget
 *
1215
 * Returns the topmost object type, that is, the object type this path
Carlos Garnacho's avatar
Carlos Garnacho committed
1216 1217
 * is representing.
 *
1218
 * Returns: The object type
Carlos Garnacho's avatar
Carlos Garnacho committed
1219 1220 1221
 *
 * Since: 3.0
 **/
1222
GType
1223
gtk_widget_path_get_object_type (const GtkWidgetPath *path)
1224 1225 1226 1227 1228
{
  GtkPathElement *elem;

  g_return_val_if_fail (path != NULL, G_TYPE_INVALID);

Carlos Garnacho's avatar
Carlos Garnacho committed
1229 1230
  elem = &g_array_index (path->elems, GtkPathElement,
                         path->elems->len - 1);
1231 1232 1233
  return elem->type;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
/**
 * gtk_widget_path_is_type:
 * @path: a #GtkWidgetPath
 * @type: widget type to match
 *
 * Returns %TRUE if the widget type represented by this path
 * is @type, or a subtype of it.
 *
 * Returns: %TRUE if the widget represented by @path is of type @type
 *
 * Since: 3.0
 **/
1246 1247 1248 1249 1250 1251 1252 1253
gboolean
gtk_widget_path_is_type (const GtkWidgetPath *path,
                         GType                type)
{
  GtkPathElement *elem;

  g_return_val_if_fail (path != NULL, FALSE);

Carlos Garnacho's avatar
Carlos Garnacho committed
1254 1255
  elem = &g_array_index (path->elems, GtkPathElement,
                         path->elems->len - 1);
1256 1257 1258 1259 1260 1261 1262 1263

  if (elem->type == type ||
      g_type_is_a (elem->type, type))
    return TRUE;

  return FALSE;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275
/**
 * gtk_widget_path_has_parent:
 * @path: a #GtkWidgetPath
 * @type: widget type to check in parents
 *
 * Returns %TRUE if any of the parents of the widget represented
 * in @path is of type @type, or any subtype of it.
 *
 * Returns: %TRUE if any parent is of type @type
 *
 * Since: 3.0
 **/
1276 1277 1278 1279 1280
gboolean
gtk_widget_path_has_parent (const GtkWidgetPath *path,
                            GType                type)
{
  guint i;
Carlos Garnacho's avatar
Carlos Garnacho committed
1281

1282
  g_return_val_if_fail (path != NULL, FALSE);
Carlos Garnacho's avatar
Carlos Garnacho committed
1283

Carlos Garnacho's avatar
Carlos Garnacho committed
1284
  for (i = 0; i < path->elems->len - 1; i++)
Carlos Garnacho's avatar
Carlos Garnacho committed
1285 1286 1287
    {
      GtkPathElement *elem;

1288
      elem = &g_array_index (path->elems, GtkPathElement, i);
Carlos Garnacho's avatar
Carlos Garnacho committed
1289

1290 1291 1292
      if (elem->type == type ||
          g_type_is_a (elem->type, type))
        return TRUE;
Carlos Garnacho's avatar
Carlos Garnacho committed
1293
    }
1294 1295

  return FALSE;
Carlos Garnacho's avatar
Carlos Garnacho committed
1296
}