]> Pileus Git - ~andy/gtk/blob - gtk/gtkwidgetpath.c
API: Add support for siblings to widget paths
[~andy/gtk] / gtk / gtkwidgetpath.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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 <string.h>
23
24 #include "gtkwidget.h"
25 #include "gtkwidgetpath.h"
26 #include "gtkstylecontextprivate.h"
27
28 /**
29  * SECTION:gtkwidgetpath
30  * @Short_description: Widget path abstraction
31  * @Title: GtkWidgetPath
32  * @See_also: #GtkStyleContext
33  *
34  * GtkWidgetPath is a boxed type that represents a widget hierarchy from
35  * the topmost widget, typically a toplevel, to any child. This widget
36  * path abstraction is used in #GtkStyleContext on behalf of the real
37  * widget in order to query style information.
38  *
39  * If you are using GTK+ widgets, you probably will not need to use
40  * this API directly, as there is gtk_widget_get_path(), and the style
41  * context returned by gtk_widget_get_style_context() will be automatically
42  * updated on widget hierarchy changes.
43  *
44  * The widget path generation is generally simple:
45  * <example>
46  * <title>Defining a button within a window</title>
47  * <programlisting>
48  * {
49  *   GtkWidgetPath *path;
50  *
51  *   path = gtk_widget_path_new ();
52  *   gtk_widget_path_append_type (path, GTK_TYPE_WINDOW);
53  *   gtk_widget_path_append_type (path, GTK_TYPE_BUTTON);
54  * }
55  * </programlisting>
56  * </example>
57  *
58  * Although more complex information, such as widget names, or
59  * different classes (property that may be used by other widget
60  * types) and intermediate regions may be included:
61  *
62  * <example>
63  * <title>Defining the first tab widget in a notebook</title>
64  * <programlisting>
65  * {
66  *   GtkWidgetPath *path;
67  *   guint pos;
68  *
69  *   path = gtk_widget_path_new ();
70  *
71  *   pos = gtk_widget_path_append_type (path, GTK_TYPE_NOTEBOOK);
72  *   gtk_widget_path_iter_add_region (path, pos, "tab", GTK_REGION_EVEN | GTK_REGION_FIRST);
73  *
74  *   pos = gtk_widget_path_append_type (path, GTK_TYPE_LABEL);
75  *   gtk_widget_path_iter_set_name (path, pos, "first tab label");
76  * }
77  * </programlisting>
78  * </example>
79  *
80  * All this information will be used to match the style information
81  * that applies to the described widget.
82  **/
83
84 G_DEFINE_BOXED_TYPE (GtkWidgetPath, gtk_widget_path,
85                      gtk_widget_path_ref, gtk_widget_path_unref)
86
87
88 typedef struct GtkPathElement GtkPathElement;
89
90 struct GtkPathElement
91 {
92   GType type;
93   GQuark name;
94   GHashTable *regions;
95   GArray *classes;
96   GtkWidgetPath *siblings;
97   guint sibling_index;
98 };
99
100 struct _GtkWidgetPath
101 {
102   volatile guint ref_count;
103
104   GArray *elems; /* First element contains the described widget */
105 };
106
107 /**
108  * gtk_widget_path_new:
109  *
110  * Returns an empty widget path.
111  *
112  * Returns: (transfer full): A newly created, empty, #GtkWidgetPath
113  *
114  * Since: 3.0
115  **/
116 GtkWidgetPath *
117 gtk_widget_path_new (void)
118 {
119   GtkWidgetPath *path;
120
121   path = g_slice_new0 (GtkWidgetPath);
122   path->elems = g_array_new (FALSE, TRUE, sizeof (GtkPathElement));
123   path->ref_count = 1;
124
125   return path;
126 }
127
128 static void
129 gtk_path_element_copy (GtkPathElement       *dest,
130                        const GtkPathElement *src)
131 {
132   memset (dest, 0, sizeof (GtkPathElement));
133
134   dest->type = src->type;
135   dest->name = src->name;
136   if (src->siblings)
137     dest->siblings = gtk_widget_path_ref (src->siblings);
138   dest->sibling_index = src->sibling_index;
139
140   if (src->regions)
141     {
142       GHashTableIter iter;
143       gpointer key, value;
144
145       g_hash_table_iter_init (&iter, src->regions);
146       dest->regions = g_hash_table_new (NULL, NULL);
147
148       while (g_hash_table_iter_next (&iter, &key, &value))
149         g_hash_table_insert (dest->regions, key, value);
150     }
151
152   if (src->classes)
153     {
154       dest->classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
155       g_array_append_vals (dest->classes, src->classes->data, src->classes->len);
156     }
157 }
158
159 /**
160  * gtk_widget_path_copy:
161  * @path: a #GtkWidgetPath
162  *
163  * Returns a copy of @path
164  *
165  * Returns: (transfer full): a copy of @path
166  *
167  * Since: 3.0
168  **/
169 GtkWidgetPath *
170 gtk_widget_path_copy (const GtkWidgetPath *path)
171 {
172   GtkWidgetPath *new_path;
173   guint i;
174
175   g_return_val_if_fail (path != NULL, NULL);
176
177   new_path = gtk_widget_path_new ();
178
179   for (i = 0; i < path->elems->len; i++)
180     {
181       GtkPathElement *elem, new;
182
183       elem = &g_array_index (path->elems, GtkPathElement, i);
184
185       gtk_path_element_copy (&new, elem);
186
187       g_array_append_val (new_path->elems, new);
188     }
189
190   return new_path;
191 }
192
193 /**
194  * gtk_widget_path_ref:
195  * @path: a #GtkWidgetPath
196  *
197  * Increments the reference count on @path.
198  *
199  * Returns: @path itself.
200  *
201  * Since: 3.2
202  **/
203 GtkWidgetPath *
204 gtk_widget_path_ref (GtkWidgetPath *path)
205 {
206   g_return_val_if_fail (path != NULL, path);
207
208   g_atomic_int_add (&path->ref_count, 1);
209
210   return path;
211 }
212
213 /**
214  * gtk_widget_path_unref:
215  * @path: a #GtkWidgetPath
216  *
217  * Decrements the reference count on @path, freeing the structure
218  * if the reference count reaches 0.
219  *
220  * Since: 3.2
221  **/
222 void
223 gtk_widget_path_unref (GtkWidgetPath *path)
224 {
225   guint i;
226
227   g_return_if_fail (path != NULL);
228
229   if (!g_atomic_int_dec_and_test (&path->ref_count))
230     return;
231
232   for (i = 0; i < path->elems->len; i++)
233     {
234       GtkPathElement *elem;
235
236       elem = &g_array_index (path->elems, GtkPathElement, i);
237
238       if (elem->regions)
239         g_hash_table_destroy (elem->regions);
240
241       if (elem->classes)
242         g_array_free (elem->classes, TRUE);
243
244       if (elem->siblings)
245         gtk_widget_path_unref (elem->siblings);
246     }
247
248   g_array_free (path->elems, TRUE);
249   g_slice_free (GtkWidgetPath, path);
250 }
251
252 /**
253  * gtk_widget_path_free:
254  * @path: a #GtkWidgetPath
255  *
256  * Decrements the reference count on @path, freeing the structure
257  * if the reference count reaches 0.
258  *
259  * Since: 3.0
260  **/
261 void
262 gtk_widget_path_free (GtkWidgetPath *path)
263 {
264   g_return_if_fail (path != NULL);
265
266   gtk_widget_path_unref (path);
267 }
268
269 /**
270  * gtk_widget_path_length:
271  * @path: a #GtkWidgetPath
272  *
273  * Returns the number of #GtkWidget #GTypes between the represented
274  * widget and its topmost container.
275  *
276  * Returns: the number of elements in the path
277  *
278  * Since: 3.0
279  **/
280 gint
281 gtk_widget_path_length (const GtkWidgetPath *path)
282 {
283   g_return_val_if_fail (path != NULL, 0);
284
285   return path->elems->len;
286 }
287
288 /**
289  * gtk_widget_path_to_string:
290  * @path: the path
291  *
292  * Dumps the widget path into a string representation. It tries to match
293  * the CSS style as closely as possible (Note that there might be paths
294  * that cannot be represented in CSS).
295  *
296  * The main use of this code is for debugging purposes, so that you can
297  * g_print() the path or dump it in a gdb session.
298  *
299  * Returns: A new string describing @path.
300  *
301  * Since: 3.2
302  **/
303 char *
304 gtk_widget_path_to_string (const GtkWidgetPath *path)
305 {
306   GString *string;
307   guint i, j;
308
309   g_return_val_if_fail (path != NULL, NULL);
310
311   string = g_string_new ("");
312
313   for (i = 0; i < path->elems->len; i++)
314     {
315       GtkPathElement *elem;
316
317       elem = &g_array_index (path->elems, GtkPathElement, i);
318
319       if (i > 0)
320         g_string_append_c (string, ' ');
321
322       g_string_append (string, g_type_name (elem->type));
323
324       if (elem->name)
325         {
326           g_string_append_c (string, '(');
327           g_string_append (string, g_quark_to_string (elem->name));
328           g_string_append_c (string, ')');
329         }
330
331       if (elem->classes)
332         {
333           for (j = 0; j < elem->classes->len; j++)
334             {
335               g_string_append_c (string, '.');
336               g_string_append (string, g_quark_to_string (g_array_index (elem->classes, GQuark, j)));
337             }
338         }
339
340       if (elem->regions)
341         {
342           GHashTableIter iter;
343           gpointer key, value;
344
345           g_hash_table_iter_init (&iter, elem->regions);
346           while (g_hash_table_iter_next (&iter, &key, &value))
347             {
348               GtkRegionFlags flags = GPOINTER_TO_UINT (value);
349               static const char *flag_names[] = {
350                 "even",
351                 "odd",
352                 "first",
353                 "last",
354                 "sorted"
355               };
356
357               g_string_append_c (string, ' ');
358               g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (key)));
359               for (j = 0; j < G_N_ELEMENTS(flag_names); j++)
360                 {
361                   if (flags & (1 << j))
362                     {
363                       g_string_append_c (string, ':');
364                       g_string_append (string, flag_names[j]);
365                     }
366                 }
367             }
368         }
369     }
370
371   return g_string_free (string, FALSE);
372 }
373
374 /**
375  * gtk_widget_path_prepend_type:
376  * @path: a #GtkWidgetPath
377  * @type: widget type to prepend
378  *
379  * Prepends a widget type to the widget hierachy represented by @path.
380  *
381  * Since: 3.0
382  **/
383 void
384 gtk_widget_path_prepend_type (GtkWidgetPath *path,
385                               GType          type)
386 {
387   GtkPathElement new = { 0 };
388
389   g_return_if_fail (path != NULL);
390
391   new.type = type;
392   g_array_prepend_val (path->elems, new);
393 }
394
395 /**
396  * gtk_widget_path_append_type:
397  * @path: a #GtkWidgetPath
398  * @type: widget type to append
399  *
400  * Appends a widget type to the widget hierarchy represented by @path.
401  *
402  * Returns: the position where the element was inserted
403  *
404  * Since: 3.0
405  **/
406 gint
407 gtk_widget_path_append_type (GtkWidgetPath *path,
408                              GType          type)
409 {
410   GtkPathElement new = { 0 };
411
412   g_return_val_if_fail (path != NULL, 0);
413
414   new.type = type;
415   g_array_append_val (path->elems, new);
416
417   return path->elems->len - 1;
418 }
419
420 /**
421  * gtk_widget_path_append_with_siblings:
422  * @path: the widget path to append to
423  * @siblings: a widget path describing a list of siblings. This path
424  *   may not contain any siblings itself and it must not be modified
425  *   afterwards.
426  * @sibling_index: index into @siblings for where the added element is
427  *   positioned.
428  *
429  * Appends a widget type with all its siblings to the widget hierarchy
430  * represented by @path. Using this function instead of
431  * gtk_widget_path_append_type() will allow the CSS theming to use
432  * sibling matches in selectors and apply :nth-child() pseudo classes.
433  * In turn, it requires a lot more care in widget implementations as
434  * widgets need to make sure to call gtk_widget_reset_style() on all
435  * involved widgets when the @siblings path changes.
436  *
437  * Returns: the position where the element was inserted.
438  *
439  * Since: 3.2
440  **/
441 gint
442 gtk_widget_path_append_with_siblings (GtkWidgetPath *path,
443                                       GtkWidgetPath *siblings,
444                                       guint          sibling_index)
445 {
446   GtkPathElement new;
447
448   g_return_val_if_fail (path != NULL, 0);
449   g_return_val_if_fail (siblings != NULL, 0);
450   g_return_val_if_fail (sibling_index < gtk_widget_path_length (siblings), 0);
451
452   gtk_path_element_copy (&new, &g_array_index (siblings->elems, GtkPathElement, sibling_index));
453   new.siblings = gtk_widget_path_ref (siblings);
454   new.sibling_index = sibling_index;
455   g_array_append_val (path->elems, new);
456
457   return path->elems->len - 1;
458 }
459
460 /**
461  * gtk_widget_path_iter_get_siblings:
462  * @path: a #GtkWidgetPath
463  * @pos: position to get the siblings for, -1 for the path head
464  *
465  * Returns the list of siblings for the element at @pos. If the element
466  * was not added with siblings, %NULL is returned.
467  *
468  * Returns: %NULL or the list of siblings for the element at @pos.
469  **/
470 const GtkWidgetPath *
471 gtk_widget_path_iter_get_siblings (const GtkWidgetPath *path,
472                                    gint                 pos)
473 {
474   GtkPathElement *elem;
475
476   g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
477   g_return_val_if_fail (path->elems->len != 0, G_TYPE_INVALID);
478
479   if (pos < 0 || pos >= path->elems->len)
480     pos = path->elems->len - 1;
481
482   elem = &g_array_index (path->elems, GtkPathElement, pos);
483   return elem->siblings;
484 }
485
486 /**
487  * gtk_widget_path_iter_get_sibling_index:
488  * @path: a #GtkWidgetPath
489  * @pos: position to get the sibling index for, -1 for the path head
490  *
491  * Returns the index into the list of siblings for the element at @pos as
492  * returned by gtk_widget_path_iter_get_siblings(). If that function would
493  * return %NULL because the element at @pos has no siblings, this function
494  * will return 0.
495  *
496  * Returns: 0 or the index into the list of siblings for the element at @pos.
497  **/
498 guint
499 gtk_widget_path_iter_get_sibling_index (const GtkWidgetPath *path,
500                                         gint                 pos)
501 {
502   GtkPathElement *elem;
503
504   g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
505   g_return_val_if_fail (path->elems->len != 0, G_TYPE_INVALID);
506
507   if (pos < 0 || pos >= path->elems->len)
508     pos = path->elems->len - 1;
509
510   elem = &g_array_index (path->elems, GtkPathElement, pos);
511   return elem->sibling_index;
512 }
513
514 /**
515  * gtk_widget_path_iter_get_object_type:
516  * @path: a #GtkWidgetPath
517  * @pos: position to get the object type for, -1 for the path head
518  *
519  * Returns the object #GType that is at position @pos in the widget
520  * hierarchy defined in @path.
521  *
522  * Returns: a widget type
523  *
524  * Since: 3.0
525  **/
526 GType
527 gtk_widget_path_iter_get_object_type (const GtkWidgetPath *path,
528                                       gint                 pos)
529 {
530   GtkPathElement *elem;
531
532   g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
533   g_return_val_if_fail (path->elems->len != 0, G_TYPE_INVALID);
534
535   if (pos < 0 || pos >= path->elems->len)
536     pos = path->elems->len - 1;
537
538   elem = &g_array_index (path->elems, GtkPathElement, pos);
539   return elem->type;
540 }
541
542 /**
543  * gtk_widget_path_iter_set_object_type:
544  * @path: a #GtkWidgetPath
545  * @pos: position to modify, -1 for the path head
546  * @type: object type to set
547  *
548  * Sets the object type for a given position in the widget hierarchy
549  * defined by @path.
550  *
551  * Since: 3.0
552  **/
553 void
554 gtk_widget_path_iter_set_object_type (GtkWidgetPath *path,
555                                       gint           pos,
556                                       GType          type)
557 {
558   GtkPathElement *elem;
559
560   g_return_if_fail (path != NULL);
561   g_return_if_fail (path->elems->len != 0);
562
563   if (pos < 0 || pos >= path->elems->len)
564     pos = path->elems->len - 1;
565
566   elem = &g_array_index (path->elems, GtkPathElement, pos);
567   elem->type = type;
568 }
569
570 /**
571  * gtk_widget_path_iter_get_name:
572  * @path: a #GtkWidgetPath
573  * @pos: position to get the widget name for, -1 for the path head
574  *
575  * Returns the name corresponding to the widget found at
576  * the position @pos in the widget hierarchy defined by
577  * @path
578  *
579  * Returns: The widget name, or %NULL if none was set.
580  **/
581 G_CONST_RETURN gchar *
582 gtk_widget_path_iter_get_name (const GtkWidgetPath *path,
583                                gint                 pos)
584 {
585   GtkPathElement *elem;
586
587   g_return_val_if_fail (path != NULL, NULL);
588   g_return_val_if_fail (path->elems->len != 0, NULL);
589
590   if (pos < 0 || pos >= path->elems->len)
591     pos = path->elems->len - 1;
592
593   elem = &g_array_index (path->elems, GtkPathElement, pos);
594   return g_quark_to_string (elem->name);
595 }
596
597 /**
598  * gtk_widget_path_iter_set_name:
599  * @path: a #GtkWidgetPath
600  * @pos: position to modify, -1 for the path head
601  * @name: widget name
602  *
603  * Sets the widget name for the widget found at position @pos
604  * in the widget hierarchy defined by @path.
605  *
606  * Since: 3.0
607  **/
608 void
609 gtk_widget_path_iter_set_name (GtkWidgetPath *path,
610                                gint           pos,
611                                const gchar   *name)
612 {
613   GtkPathElement *elem;
614
615   g_return_if_fail (path != NULL);
616   g_return_if_fail (path->elems->len != 0);
617   g_return_if_fail (name != NULL);
618
619   if (pos < 0 || pos >= path->elems->len)
620     pos = path->elems->len - 1;
621
622   elem = &g_array_index (path->elems, GtkPathElement, pos);
623
624   elem->name = g_quark_from_string (name);
625 }
626
627 /**
628  * gtk_widget_path_iter_has_qname:
629  * @path: a #GtkWidgetPath
630  * @pos: position to query, -1 for the path head
631  * @qname: widget name as a #GQuark
632  *
633  * See gtk_widget_path_iter_has_name(). This is a version
634  * that operates on #GQuark<!-- -->s.
635  *
636  * Returns: %TRUE if the widget at @pos has this name
637  *
638  * Since: 3.0
639  **/
640 gboolean
641 gtk_widget_path_iter_has_qname (const GtkWidgetPath *path,
642                                 gint                 pos,
643                                 GQuark               qname)
644 {
645   GtkPathElement *elem;
646
647   g_return_val_if_fail (path != NULL, FALSE);
648   g_return_val_if_fail (path->elems->len != 0, FALSE);
649   g_return_val_if_fail (qname != 0, FALSE);
650
651   if (pos < 0 || pos >= path->elems->len)
652     pos = path->elems->len - 1;
653
654   elem = &g_array_index (path->elems, GtkPathElement, pos);
655
656   return (elem->name == qname);
657 }
658
659 /**
660  * gtk_widget_path_iter_has_name:
661  * @path: a #GtkWidgetPath
662  * @pos: position to query, -1 for the path head
663  * @name: a widget name
664  *
665  * Returns %TRUE if the widget at position @pos has the name @name,
666  * %FALSE otherwise.
667  *
668  * Returns: %TRUE if the widget at @pos has this name
669  *
670  * Since: 3.0
671  **/
672 gboolean
673 gtk_widget_path_iter_has_name (const GtkWidgetPath *path,
674                                gint                 pos,
675                                const gchar         *name)
676 {
677   GQuark qname;
678
679   g_return_val_if_fail (path != NULL, FALSE);
680   g_return_val_if_fail (path->elems->len != 0, FALSE);
681
682   if (pos < 0 || pos >= path->elems->len)
683     pos = path->elems->len - 1;
684
685   qname = g_quark_try_string (name);
686
687   if (qname == 0)
688     return FALSE;
689
690   return gtk_widget_path_iter_has_qname (path, pos, qname);
691 }
692
693 /**
694  * gtk_widget_path_iter_add_class:
695  * @path: a #GtkWidget
696  * @pos: position to modify, -1 for the path head
697  * @name: a class name
698  *
699  * Adds the class @name to the widget at position @pos in
700  * the hierarchy defined in @path. See
701  * gtk_style_context_add_class().
702  *
703  * Since: 3.0
704  **/
705 void
706 gtk_widget_path_iter_add_class (GtkWidgetPath *path,
707                                 gint           pos,
708                                 const gchar   *name)
709 {
710   GtkPathElement *elem;
711   gboolean added = FALSE;
712   GQuark qname;
713   guint i;
714
715   g_return_if_fail (path != NULL);
716   g_return_if_fail (path->elems->len != 0);
717   g_return_if_fail (name != NULL);
718
719   if (pos < 0 || pos >= path->elems->len)
720     pos = path->elems->len - 1;
721
722   elem = &g_array_index (path->elems, GtkPathElement, pos);
723   qname = g_quark_from_string (name);
724
725   if (!elem->classes)
726     elem->classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
727
728   for (i = 0; i < elem->classes->len; i++)
729     {
730       GQuark quark;
731
732       quark = g_array_index (elem->classes, GQuark, i);
733
734       if (qname == quark)
735         {
736           /* Already there */
737           added = TRUE;
738           break;
739         }
740       if (qname < quark)
741         {
742           g_array_insert_val (elem->classes, i, qname);
743           added = TRUE;
744           break;
745         }
746     }
747
748   if (!added)
749     g_array_append_val (elem->classes, qname);
750 }
751
752 /**
753  * gtk_widget_path_iter_remove_class:
754  * @path: a #GtkWidgetPath
755  * @pos: position to modify, -1 for the path head
756  * @name: class name
757  *
758  * Removes the class @name from the widget at position @pos in
759  * the hierarchy defined in @path.
760  *
761  * Since: 3.0
762  **/
763 void
764 gtk_widget_path_iter_remove_class (GtkWidgetPath *path,
765                                    gint           pos,
766                                    const gchar   *name)
767 {
768   GtkPathElement *elem;
769   GQuark qname;
770   guint i;
771
772   g_return_if_fail (path != NULL);
773   g_return_if_fail (path->elems->len != 0);
774   g_return_if_fail (name != NULL);
775
776   if (pos < 0 || pos >= path->elems->len)
777     pos = path->elems->len - 1;
778
779   qname = g_quark_try_string (name);
780
781   if (qname == 0)
782     return;
783
784   elem = &g_array_index (path->elems, GtkPathElement, pos);
785
786   if (!elem->classes)
787     return;
788
789   for (i = 0; i < elem->classes->len; i++)
790     {
791       GQuark quark;
792
793       quark = g_array_index (elem->classes, GQuark, i);
794
795       if (quark > qname)
796         break;
797       else if (quark == qname)
798         {
799           g_array_remove_index (elem->classes, i);
800           break;
801         }
802     }
803 }
804
805 /**
806  * gtk_widget_path_iter_clear_classes:
807  * @path: a #GtkWidget
808  * @pos: position to modify, -1 for the path head
809  *
810  * Removes all classes from the widget at position @pos in the
811  * hierarchy defined in @path.
812  *
813  * Since: 3.0
814  **/
815 void
816 gtk_widget_path_iter_clear_classes (GtkWidgetPath *path,
817                                     gint           pos)
818 {
819   GtkPathElement *elem;
820
821   g_return_if_fail (path != NULL);
822   g_return_if_fail (path->elems->len != 0);
823
824   if (pos < 0 || pos >= path->elems->len)
825     pos = path->elems->len - 1;
826
827   elem = &g_array_index (path->elems, GtkPathElement, pos);
828
829   if (!elem->classes)
830     return;
831
832   if (elem->classes->len > 0)
833     g_array_remove_range (elem->classes, 0, elem->classes->len);
834 }
835
836 /**
837  * gtk_widget_path_iter_list_classes:
838  * @path: a #GtkWidgetPath
839  * @pos: position to query, -1 for the path head
840  *
841  * Returns a list with all the class names defined for the widget
842  * at position @pos in the hierarchy defined in @path.
843  *
844  * Returns: (transfer container) (element-type utf8): The list of
845  *          classes, This is a list of strings, the #GSList contents
846  *          are owned by GTK+, but you should use g_slist_free() to
847  *          free the list itself.
848  *
849  * Since: 3.0
850  **/
851 GSList *
852 gtk_widget_path_iter_list_classes (const GtkWidgetPath *path,
853                                    gint                 pos)
854 {
855   GtkPathElement *elem;
856   GSList *list = NULL;
857   guint i;
858
859   g_return_val_if_fail (path != NULL, NULL);
860   g_return_val_if_fail (path->elems->len != 0, NULL);
861
862   if (pos < 0 || pos >= path->elems->len)
863     pos = path->elems->len - 1;
864
865   elem = &g_array_index (path->elems, GtkPathElement, pos);
866
867   if (!elem->classes)
868     return NULL;
869
870   for (i = 0; i < elem->classes->len; i++)
871     {
872       GQuark quark;
873
874       quark = g_array_index (elem->classes, GQuark, i);
875       list = g_slist_prepend (list, (gchar *) g_quark_to_string (quark));
876     }
877
878   return g_slist_reverse (list);
879 }
880
881 /**
882  * gtk_widget_path_iter_has_qclass:
883  * @path: a #GtkWidgetPath
884  * @pos: position to query, -1 for the path head
885  * @qname: class name as a #GQuark
886  *
887  * See gtk_widget_path_iter_has_class(). This is a version that operates
888  * with GQuark<!-- -->s.
889  *
890  * Returns: %TRUE if the widget at @pos has the class defined.
891  *
892  * Since: 3.0
893  **/
894 gboolean
895 gtk_widget_path_iter_has_qclass (const GtkWidgetPath *path,
896                                  gint                 pos,
897                                  GQuark               qname)
898 {
899   GtkPathElement *elem;
900   guint i;
901
902   g_return_val_if_fail (path != NULL, FALSE);
903   g_return_val_if_fail (path->elems->len != 0, FALSE);
904   g_return_val_if_fail (qname != 0, FALSE);
905
906   if (pos < 0 || pos >= path->elems->len)
907     pos = path->elems->len - 1;
908
909   elem = &g_array_index (path->elems, GtkPathElement, pos);
910
911   if (!elem->classes)
912     return FALSE;
913
914   for (i = 0; i < elem->classes->len; i++)
915     {
916       GQuark quark;
917
918       quark = g_array_index (elem->classes, GQuark, i);
919
920       if (quark == qname)
921         return TRUE;
922       else if (quark > qname)
923         break;
924     }
925
926   return FALSE;
927 }
928
929 /**
930  * gtk_widget_path_iter_has_class:
931  * @path: a #GtkWidgetPath
932  * @pos: position to query, -1 for the path head
933  * @name: class name
934  *
935  * Returns %TRUE if the widget at position @pos has the class @name
936  * defined, %FALSE otherwise.
937  *
938  * Returns: %TRUE if the class @name is defined for the widget at @pos
939  *
940  * Since: 3.0
941  **/
942 gboolean
943 gtk_widget_path_iter_has_class (const GtkWidgetPath *path,
944                                 gint                 pos,
945                                 const gchar         *name)
946 {
947   GQuark qname;
948
949   g_return_val_if_fail (path != NULL, FALSE);
950   g_return_val_if_fail (path->elems->len != 0, FALSE);
951   g_return_val_if_fail (name != NULL, FALSE);
952
953   if (pos < 0 || pos >= path->elems->len)
954     pos = path->elems->len - 1;
955
956   qname = g_quark_try_string (name);
957
958   if (qname == 0)
959     return FALSE;
960
961   return gtk_widget_path_iter_has_qclass (path, pos, qname);
962 }
963
964 /**
965  * gtk_widget_path_iter_add_region:
966  * @path: a #GtkWidgetPath
967  * @pos: position to modify, -1 for the path head
968  * @name: region name
969  * @flags: flags affecting the region
970  *
971  * Adds the region @name to the widget at position @pos in
972  * the hierarchy defined in @path. See
973  * gtk_style_context_add_region().
974  *
975  * <note><para>Region names must only contain lowercase letters
976  * and '-', starting always with a lowercase letter.</para></note>
977  *
978  * Since: 3.0
979  **/
980 void
981 gtk_widget_path_iter_add_region (GtkWidgetPath  *path,
982                                  gint            pos,
983                                  const gchar    *name,
984                                  GtkRegionFlags  flags)
985 {
986   GtkPathElement *elem;
987   GQuark qname;
988
989   g_return_if_fail (path != NULL);
990   g_return_if_fail (path->elems->len != 0);
991   g_return_if_fail (name != NULL);
992   g_return_if_fail (_gtk_style_context_check_region_name (name));
993
994   if (pos < 0 || pos >= path->elems->len)
995     pos = path->elems->len - 1;
996
997   elem = &g_array_index (path->elems, GtkPathElement, pos);
998   qname = g_quark_from_string (name);
999
1000   if (!elem->regions)
1001     elem->regions = g_hash_table_new (NULL, NULL);
1002
1003   g_hash_table_insert (elem->regions,
1004                        GUINT_TO_POINTER (qname),
1005                        GUINT_TO_POINTER (flags));
1006 }
1007
1008 /**
1009  * gtk_widget_path_iter_remove_region:
1010  * @path: a #GtkWidgetPath
1011  * @pos: position to modify, -1 for the path head
1012  * @name: region name
1013  *
1014  * Removes the region @name from the widget at position @pos in
1015  * the hierarchy defined in @path.
1016  *
1017  * Since: 3.0
1018  **/
1019 void
1020 gtk_widget_path_iter_remove_region (GtkWidgetPath *path,
1021                                     gint           pos,
1022                                     const gchar   *name)
1023 {
1024   GtkPathElement *elem;
1025   GQuark qname;
1026
1027   g_return_if_fail (path != NULL);
1028   g_return_if_fail (path->elems->len != 0);
1029   g_return_if_fail (name != NULL);
1030
1031   if (pos < 0 || pos >= path->elems->len)
1032     pos = path->elems->len - 1;
1033
1034   qname = g_quark_try_string (name);
1035
1036   if (qname == 0)
1037     return;
1038
1039   elem = &g_array_index (path->elems, GtkPathElement, pos);
1040
1041   if (elem->regions)
1042     g_hash_table_remove (elem->regions, GUINT_TO_POINTER (qname));
1043 }
1044
1045 /**
1046  * gtk_widget_path_iter_clear_regions:
1047  * @path: a #GtkWidgetPath
1048  * @pos: position to modify, -1 for the path head
1049  *
1050  * Removes all regions from the widget at position @pos in the
1051  * hierarchy defined in @path.
1052  *
1053  * Since: 3.0
1054  **/
1055 void
1056 gtk_widget_path_iter_clear_regions (GtkWidgetPath *path,
1057                                     gint           pos)
1058 {
1059   GtkPathElement *elem;
1060
1061   g_return_if_fail (path != NULL);
1062   g_return_if_fail (path->elems->len != 0);
1063
1064   if (pos < 0 || pos >= path->elems->len)
1065     pos = path->elems->len - 1;
1066
1067   elem = &g_array_index (path->elems, GtkPathElement, pos);
1068
1069   if (elem->regions)
1070     g_hash_table_remove_all (elem->regions);
1071 }
1072
1073 /**
1074  * gtk_widget_path_iter_list_regions:
1075  * @path: a #GtkWidgetPath
1076  * @pos: position to query, -1 for the path head
1077  *
1078  * Returns a list with all the region names defined for the widget
1079  * at position @pos in the hierarchy defined in @path.
1080  *
1081  * Returns: (transfer container) (element-type utf8): The list of
1082  *          regions, This is a list of strings, the #GSList contents
1083  *          are owned by GTK+, but you should use g_slist_free() to
1084  *          free the list itself.
1085  *
1086  * Since: 3.0
1087  **/
1088 GSList *
1089 gtk_widget_path_iter_list_regions (const GtkWidgetPath *path,
1090                                    gint                 pos)
1091 {
1092   GtkPathElement *elem;
1093   GHashTableIter iter;
1094   GSList *list = NULL;
1095   gpointer key;
1096
1097   g_return_val_if_fail (path != NULL, NULL);
1098   g_return_val_if_fail (path->elems->len != 0, NULL);
1099
1100   if (pos < 0 || pos >= path->elems->len)
1101     pos = path->elems->len - 1;
1102
1103   elem = &g_array_index (path->elems, GtkPathElement, pos);
1104
1105   if (!elem->regions)
1106     return NULL;
1107
1108   g_hash_table_iter_init (&iter, elem->regions);
1109
1110   while (g_hash_table_iter_next (&iter, &key, NULL))
1111     {
1112       GQuark qname;
1113
1114       qname = GPOINTER_TO_UINT (key);
1115       list = g_slist_prepend (list, (gchar *) g_quark_to_string (qname));
1116     }
1117
1118   return list;
1119 }
1120
1121 /**
1122  * gtk_widget_path_iter_has_qregion:
1123  * @path: a #GtkWidgetPath
1124  * @pos: position to query, -1 for the path head
1125  * @qname: region name as a #GQuark
1126  * @flags: (out): return location for the region flags
1127  *
1128  * See gtk_widget_path_iter_has_region(). This is a version that operates
1129  * with GQuark<!-- -->s.
1130  *
1131  * Returns: %TRUE if the widget at @pos has the region defined.
1132  *
1133  * Since: 3.0
1134  **/
1135 gboolean
1136 gtk_widget_path_iter_has_qregion (const GtkWidgetPath *path,
1137                                   gint                 pos,
1138                                   GQuark               qname,
1139                                   GtkRegionFlags      *flags)
1140 {
1141   GtkPathElement *elem;
1142   gpointer value;
1143
1144   g_return_val_if_fail (path != NULL, FALSE);
1145   g_return_val_if_fail (path->elems->len != 0, FALSE);
1146   g_return_val_if_fail (qname != 0, FALSE);
1147
1148   if (pos < 0 || pos >= path->elems->len)
1149     pos = path->elems->len - 1;
1150
1151   elem = &g_array_index (path->elems, GtkPathElement, pos);
1152
1153   if (!elem->regions)
1154     return FALSE;
1155
1156   if (!g_hash_table_lookup_extended (elem->regions,
1157                                      GUINT_TO_POINTER (qname),
1158                                      NULL, &value))
1159     return FALSE;
1160
1161   if (flags)
1162     *flags = GPOINTER_TO_UINT (value);
1163
1164   return TRUE;
1165 }
1166
1167 /**
1168  * gtk_widget_path_iter_has_region:
1169  * @path: a #GtkWidgetPath
1170  * @pos: position to query, -1 for the path head
1171  * @name: region name
1172  * @flags: (out): return location for the region flags
1173  *
1174  * Returns %TRUE if the widget at position @pos has the class @name
1175  * defined, %FALSE otherwise.
1176  *
1177  * Returns: %TRUE if the class @name is defined for the widget at @pos
1178  *
1179  * Since: 3.0
1180  **/
1181 gboolean
1182 gtk_widget_path_iter_has_region (const GtkWidgetPath *path,
1183                                  gint                 pos,
1184                                  const gchar         *name,
1185                                  GtkRegionFlags      *flags)
1186 {
1187   GQuark qname;
1188
1189   g_return_val_if_fail (path != NULL, FALSE);
1190   g_return_val_if_fail (path->elems->len != 0, FALSE);
1191   g_return_val_if_fail (name != NULL, FALSE);
1192
1193   if (pos < 0 || pos >= path->elems->len)
1194     pos = path->elems->len - 1;
1195
1196   qname = g_quark_try_string (name);
1197
1198   if (qname == 0)
1199     return FALSE;
1200
1201   return gtk_widget_path_iter_has_qregion (path, pos, qname, flags);
1202 }
1203
1204 /**
1205  * gtk_widget_path_get_object_type:
1206  * @path: a #GtkWidget
1207  *
1208  * Returns the topmost object type, that is, the object type this path
1209  * is representing.
1210  *
1211  * Returns: The object type
1212  *
1213  * Since: 3.0
1214  **/
1215 GType
1216 gtk_widget_path_get_object_type (const GtkWidgetPath *path)
1217 {
1218   GtkPathElement *elem;
1219
1220   g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
1221
1222   elem = &g_array_index (path->elems, GtkPathElement,
1223                          path->elems->len - 1);
1224   return elem->type;
1225 }
1226
1227 /**
1228  * gtk_widget_path_is_type:
1229  * @path: a #GtkWidgetPath
1230  * @type: widget type to match
1231  *
1232  * Returns %TRUE if the widget type represented by this path
1233  * is @type, or a subtype of it.
1234  *
1235  * Returns: %TRUE if the widget represented by @path is of type @type
1236  *
1237  * Since: 3.0
1238  **/
1239 gboolean
1240 gtk_widget_path_is_type (const GtkWidgetPath *path,
1241                          GType                type)
1242 {
1243   GtkPathElement *elem;
1244
1245   g_return_val_if_fail (path != NULL, FALSE);
1246
1247   elem = &g_array_index (path->elems, GtkPathElement,
1248                          path->elems->len - 1);
1249
1250   if (elem->type == type ||
1251       g_type_is_a (elem->type, type))
1252     return TRUE;
1253
1254   return FALSE;
1255 }
1256
1257 /**
1258  * gtk_widget_path_has_parent:
1259  * @path: a #GtkWidgetPath
1260  * @type: widget type to check in parents
1261  *
1262  * Returns %TRUE if any of the parents of the widget represented
1263  * in @path is of type @type, or any subtype of it.
1264  *
1265  * Returns: %TRUE if any parent is of type @type
1266  *
1267  * Since: 3.0
1268  **/
1269 gboolean
1270 gtk_widget_path_has_parent (const GtkWidgetPath *path,
1271                             GType                type)
1272 {
1273   guint i;
1274
1275   g_return_val_if_fail (path != NULL, FALSE);
1276
1277   for (i = 0; i < path->elems->len - 1; i++)
1278     {
1279       GtkPathElement *elem;
1280
1281       elem = &g_array_index (path->elems, GtkPathElement, i);
1282
1283       if (elem->type == type ||
1284           g_type_is_a (elem->type, type))
1285         return TRUE;
1286     }
1287
1288   return FALSE;
1289 }