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