]> Pileus Git - ~andy/gtk/blob - gtk/gtkwidgetpath.c
widgetpath: Sanitize pos argument for iters correctly
[~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_prepend_type:
229  * @path: a #GtkWidgetPath
230  * @type: widget type to prepend
231  *
232  * Prepends a widget type to the widget hierachy represented by @path.
233  *
234  * Since: 3.0
235  **/
236 void
237 gtk_widget_path_prepend_type (GtkWidgetPath *path,
238                               GType          type)
239 {
240   GtkPathElement new = { 0 };
241
242   g_return_if_fail (path != NULL);
243
244   new.type = type;
245   g_array_prepend_val (path->elems, new);
246 }
247
248 /**
249  * gtk_widget_path_append_type:
250  * @path: a #GtkWidgetPath
251  * @type: widget type to append
252  *
253  * Appends a widget type to the widget hierachy represented by @path.
254  *
255  * Returns: the position where the element was inserted
256  *
257  * Since: 3.0
258  **/
259 gint
260 gtk_widget_path_append_type (GtkWidgetPath *path,
261                              GType          type)
262 {
263   GtkPathElement new = { 0 };
264
265   g_return_val_if_fail (path != NULL, 0);
266
267   new.type = type;
268   g_array_append_val (path->elems, new);
269
270   return path->elems->len - 1;
271 }
272
273 /**
274  * gtk_widget_path_iter_get_object_type:
275  * @path: a #GtkWidgetPath
276  * @pos: position to get the object type for, -1 for the path head
277  *
278  * Returns the object #GType that is at position @pos in the widget
279  * hierarchy defined in @path.
280  *
281  * Returns: a widget type
282  *
283  * Since: 3.0
284  **/
285 GType
286 gtk_widget_path_iter_get_object_type (const GtkWidgetPath *path,
287                                       gint                 pos)
288 {
289   GtkPathElement *elem;
290
291   g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
292   g_return_val_if_fail (path->elems->len != 0, G_TYPE_INVALID);
293
294   if (pos < 0 || pos >= path->elems->len)
295     pos = path->elems->len - 1;
296
297   elem = &g_array_index (path->elems, GtkPathElement, pos);
298   return elem->type;
299 }
300
301 /**
302  * gtk_widget_path_iter_set_object_type:
303  * @path: a #GtkWidgetPath
304  * @pos: position to modify, -1 for the path head
305  * @type: object type to set
306  *
307  * Sets the object type for a given position in the widget hierarchy
308  * defined by @path.
309  *
310  * Since: 3.0
311  **/
312 void
313 gtk_widget_path_iter_set_object_type (GtkWidgetPath *path,
314                                       gint           pos,
315                                       GType          type)
316 {
317   GtkPathElement *elem;
318
319   g_return_if_fail (path != NULL);
320   g_return_if_fail (path->elems->len != 0);
321
322   if (pos < 0 || pos >= path->elems->len)
323     pos = path->elems->len - 1;
324
325   elem = &g_array_index (path->elems, GtkPathElement, pos);
326   elem->type = type;
327 }
328
329 /**
330  * gtk_widget_path_iter_get_name:
331  * @path: a #GtkWidgetPath
332  * @pos: position to get the widget name for, -1 for the path head
333  *
334  * Returns the name corresponding to the widget found at
335  * the position @pos in the widget hierarchy defined by
336  * @path
337  *
338  * Returns: The widget name, or %NULL if none was set.
339  **/
340 G_CONST_RETURN gchar *
341 gtk_widget_path_iter_get_name (const GtkWidgetPath *path,
342                                gint                 pos)
343 {
344   GtkPathElement *elem;
345
346   g_return_val_if_fail (path != NULL, NULL);
347   g_return_val_if_fail (path->elems->len != 0, NULL);
348
349   if (pos < 0 || pos >= path->elems->len)
350     pos = path->elems->len - 1;
351
352   elem = &g_array_index (path->elems, GtkPathElement, pos);
353   return g_quark_to_string (elem->name);
354 }
355
356 /**
357  * gtk_widget_path_iter_set_name:
358  * @path: a #GtkWidgetPath
359  * @pos: position to modify, -1 for the path head
360  * @name: widget name
361  *
362  * Sets the widget name for the widget found at position @pos
363  * in the widget hierarchy defined by @path.
364  *
365  * Since: 3.0
366  **/
367 void
368 gtk_widget_path_iter_set_name (GtkWidgetPath *path,
369                                gint           pos,
370                                const gchar   *name)
371 {
372   GtkPathElement *elem;
373
374   g_return_if_fail (path != NULL);
375   g_return_if_fail (path->elems->len != 0);
376   g_return_if_fail (name != NULL);
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
383   elem->name = g_quark_from_string (name);
384 }
385
386 /**
387  * gtk_widget_path_iter_has_qname:
388  * @path: a #GtkWidgetPath
389  * @pos: position to query, -1 for the path head
390  * @qname: widget name as a #GQuark
391  *
392  * See gtk_widget_path_iter_has_name(). This is a version
393  * that operates on #GQuark<!-- -->s.
394  *
395  * Returns: %TRUE if the widget at @pos has this name
396  *
397  * Since: 3.0
398  **/
399 gboolean
400 gtk_widget_path_iter_has_qname (const GtkWidgetPath *path,
401                                 gint                 pos,
402                                 GQuark               qname)
403 {
404   GtkPathElement *elem;
405
406   g_return_val_if_fail (path != NULL, FALSE);
407   g_return_val_if_fail (path->elems->len != 0, FALSE);
408   g_return_val_if_fail (qname != 0, FALSE);
409
410   if (pos < 0 || pos >= path->elems->len)
411     pos = path->elems->len - 1;
412
413   elem = &g_array_index (path->elems, GtkPathElement, pos);
414
415   return (elem->name == qname);
416 }
417
418 /**
419  * gtk_widget_path_iter_has_name:
420  * @path: a #GtkWidgetPath
421  * @pos: position to query, -1 for the path head
422  * @name: a widget name
423  *
424  * Returns %TRUE if the widget at position @pos has the name @name,
425  * %FALSE otherwise.
426  *
427  * Returns: %TRUE if the widget at @pos has this name
428  *
429  * Since: 3.0
430  **/
431 gboolean
432 gtk_widget_path_iter_has_name (const GtkWidgetPath *path,
433                                gint                 pos,
434                                const gchar         *name)
435 {
436   GQuark qname;
437
438   g_return_val_if_fail (path != NULL, FALSE);
439   g_return_val_if_fail (path->elems->len != 0, FALSE);
440
441   if (pos < 0 || pos >= path->elems->len)
442     pos = path->elems->len - 1;
443
444   qname = g_quark_try_string (name);
445
446   if (qname == 0)
447     return FALSE;
448
449   return gtk_widget_path_iter_has_qname (path, pos, qname);
450 }
451
452 /**
453  * gtk_widget_path_iter_add_class:
454  * @path: a #GtkWidget
455  * @pos: position to modify, -1 for the path head
456  * @name: a class name
457  *
458  * Adds the class @name to the widget at position @pos in
459  * the hierarchy defined in @path. See
460  * gtk_style_context_add_class().
461  *
462  * Since: 3.0
463  **/
464 void
465 gtk_widget_path_iter_add_class (GtkWidgetPath *path,
466                                 gint           pos,
467                                 const gchar   *name)
468 {
469   GtkPathElement *elem;
470   gboolean added = FALSE;
471   GQuark qname;
472   guint i;
473
474   g_return_if_fail (path != NULL);
475   g_return_if_fail (path->elems->len != 0);
476   g_return_if_fail (name != NULL);
477
478   if (pos < 0 || pos >= path->elems->len)
479     pos = path->elems->len - 1;
480
481   elem = &g_array_index (path->elems, GtkPathElement, pos);
482   qname = g_quark_from_string (name);
483
484   if (!elem->classes)
485     elem->classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
486
487   for (i = 0; i < elem->classes->len; i++)
488     {
489       GQuark quark;
490
491       quark = g_array_index (elem->classes, GQuark, i);
492
493       if (qname == quark)
494         {
495           /* Already there */
496           added = TRUE;
497           break;
498         }
499       if (qname < quark)
500         {
501           g_array_insert_val (elem->classes, i, qname);
502           added = TRUE;
503           break;
504         }
505     }
506
507   if (!added)
508     g_array_append_val (elem->classes, qname);
509 }
510
511 /**
512  * gtk_widget_path_iter_remove_class:
513  * @path: a #GtkWidgetPath
514  * @pos: position to modify, -1 for the path head
515  * @name: class name
516  *
517  * Removes the class @name from the widget at position @pos in
518  * the hierarchy defined in @path.
519  *
520  * Since: 3.0
521  **/
522 void
523 gtk_widget_path_iter_remove_class (GtkWidgetPath *path,
524                                    gint           pos,
525                                    const gchar   *name)
526 {
527   GtkPathElement *elem;
528   GQuark qname;
529   guint i;
530
531   g_return_if_fail (path != NULL);
532   g_return_if_fail (path->elems->len != 0);
533   g_return_if_fail (name != NULL);
534
535   if (pos < 0 || pos >= path->elems->len)
536     pos = path->elems->len - 1;
537
538   qname = g_quark_try_string (name);
539
540   if (qname == 0)
541     return;
542
543   elem = &g_array_index (path->elems, GtkPathElement, pos);
544
545   if (!elem->classes)
546     return;
547
548   for (i = 0; i < elem->classes->len; i++)
549     {
550       GQuark quark;
551
552       quark = g_array_index (elem->classes, GQuark, i);
553
554       if (quark > qname)
555         break;
556       else if (quark == qname)
557         {
558           g_array_remove_index (elem->classes, i);
559           break;
560         }
561     }
562 }
563
564 /**
565  * gtk_widget_path_iter_clear_classes:
566  * @path: a #GtkWidget
567  * @pos: position to modify, -1 for the path head
568  *
569  * Removes all classes from the widget at position @pos in the
570  * hierarchy defined in @path.
571  *
572  * Since: 3.0
573  **/
574 void
575 gtk_widget_path_iter_clear_classes (GtkWidgetPath *path,
576                                     gint           pos)
577 {
578   GtkPathElement *elem;
579
580   g_return_if_fail (path != NULL);
581   g_return_if_fail (path->elems->len != 0);
582
583   if (pos < 0 || pos >= path->elems->len)
584     pos = path->elems->len - 1;
585
586   elem = &g_array_index (path->elems, GtkPathElement, pos);
587
588   if (!elem->classes)
589     return;
590
591   if (elem->classes->len > 0)
592     g_array_remove_range (elem->classes, 0, elem->classes->len);
593 }
594
595 /**
596  * gtk_widget_path_iter_list_classes:
597  * @path: a #GtkWidgetPath
598  * @pos: position to query, -1 for the path head
599  *
600  * Returns a list with all the class names defined for the widget
601  * at position @pos in the hierarchy defined in @path.
602  *
603  * Returns: (transfer container) (element-type utf8): The list of
604  *          classes, This is a list of strings, the #GSList contents
605  *          are owned by GTK+, but you should use g_slist_free() to
606  *          free the list itself.
607  *
608  * Since: 3.0
609  **/
610 GSList *
611 gtk_widget_path_iter_list_classes (const GtkWidgetPath *path,
612                                    gint                 pos)
613 {
614   GtkPathElement *elem;
615   GSList *list = NULL;
616   guint i;
617
618   g_return_val_if_fail (path != NULL, NULL);
619   g_return_val_if_fail (path->elems->len != 0, NULL);
620
621   if (pos < 0 || pos >= path->elems->len)
622     pos = path->elems->len - 1;
623
624   elem = &g_array_index (path->elems, GtkPathElement, pos);
625
626   if (!elem->classes)
627     return NULL;
628
629   for (i = 0; i < elem->classes->len; i++)
630     {
631       GQuark quark;
632
633       quark = g_array_index (elem->classes, GQuark, i);
634       list = g_slist_prepend (list, (gchar *) g_quark_to_string (quark));
635     }
636
637   return g_slist_reverse (list);
638 }
639
640 /**
641  * gtk_widget_path_iter_has_qclass:
642  * @path: a #GtkWidgetPath
643  * @pos: position to query, -1 for the path head
644  * @qname: class name as a #GQuark
645  *
646  * See gtk_widget_path_iter_has_class(). This is a version that operates
647  * with GQuark<!-- -->s.
648  *
649  * Returns: %TRUE if the widget at @pos has the class defined.
650  *
651  * Since: 3.0
652  **/
653 gboolean
654 gtk_widget_path_iter_has_qclass (const GtkWidgetPath *path,
655                                  gint                 pos,
656                                  GQuark               qname)
657 {
658   GtkPathElement *elem;
659   guint i;
660
661   g_return_val_if_fail (path != NULL, FALSE);
662   g_return_val_if_fail (path->elems->len != 0, FALSE);
663   g_return_val_if_fail (qname != 0, FALSE);
664
665   if (pos < 0 || pos >= path->elems->len)
666     pos = path->elems->len - 1;
667
668   elem = &g_array_index (path->elems, GtkPathElement, pos);
669
670   if (!elem->classes)
671     return FALSE;
672
673   for (i = 0; i < elem->classes->len; i++)
674     {
675       GQuark quark;
676
677       quark = g_array_index (elem->classes, GQuark, i);
678
679       if (quark == qname)
680         return TRUE;
681       else if (quark > qname)
682         break;
683     }
684
685   return FALSE;
686 }
687
688 /**
689  * gtk_widget_path_iter_has_class:
690  * @path: a #GtkWidgetPath
691  * @pos: position to query, -1 for the path head
692  * @name: class name
693  *
694  * Returns %TRUE if the widget at position @pos has the class @name
695  * defined, %FALSE otherwise.
696  *
697  * Returns: %TRUE if the class @name is defined for the widget at @pos
698  *
699  * Since: 3.0
700  **/
701 gboolean
702 gtk_widget_path_iter_has_class (const GtkWidgetPath *path,
703                                 gint                 pos,
704                                 const gchar         *name)
705 {
706   GQuark qname;
707
708   g_return_val_if_fail (path != NULL, FALSE);
709   g_return_val_if_fail (path->elems->len != 0, FALSE);
710   g_return_val_if_fail (name != NULL, FALSE);
711
712   if (pos < 0 || pos >= path->elems->len)
713     pos = path->elems->len - 1;
714
715   qname = g_quark_try_string (name);
716
717   if (qname == 0)
718     return FALSE;
719
720   return gtk_widget_path_iter_has_qclass (path, pos, qname);
721 }
722
723 /**
724  * gtk_widget_path_iter_add_region:
725  * @path: a #GtkWidgetPath
726  * @pos: position to modify, -1 for the path head
727  * @name: region name
728  * @flags: flags affecting the region
729  *
730  * Adds the region @name to the widget at position @pos in
731  * the hierarchy defined in @path. See
732  * gtk_style_context_add_region().
733  *
734  * <note><para>Region names must only contain lowercase letters
735  * and '-', starting always with a lowercase letter.</para></note>
736  *
737  * Since: 3.0
738  **/
739 void
740 gtk_widget_path_iter_add_region (GtkWidgetPath  *path,
741                                  gint            pos,
742                                  const gchar    *name,
743                                  GtkRegionFlags  flags)
744 {
745   GtkPathElement *elem;
746   GQuark qname;
747
748   g_return_if_fail (path != NULL);
749   g_return_if_fail (path->elems->len != 0);
750   g_return_if_fail (name != NULL);
751   g_return_if_fail (_gtk_style_context_check_region_name (name));
752
753   if (pos < 0 || pos >= path->elems->len)
754     pos = path->elems->len - 1;
755
756   elem = &g_array_index (path->elems, GtkPathElement, pos);
757   qname = g_quark_from_string (name);
758
759   if (!elem->regions)
760     elem->regions = g_hash_table_new (NULL, NULL);
761
762   g_hash_table_insert (elem->regions,
763                        GUINT_TO_POINTER (qname),
764                        GUINT_TO_POINTER (flags));
765 }
766
767 /**
768  * gtk_widget_path_iter_remove_region:
769  * @path: a #GtkWidgetPath
770  * @pos: position to modify, -1 for the path head
771  * @name: region name
772  *
773  * Removes the region @name from the widget at position @pos in
774  * the hierarchy defined in @path.
775  *
776  * Since: 3.0
777  **/
778 void
779 gtk_widget_path_iter_remove_region (GtkWidgetPath *path,
780                                     gint           pos,
781                                     const gchar   *name)
782 {
783   GtkPathElement *elem;
784   GQuark qname;
785
786   g_return_if_fail (path != NULL);
787   g_return_if_fail (path->elems->len != 0);
788   g_return_if_fail (name != NULL);
789
790   if (pos < 0 || pos >= path->elems->len)
791     pos = path->elems->len - 1;
792
793   qname = g_quark_try_string (name);
794
795   if (qname == 0)
796     return;
797
798   elem = &g_array_index (path->elems, GtkPathElement, pos);
799
800   if (elem->regions)
801     g_hash_table_remove (elem->regions, GUINT_TO_POINTER (qname));
802 }
803
804 /**
805  * gtk_widget_path_iter_clear_regions:
806  * @path: a #GtkWidgetPath
807  * @pos: position to modify, -1 for the path head
808  *
809  * Removes all regions from the widget at position @pos in the
810  * hierarchy defined in @path.
811  *
812  * Since: 3.0
813  **/
814 void
815 gtk_widget_path_iter_clear_regions (GtkWidgetPath *path,
816                                     gint           pos)
817 {
818   GtkPathElement *elem;
819
820   g_return_if_fail (path != NULL);
821   g_return_if_fail (path->elems->len != 0);
822
823   if (pos < 0 || pos >= path->elems->len)
824     pos = path->elems->len - 1;
825
826   elem = &g_array_index (path->elems, GtkPathElement, pos);
827
828   if (elem->regions)
829     g_hash_table_remove_all (elem->regions);
830 }
831
832 /**
833  * gtk_widget_path_iter_list_regions:
834  * @path: a #GtkWidgetPath
835  * @pos: position to query, -1 for the path head
836  *
837  * Returns a list with all the region names defined for the widget
838  * at position @pos in the hierarchy defined in @path.
839  *
840  * Returns: (transfer container) (element-type utf8): The list of
841  *          regions, This is a list of strings, the #GSList contents
842  *          are owned by GTK+, but you should use g_slist_free() to
843  *          free the list itself.
844  *
845  * Since: 3.0
846  **/
847 GSList *
848 gtk_widget_path_iter_list_regions (const GtkWidgetPath *path,
849                                    gint                 pos)
850 {
851   GtkPathElement *elem;
852   GHashTableIter iter;
853   GSList *list = NULL;
854   gpointer key;
855
856   g_return_val_if_fail (path != NULL, NULL);
857   g_return_val_if_fail (path->elems->len != 0, NULL);
858
859   if (pos < 0 || pos >= path->elems->len)
860     pos = path->elems->len - 1;
861
862   elem = &g_array_index (path->elems, GtkPathElement, pos);
863
864   if (!elem->regions)
865     return NULL;
866
867   g_hash_table_iter_init (&iter, elem->regions);
868
869   while (g_hash_table_iter_next (&iter, &key, NULL))
870     {
871       GQuark qname;
872
873       qname = GPOINTER_TO_UINT (key);
874       list = g_slist_prepend (list, (gchar *) g_quark_to_string (qname));
875     }
876
877   return list;
878 }
879
880 /**
881  * gtk_widget_path_iter_has_qregion:
882  * @path: a #GtkWidgetPath
883  * @pos: position to query, -1 for the path head
884  * @qname: region name as a #GQuark
885  * @flags: (out): return location for the region flags
886  *
887  * See gtk_widget_path_iter_has_region(). This is a version that operates
888  * with GQuark<!-- -->s.
889  *
890  * Returns: %TRUE if the widget at @pos has the region defined.
891  *
892  * Since: 3.0
893  **/
894 gboolean
895 gtk_widget_path_iter_has_qregion (const GtkWidgetPath *path,
896                                   gint                 pos,
897                                   GQuark               qname,
898                                   GtkRegionFlags      *flags)
899 {
900   GtkPathElement *elem;
901   gpointer value;
902
903   g_return_val_if_fail (path != NULL, FALSE);
904   g_return_val_if_fail (path->elems->len != 0, FALSE);
905   g_return_val_if_fail (qname != 0, FALSE);
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     return FALSE;
914
915   if (!g_hash_table_lookup_extended (elem->regions,
916                                      GUINT_TO_POINTER (qname),
917                                      NULL, &value))
918     return FALSE;
919
920   if (flags)
921     *flags = GPOINTER_TO_UINT (value);
922
923   return TRUE;
924 }
925
926 /**
927  * gtk_widget_path_iter_has_region:
928  * @path: a #GtkWidgetPath
929  * @pos: position to query, -1 for the path head
930  * @name: region name
931  * @flags: (out): return location for the region flags
932  *
933  * Returns %TRUE if the widget at position @pos has the class @name
934  * defined, %FALSE otherwise.
935  *
936  * Returns: %TRUE if the class @name is defined for the widget at @pos
937  *
938  * Since: 3.0
939  **/
940 gboolean
941 gtk_widget_path_iter_has_region (const GtkWidgetPath *path,
942                                  gint                 pos,
943                                  const gchar         *name,
944                                  GtkRegionFlags      *flags)
945 {
946   GQuark qname;
947
948   g_return_val_if_fail (path != NULL, FALSE);
949   g_return_val_if_fail (path->elems->len != 0, FALSE);
950   g_return_val_if_fail (name != NULL, FALSE);
951
952   if (pos < 0 || pos >= path->elems->len)
953     pos = path->elems->len - 1;
954
955   qname = g_quark_try_string (name);
956
957   if (qname == 0)
958     return FALSE;
959
960   return gtk_widget_path_iter_has_qregion (path, pos, qname, flags);
961 }
962
963 /**
964  * gtk_widget_path_get_object_type:
965  * @path: a #GtkWidget
966  *
967  * Returns the topmost object type, that is, the object type this path
968  * is representing.
969  *
970  * Returns: The object type
971  *
972  * Since: 3.0
973  **/
974 GType
975 gtk_widget_path_get_object_type (const GtkWidgetPath *path)
976 {
977   GtkPathElement *elem;
978
979   g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
980
981   elem = &g_array_index (path->elems, GtkPathElement,
982                          path->elems->len - 1);
983   return elem->type;
984 }
985
986 /**
987  * gtk_widget_path_is_type:
988  * @path: a #GtkWidgetPath
989  * @type: widget type to match
990  *
991  * Returns %TRUE if the widget type represented by this path
992  * is @type, or a subtype of it.
993  *
994  * Returns: %TRUE if the widget represented by @path is of type @type
995  *
996  * Since: 3.0
997  **/
998 gboolean
999 gtk_widget_path_is_type (const GtkWidgetPath *path,
1000                          GType                type)
1001 {
1002   GtkPathElement *elem;
1003
1004   g_return_val_if_fail (path != NULL, FALSE);
1005
1006   elem = &g_array_index (path->elems, GtkPathElement,
1007                          path->elems->len - 1);
1008
1009   if (elem->type == type ||
1010       g_type_is_a (elem->type, type))
1011     return TRUE;
1012
1013   return FALSE;
1014 }
1015
1016 /**
1017  * gtk_widget_path_has_parent:
1018  * @path: a #GtkWidgetPath
1019  * @type: widget type to check in parents
1020  *
1021  * Returns %TRUE if any of the parents of the widget represented
1022  * in @path is of type @type, or any subtype of it.
1023  *
1024  * Returns: %TRUE if any parent is of type @type
1025  *
1026  * Since: 3.0
1027  **/
1028 gboolean
1029 gtk_widget_path_has_parent (const GtkWidgetPath *path,
1030                             GType                type)
1031 {
1032   guint i;
1033
1034   g_return_val_if_fail (path != NULL, FALSE);
1035
1036   for (i = 0; i < path->elems->len - 1; i++)
1037     {
1038       GtkPathElement *elem;
1039
1040       elem = &g_array_index (path->elems, GtkPathElement, i);
1041
1042       if (elem->type == type ||
1043           g_type_is_a (elem->type, type))
1044         return TRUE;
1045     }
1046
1047   return FALSE;
1048 }