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