]> Pileus Git - ~andy/gtk/blob - gtk/gtkwidgetpath.c
API: Add gtk_widget_path_to_string()
[~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_copy, gtk_widget_path_free)
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 };
97
98 struct _GtkWidgetPath
99 {
100   GArray *elems; /* First element contains the described widget */
101 };
102
103 /**
104  * gtk_widget_path_new:
105  *
106  * Returns an empty widget path.
107  *
108  * Returns: (transfer full): A newly created, empty, #GtkWidgetPath
109  *
110  * Since: 3.0
111  **/
112 GtkWidgetPath *
113 gtk_widget_path_new (void)
114 {
115   GtkWidgetPath *path;
116
117   path = g_slice_new0 (GtkWidgetPath);
118   path->elems = g_array_new (FALSE, TRUE, sizeof (GtkPathElement));
119
120   return path;
121 }
122
123 /**
124  * gtk_widget_path_copy:
125  * @path: a #GtkWidgetPath
126  *
127  * Returns a copy of @path
128  *
129  * Returns: (transfer full): a copy of @path
130  *
131  * Since: 3.0
132  **/
133 GtkWidgetPath *
134 gtk_widget_path_copy (const GtkWidgetPath *path)
135 {
136   GtkWidgetPath *new_path;
137   guint i;
138
139   g_return_val_if_fail (path != NULL, NULL);
140
141   new_path = gtk_widget_path_new ();
142
143   for (i = 0; i < path->elems->len; i++)
144     {
145       GtkPathElement *elem, new = { 0 };
146
147       elem = &g_array_index (path->elems, GtkPathElement, i);
148
149       new.type = elem->type;
150       new.name = elem->name;
151
152       if (elem->regions)
153         {
154           GHashTableIter iter;
155           gpointer key, value;
156
157           g_hash_table_iter_init (&iter, elem->regions);
158           new.regions = g_hash_table_new (NULL, NULL);
159
160           while (g_hash_table_iter_next (&iter, &key, &value))
161             g_hash_table_insert (new.regions, key, value);
162         }
163
164       if (elem->classes)
165         {
166           new.classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
167           g_array_append_vals (new.classes, elem->classes->data, elem->classes->len);
168         }
169
170       g_array_append_val (new_path->elems, new);
171     }
172
173   return new_path;
174 }
175
176 /**
177  * gtk_widget_path_free:
178  * @path: a #GtkWidgetPath
179  *
180  * Frees a #GtkWidgetPath.
181  *
182  * Since: 3.0
183  **/
184 void
185 gtk_widget_path_free (GtkWidgetPath *path)
186 {
187   guint i;
188
189   g_return_if_fail (path != NULL);
190
191   for (i = 0; i < path->elems->len; i++)
192     {
193       GtkPathElement *elem;
194
195       elem = &g_array_index (path->elems, GtkPathElement, i);
196
197       if (elem->regions)
198         g_hash_table_destroy (elem->regions);
199
200       if (elem->classes)
201         g_array_free (elem->classes, TRUE);
202     }
203
204   g_array_free (path->elems, TRUE);
205   g_slice_free (GtkWidgetPath, path);
206 }
207
208 /**
209  * gtk_widget_path_length:
210  * @path: a #GtkWidgetPath
211  *
212  * Returns the number of #GtkWidget #GTypes between the represented
213  * widget and its topmost container.
214  *
215  * Returns: the number of elements in the path
216  *
217  * Since: 3.0
218  **/
219 gint
220 gtk_widget_path_length (const GtkWidgetPath *path)
221 {
222   g_return_val_if_fail (path != NULL, 0);
223
224   return path->elems->len;
225 }
226
227 /**
228  * gtk_widget_path_to_string:
229  * @path: the path
230  *
231  * Dumps the widget path into a string representation. It tries to match
232  * the CSS style as closely as possible (Note that there might be paths
233  * that cannot be represented in CSS).
234  *                 
235  * The main use of this code is for debugging purposes, so that you can
236  * g_print() the path or dump it in a gdb session.
237  *
238  * Returns: A new string describing @path.
239  **/
240 char *
241 gtk_widget_path_to_string (const GtkWidgetPath *path)
242 {
243   GString *string;
244   guint i, j;
245
246   g_return_val_if_fail (path != NULL, NULL);
247
248   string = g_string_new ("");
249
250   for (i = 0; i < path->elems->len; i++)
251     {
252       GtkPathElement *elem;
253
254       elem = &g_array_index (path->elems, GtkPathElement, i);
255
256       if (i > 0)
257         g_string_append_c (string, ' ');
258
259       g_string_append (string, g_type_name (elem->type));
260
261       if (elem->name)
262         {
263           g_string_append_c (string, '(');
264           g_string_append (string, g_quark_to_string (elem->name));
265           g_string_append_c (string, ')');
266         }
267
268       if (elem->classes)
269         {
270           for (j = 0; j < elem->classes->len; j++)
271             {
272               g_string_append_c (string, '.');
273               g_string_append (string, g_quark_to_string (g_array_index (elem->classes, GQuark, j)));
274             }
275         }
276
277       if (elem->regions)
278         {
279           GHashTableIter iter;
280           gpointer key, value;
281
282           g_hash_table_iter_init (&iter, elem->regions);
283           while (g_hash_table_iter_next (&iter, &key, &value))
284             {
285               GtkRegionFlags flags = GPOINTER_TO_UINT (value);
286               static const char *flag_names[] = {
287                 "even",
288                 "odd",
289                 "first",
290                 "last",
291                 "sorted"
292               };
293
294               g_string_append_c (string, ' ');
295               g_string_append (string, g_quark_to_string (GPOINTER_TO_UINT (key)));
296               for (j = 0; j < G_N_ELEMENTS(flag_names); j++)
297                 {
298                   if (flags & (1 << j))
299                     {
300                       g_string_append_c (string, ':');
301                       g_string_append (string, flag_names[j]);
302                     }
303                 }
304             }
305         }
306     }
307
308   return g_string_free (string, FALSE);
309 }
310
311 /**
312  * gtk_widget_path_prepend_type:
313  * @path: a #GtkWidgetPath
314  * @type: widget type to prepend
315  *
316  * Prepends a widget type to the widget hierachy represented by @path.
317  *
318  * Since: 3.0
319  **/
320 void
321 gtk_widget_path_prepend_type (GtkWidgetPath *path,
322                               GType          type)
323 {
324   GtkPathElement new = { 0 };
325
326   g_return_if_fail (path != NULL);
327
328   new.type = type;
329   g_array_prepend_val (path->elems, new);
330 }
331
332 /**
333  * gtk_widget_path_append_type:
334  * @path: a #GtkWidgetPath
335  * @type: widget type to append
336  *
337  * Appends a widget type to the widget hierachy represented by @path.
338  *
339  * Returns: the position where the element was inserted
340  *
341  * Since: 3.0
342  **/
343 gint
344 gtk_widget_path_append_type (GtkWidgetPath *path,
345                              GType          type)
346 {
347   GtkPathElement new = { 0 };
348
349   g_return_val_if_fail (path != NULL, 0);
350
351   new.type = type;
352   g_array_append_val (path->elems, new);
353
354   return path->elems->len - 1;
355 }
356
357 /**
358  * gtk_widget_path_iter_get_object_type:
359  * @path: a #GtkWidgetPath
360  * @pos: position to get the object type for, -1 for the path head
361  *
362  * Returns the object #GType that is at position @pos in the widget
363  * hierarchy defined in @path.
364  *
365  * Returns: a widget type
366  *
367  * Since: 3.0
368  **/
369 GType
370 gtk_widget_path_iter_get_object_type (const GtkWidgetPath *path,
371                                       gint                 pos)
372 {
373   GtkPathElement *elem;
374
375   g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
376   g_return_val_if_fail (path->elems->len != 0, G_TYPE_INVALID);
377
378   if (pos < 0 || pos >= path->elems->len)
379     pos = path->elems->len - 1;
380
381   elem = &g_array_index (path->elems, GtkPathElement, pos);
382   return elem->type;
383 }
384
385 /**
386  * gtk_widget_path_iter_set_object_type:
387  * @path: a #GtkWidgetPath
388  * @pos: position to modify, -1 for the path head
389  * @type: object type to set
390  *
391  * Sets the object type for a given position in the widget hierarchy
392  * defined by @path.
393  *
394  * Since: 3.0
395  **/
396 void
397 gtk_widget_path_iter_set_object_type (GtkWidgetPath *path,
398                                       gint           pos,
399                                       GType          type)
400 {
401   GtkPathElement *elem;
402
403   g_return_if_fail (path != NULL);
404   g_return_if_fail (path->elems->len != 0);
405
406   if (pos < 0 || pos >= path->elems->len)
407     pos = path->elems->len - 1;
408
409   elem = &g_array_index (path->elems, GtkPathElement, pos);
410   elem->type = type;
411 }
412
413 /**
414  * gtk_widget_path_iter_get_name:
415  * @path: a #GtkWidgetPath
416  * @pos: position to get the widget name for, -1 for the path head
417  *
418  * Returns the name corresponding to the widget found at
419  * the position @pos in the widget hierarchy defined by
420  * @path
421  *
422  * Returns: The widget name, or %NULL if none was set.
423  **/
424 G_CONST_RETURN gchar *
425 gtk_widget_path_iter_get_name (const GtkWidgetPath *path,
426                                gint                 pos)
427 {
428   GtkPathElement *elem;
429
430   g_return_val_if_fail (path != NULL, NULL);
431   g_return_val_if_fail (path->elems->len != 0, NULL);
432
433   if (pos < 0 || pos >= path->elems->len)
434     pos = path->elems->len - 1;
435
436   elem = &g_array_index (path->elems, GtkPathElement, pos);
437   return g_quark_to_string (elem->name);
438 }
439
440 /**
441  * gtk_widget_path_iter_set_name:
442  * @path: a #GtkWidgetPath
443  * @pos: position to modify, -1 for the path head
444  * @name: widget name
445  *
446  * Sets the widget name for the widget found at position @pos
447  * in the widget hierarchy defined by @path.
448  *
449  * Since: 3.0
450  **/
451 void
452 gtk_widget_path_iter_set_name (GtkWidgetPath *path,
453                                gint           pos,
454                                const gchar   *name)
455 {
456   GtkPathElement *elem;
457
458   g_return_if_fail (path != NULL);
459   g_return_if_fail (path->elems->len != 0);
460   g_return_if_fail (name != NULL);
461
462   if (pos < 0 || pos >= path->elems->len)
463     pos = path->elems->len - 1;
464
465   elem = &g_array_index (path->elems, GtkPathElement, pos);
466
467   elem->name = g_quark_from_string (name);
468 }
469
470 /**
471  * gtk_widget_path_iter_has_qname:
472  * @path: a #GtkWidgetPath
473  * @pos: position to query, -1 for the path head
474  * @qname: widget name as a #GQuark
475  *
476  * See gtk_widget_path_iter_has_name(). This is a version
477  * that operates on #GQuark<!-- -->s.
478  *
479  * Returns: %TRUE if the widget at @pos has this name
480  *
481  * Since: 3.0
482  **/
483 gboolean
484 gtk_widget_path_iter_has_qname (const GtkWidgetPath *path,
485                                 gint                 pos,
486                                 GQuark               qname)
487 {
488   GtkPathElement *elem;
489
490   g_return_val_if_fail (path != NULL, FALSE);
491   g_return_val_if_fail (path->elems->len != 0, FALSE);
492   g_return_val_if_fail (qname != 0, FALSE);
493
494   if (pos < 0 || pos >= path->elems->len)
495     pos = path->elems->len - 1;
496
497   elem = &g_array_index (path->elems, GtkPathElement, pos);
498
499   return (elem->name == qname);
500 }
501
502 /**
503  * gtk_widget_path_iter_has_name:
504  * @path: a #GtkWidgetPath
505  * @pos: position to query, -1 for the path head
506  * @name: a widget name
507  *
508  * Returns %TRUE if the widget at position @pos has the name @name,
509  * %FALSE otherwise.
510  *
511  * Returns: %TRUE if the widget at @pos has this name
512  *
513  * Since: 3.0
514  **/
515 gboolean
516 gtk_widget_path_iter_has_name (const GtkWidgetPath *path,
517                                gint                 pos,
518                                const gchar         *name)
519 {
520   GQuark qname;
521
522   g_return_val_if_fail (path != NULL, FALSE);
523   g_return_val_if_fail (path->elems->len != 0, FALSE);
524
525   if (pos < 0 || pos >= path->elems->len)
526     pos = path->elems->len - 1;
527
528   qname = g_quark_try_string (name);
529
530   if (qname == 0)
531     return FALSE;
532
533   return gtk_widget_path_iter_has_qname (path, pos, qname);
534 }
535
536 /**
537  * gtk_widget_path_iter_add_class:
538  * @path: a #GtkWidget
539  * @pos: position to modify, -1 for the path head
540  * @name: a class name
541  *
542  * Adds the class @name to the widget at position @pos in
543  * the hierarchy defined in @path. See
544  * gtk_style_context_add_class().
545  *
546  * Since: 3.0
547  **/
548 void
549 gtk_widget_path_iter_add_class (GtkWidgetPath *path,
550                                 gint           pos,
551                                 const gchar   *name)
552 {
553   GtkPathElement *elem;
554   gboolean added = FALSE;
555   GQuark qname;
556   guint i;
557
558   g_return_if_fail (path != NULL);
559   g_return_if_fail (path->elems->len != 0);
560   g_return_if_fail (name != NULL);
561
562   if (pos < 0 || pos >= path->elems->len)
563     pos = path->elems->len - 1;
564
565   elem = &g_array_index (path->elems, GtkPathElement, pos);
566   qname = g_quark_from_string (name);
567
568   if (!elem->classes)
569     elem->classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
570
571   for (i = 0; i < elem->classes->len; i++)
572     {
573       GQuark quark;
574
575       quark = g_array_index (elem->classes, GQuark, i);
576
577       if (qname == quark)
578         {
579           /* Already there */
580           added = TRUE;
581           break;
582         }
583       if (qname < quark)
584         {
585           g_array_insert_val (elem->classes, i, qname);
586           added = TRUE;
587           break;
588         }
589     }
590
591   if (!added)
592     g_array_append_val (elem->classes, qname);
593 }
594
595 /**
596  * gtk_widget_path_iter_remove_class:
597  * @path: a #GtkWidgetPath
598  * @pos: position to modify, -1 for the path head
599  * @name: class name
600  *
601  * Removes the class @name from the widget at position @pos in
602  * the hierarchy defined in @path.
603  *
604  * Since: 3.0
605  **/
606 void
607 gtk_widget_path_iter_remove_class (GtkWidgetPath *path,
608                                    gint           pos,
609                                    const gchar   *name)
610 {
611   GtkPathElement *elem;
612   GQuark qname;
613   guint i;
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   qname = g_quark_try_string (name);
623
624   if (qname == 0)
625     return;
626
627   elem = &g_array_index (path->elems, GtkPathElement, pos);
628
629   if (!elem->classes)
630     return;
631
632   for (i = 0; i < elem->classes->len; i++)
633     {
634       GQuark quark;
635
636       quark = g_array_index (elem->classes, GQuark, i);
637
638       if (quark > qname)
639         break;
640       else if (quark == qname)
641         {
642           g_array_remove_index (elem->classes, i);
643           break;
644         }
645     }
646 }
647
648 /**
649  * gtk_widget_path_iter_clear_classes:
650  * @path: a #GtkWidget
651  * @pos: position to modify, -1 for the path head
652  *
653  * Removes all classes from the widget at position @pos in the
654  * hierarchy defined in @path.
655  *
656  * Since: 3.0
657  **/
658 void
659 gtk_widget_path_iter_clear_classes (GtkWidgetPath *path,
660                                     gint           pos)
661 {
662   GtkPathElement *elem;
663
664   g_return_if_fail (path != NULL);
665   g_return_if_fail (path->elems->len != 0);
666
667   if (pos < 0 || pos >= path->elems->len)
668     pos = path->elems->len - 1;
669
670   elem = &g_array_index (path->elems, GtkPathElement, pos);
671
672   if (!elem->classes)
673     return;
674
675   if (elem->classes->len > 0)
676     g_array_remove_range (elem->classes, 0, elem->classes->len);
677 }
678
679 /**
680  * gtk_widget_path_iter_list_classes:
681  * @path: a #GtkWidgetPath
682  * @pos: position to query, -1 for the path head
683  *
684  * Returns a list with all the class names defined for the widget
685  * at position @pos in the hierarchy defined in @path.
686  *
687  * Returns: (transfer container) (element-type utf8): The list of
688  *          classes, This is a list of strings, the #GSList contents
689  *          are owned by GTK+, but you should use g_slist_free() to
690  *          free the list itself.
691  *
692  * Since: 3.0
693  **/
694 GSList *
695 gtk_widget_path_iter_list_classes (const GtkWidgetPath *path,
696                                    gint                 pos)
697 {
698   GtkPathElement *elem;
699   GSList *list = NULL;
700   guint i;
701
702   g_return_val_if_fail (path != NULL, NULL);
703   g_return_val_if_fail (path->elems->len != 0, NULL);
704
705   if (pos < 0 || pos >= path->elems->len)
706     pos = path->elems->len - 1;
707
708   elem = &g_array_index (path->elems, GtkPathElement, pos);
709
710   if (!elem->classes)
711     return NULL;
712
713   for (i = 0; i < elem->classes->len; i++)
714     {
715       GQuark quark;
716
717       quark = g_array_index (elem->classes, GQuark, i);
718       list = g_slist_prepend (list, (gchar *) g_quark_to_string (quark));
719     }
720
721   return g_slist_reverse (list);
722 }
723
724 /**
725  * gtk_widget_path_iter_has_qclass:
726  * @path: a #GtkWidgetPath
727  * @pos: position to query, -1 for the path head
728  * @qname: class name as a #GQuark
729  *
730  * See gtk_widget_path_iter_has_class(). This is a version that operates
731  * with GQuark<!-- -->s.
732  *
733  * Returns: %TRUE if the widget at @pos has the class defined.
734  *
735  * Since: 3.0
736  **/
737 gboolean
738 gtk_widget_path_iter_has_qclass (const GtkWidgetPath *path,
739                                  gint                 pos,
740                                  GQuark               qname)
741 {
742   GtkPathElement *elem;
743   guint i;
744
745   g_return_val_if_fail (path != NULL, FALSE);
746   g_return_val_if_fail (path->elems->len != 0, FALSE);
747   g_return_val_if_fail (qname != 0, FALSE);
748
749   if (pos < 0 || pos >= path->elems->len)
750     pos = path->elems->len - 1;
751
752   elem = &g_array_index (path->elems, GtkPathElement, pos);
753
754   if (!elem->classes)
755     return FALSE;
756
757   for (i = 0; i < elem->classes->len; i++)
758     {
759       GQuark quark;
760
761       quark = g_array_index (elem->classes, GQuark, i);
762
763       if (quark == qname)
764         return TRUE;
765       else if (quark > qname)
766         break;
767     }
768
769   return FALSE;
770 }
771
772 /**
773  * gtk_widget_path_iter_has_class:
774  * @path: a #GtkWidgetPath
775  * @pos: position to query, -1 for the path head
776  * @name: class name
777  *
778  * Returns %TRUE if the widget at position @pos has the class @name
779  * defined, %FALSE otherwise.
780  *
781  * Returns: %TRUE if the class @name is defined for the widget at @pos
782  *
783  * Since: 3.0
784  **/
785 gboolean
786 gtk_widget_path_iter_has_class (const GtkWidgetPath *path,
787                                 gint                 pos,
788                                 const gchar         *name)
789 {
790   GQuark qname;
791
792   g_return_val_if_fail (path != NULL, FALSE);
793   g_return_val_if_fail (path->elems->len != 0, FALSE);
794   g_return_val_if_fail (name != NULL, FALSE);
795
796   if (pos < 0 || pos >= path->elems->len)
797     pos = path->elems->len - 1;
798
799   qname = g_quark_try_string (name);
800
801   if (qname == 0)
802     return FALSE;
803
804   return gtk_widget_path_iter_has_qclass (path, pos, qname);
805 }
806
807 /**
808  * gtk_widget_path_iter_add_region:
809  * @path: a #GtkWidgetPath
810  * @pos: position to modify, -1 for the path head
811  * @name: region name
812  * @flags: flags affecting the region
813  *
814  * Adds the region @name to the widget at position @pos in
815  * the hierarchy defined in @path. See
816  * gtk_style_context_add_region().
817  *
818  * <note><para>Region names must only contain lowercase letters
819  * and '-', starting always with a lowercase letter.</para></note>
820  *
821  * Since: 3.0
822  **/
823 void
824 gtk_widget_path_iter_add_region (GtkWidgetPath  *path,
825                                  gint            pos,
826                                  const gchar    *name,
827                                  GtkRegionFlags  flags)
828 {
829   GtkPathElement *elem;
830   GQuark qname;
831
832   g_return_if_fail (path != NULL);
833   g_return_if_fail (path->elems->len != 0);
834   g_return_if_fail (name != NULL);
835   g_return_if_fail (_gtk_style_context_check_region_name (name));
836
837   if (pos < 0 || pos >= path->elems->len)
838     pos = path->elems->len - 1;
839
840   elem = &g_array_index (path->elems, GtkPathElement, pos);
841   qname = g_quark_from_string (name);
842
843   if (!elem->regions)
844     elem->regions = g_hash_table_new (NULL, NULL);
845
846   g_hash_table_insert (elem->regions,
847                        GUINT_TO_POINTER (qname),
848                        GUINT_TO_POINTER (flags));
849 }
850
851 /**
852  * gtk_widget_path_iter_remove_region:
853  * @path: a #GtkWidgetPath
854  * @pos: position to modify, -1 for the path head
855  * @name: region name
856  *
857  * Removes the region @name from the widget at position @pos in
858  * the hierarchy defined in @path.
859  *
860  * Since: 3.0
861  **/
862 void
863 gtk_widget_path_iter_remove_region (GtkWidgetPath *path,
864                                     gint           pos,
865                                     const gchar   *name)
866 {
867   GtkPathElement *elem;
868   GQuark qname;
869
870   g_return_if_fail (path != NULL);
871   g_return_if_fail (path->elems->len != 0);
872   g_return_if_fail (name != NULL);
873
874   if (pos < 0 || pos >= path->elems->len)
875     pos = path->elems->len - 1;
876
877   qname = g_quark_try_string (name);
878
879   if (qname == 0)
880     return;
881
882   elem = &g_array_index (path->elems, GtkPathElement, pos);
883
884   if (elem->regions)
885     g_hash_table_remove (elem->regions, GUINT_TO_POINTER (qname));
886 }
887
888 /**
889  * gtk_widget_path_iter_clear_regions:
890  * @path: a #GtkWidgetPath
891  * @pos: position to modify, -1 for the path head
892  *
893  * Removes all regions from the widget at position @pos in the
894  * hierarchy defined in @path.
895  *
896  * Since: 3.0
897  **/
898 void
899 gtk_widget_path_iter_clear_regions (GtkWidgetPath *path,
900                                     gint           pos)
901 {
902   GtkPathElement *elem;
903
904   g_return_if_fail (path != NULL);
905   g_return_if_fail (path->elems->len != 0);
906
907   if (pos < 0 || pos >= path->elems->len)
908     pos = path->elems->len - 1;
909
910   elem = &g_array_index (path->elems, GtkPathElement, pos);
911
912   if (elem->regions)
913     g_hash_table_remove_all (elem->regions);
914 }
915
916 /**
917  * gtk_widget_path_iter_list_regions:
918  * @path: a #GtkWidgetPath
919  * @pos: position to query, -1 for the path head
920  *
921  * Returns a list with all the region names defined for the widget
922  * at position @pos in the hierarchy defined in @path.
923  *
924  * Returns: (transfer container) (element-type utf8): The list of
925  *          regions, This is a list of strings, the #GSList contents
926  *          are owned by GTK+, but you should use g_slist_free() to
927  *          free the list itself.
928  *
929  * Since: 3.0
930  **/
931 GSList *
932 gtk_widget_path_iter_list_regions (const GtkWidgetPath *path,
933                                    gint                 pos)
934 {
935   GtkPathElement *elem;
936   GHashTableIter iter;
937   GSList *list = NULL;
938   gpointer key;
939
940   g_return_val_if_fail (path != NULL, NULL);
941   g_return_val_if_fail (path->elems->len != 0, NULL);
942
943   if (pos < 0 || pos >= path->elems->len)
944     pos = path->elems->len - 1;
945
946   elem = &g_array_index (path->elems, GtkPathElement, pos);
947
948   if (!elem->regions)
949     return NULL;
950
951   g_hash_table_iter_init (&iter, elem->regions);
952
953   while (g_hash_table_iter_next (&iter, &key, NULL))
954     {
955       GQuark qname;
956
957       qname = GPOINTER_TO_UINT (key);
958       list = g_slist_prepend (list, (gchar *) g_quark_to_string (qname));
959     }
960
961   return list;
962 }
963
964 /**
965  * gtk_widget_path_iter_has_qregion:
966  * @path: a #GtkWidgetPath
967  * @pos: position to query, -1 for the path head
968  * @qname: region name as a #GQuark
969  * @flags: (out): return location for the region flags
970  *
971  * See gtk_widget_path_iter_has_region(). This is a version that operates
972  * with GQuark<!-- -->s.
973  *
974  * Returns: %TRUE if the widget at @pos has the region defined.
975  *
976  * Since: 3.0
977  **/
978 gboolean
979 gtk_widget_path_iter_has_qregion (const GtkWidgetPath *path,
980                                   gint                 pos,
981                                   GQuark               qname,
982                                   GtkRegionFlags      *flags)
983 {
984   GtkPathElement *elem;
985   gpointer value;
986
987   g_return_val_if_fail (path != NULL, FALSE);
988   g_return_val_if_fail (path->elems->len != 0, FALSE);
989   g_return_val_if_fail (qname != 0, FALSE);
990
991   if (pos < 0 || pos >= path->elems->len)
992     pos = path->elems->len - 1;
993
994   elem = &g_array_index (path->elems, GtkPathElement, pos);
995
996   if (!elem->regions)
997     return FALSE;
998
999   if (!g_hash_table_lookup_extended (elem->regions,
1000                                      GUINT_TO_POINTER (qname),
1001                                      NULL, &value))
1002     return FALSE;
1003
1004   if (flags)
1005     *flags = GPOINTER_TO_UINT (value);
1006
1007   return TRUE;
1008 }
1009
1010 /**
1011  * gtk_widget_path_iter_has_region:
1012  * @path: a #GtkWidgetPath
1013  * @pos: position to query, -1 for the path head
1014  * @name: region name
1015  * @flags: (out): return location for the region flags
1016  *
1017  * Returns %TRUE if the widget at position @pos has the class @name
1018  * defined, %FALSE otherwise.
1019  *
1020  * Returns: %TRUE if the class @name is defined for the widget at @pos
1021  *
1022  * Since: 3.0
1023  **/
1024 gboolean
1025 gtk_widget_path_iter_has_region (const GtkWidgetPath *path,
1026                                  gint                 pos,
1027                                  const gchar         *name,
1028                                  GtkRegionFlags      *flags)
1029 {
1030   GQuark qname;
1031
1032   g_return_val_if_fail (path != NULL, FALSE);
1033   g_return_val_if_fail (path->elems->len != 0, FALSE);
1034   g_return_val_if_fail (name != NULL, FALSE);
1035
1036   if (pos < 0 || pos >= path->elems->len)
1037     pos = path->elems->len - 1;
1038
1039   qname = g_quark_try_string (name);
1040
1041   if (qname == 0)
1042     return FALSE;
1043
1044   return gtk_widget_path_iter_has_qregion (path, pos, qname, flags);
1045 }
1046
1047 /**
1048  * gtk_widget_path_get_object_type:
1049  * @path: a #GtkWidget
1050  *
1051  * Returns the topmost object type, that is, the object type this path
1052  * is representing.
1053  *
1054  * Returns: The object type
1055  *
1056  * Since: 3.0
1057  **/
1058 GType
1059 gtk_widget_path_get_object_type (const GtkWidgetPath *path)
1060 {
1061   GtkPathElement *elem;
1062
1063   g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
1064
1065   elem = &g_array_index (path->elems, GtkPathElement,
1066                          path->elems->len - 1);
1067   return elem->type;
1068 }
1069
1070 /**
1071  * gtk_widget_path_is_type:
1072  * @path: a #GtkWidgetPath
1073  * @type: widget type to match
1074  *
1075  * Returns %TRUE if the widget type represented by this path
1076  * is @type, or a subtype of it.
1077  *
1078  * Returns: %TRUE if the widget represented by @path is of type @type
1079  *
1080  * Since: 3.0
1081  **/
1082 gboolean
1083 gtk_widget_path_is_type (const GtkWidgetPath *path,
1084                          GType                type)
1085 {
1086   GtkPathElement *elem;
1087
1088   g_return_val_if_fail (path != NULL, FALSE);
1089
1090   elem = &g_array_index (path->elems, GtkPathElement,
1091                          path->elems->len - 1);
1092
1093   if (elem->type == type ||
1094       g_type_is_a (elem->type, type))
1095     return TRUE;
1096
1097   return FALSE;
1098 }
1099
1100 /**
1101  * gtk_widget_path_has_parent:
1102  * @path: a #GtkWidgetPath
1103  * @type: widget type to check in parents
1104  *
1105  * Returns %TRUE if any of the parents of the widget represented
1106  * in @path is of type @type, or any subtype of it.
1107  *
1108  * Returns: %TRUE if any parent is of type @type
1109  *
1110  * Since: 3.0
1111  **/
1112 gboolean
1113 gtk_widget_path_has_parent (const GtkWidgetPath *path,
1114                             GType                type)
1115 {
1116   guint i;
1117
1118   g_return_val_if_fail (path != NULL, FALSE);
1119
1120   for (i = 0; i < path->elems->len - 1; i++)
1121     {
1122       GtkPathElement *elem;
1123
1124       elem = &g_array_index (path->elems, GtkPathElement, i);
1125
1126       if (elem->type == type ||
1127           g_type_is_a (elem->type, type))
1128         return TRUE;
1129     }
1130
1131   return FALSE;
1132 }