1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
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.
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.
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.
24 #include "gtkwidget.h"
25 #include "gtkwidgetpath.h"
28 * SECTION:gtkwidgetpath
29 * @Short_description: Widget path abstraction
30 * @Title: GtkWidgetPath
31 * @See_also: #GtkStyleContext
33 * #GtkWidgetPath is a boxed type that represents a widget hierarchy from
34 * the topmost widget, typically a toplevel, to any child. This widget
35 * path abstraction is used in #GtkStyleContext on behalf of the real
36 * widget in order to query style information.
38 * If you are using GTK+ widgets, there are many chances you don't
39 * need this API directly, as there is gtk_widget_get_path(), and the
40 * style context returned by gtk_widget_get_style_context() will be
41 * automatically updated on widget hierarchy changes.
43 * The widget path generation is generally simple:
45 * <title>Defining a button within a window</title>
48 * GtkWidgetPath *path;
50 * path = gtk_widget_path_new ();
51 * gtk_widget_path_append_type (path, GTK_TYPE_WINDOW);
52 * gtk_widget_path_append_type (path, GTK_TYPE_BUTTON);
57 * Although more complex information, such as widget names, or
58 * different classes (property that may be used by other widget
59 * types) and intermediate regions may be included:
62 * <title>Defining the first tab widget in a notebook</title>
65 * GtkWidgetPath *path;
68 * path = gtk_widget_path_new ();
70 * pos = gtk_widget_path_append_type (path, GTK_TYPE_NOTEBOOK);
71 * gtk_widget_path_iter_add_region (path, pos, "tab", GTK_REGION_EVEN | GTK_REGION_FIRST);
73 * pos = gtk_widget_path_append_type (path, GTK_TYPE_LABEL);
74 * gtk_widget_path_iter_set_name (path, pos, "first tab label");
79 * All this information will be used to match the style information
80 * that applies to the described widget.
83 G_DEFINE_BOXED_TYPE (GtkWidgetPath, gtk_widget_path,
84 gtk_widget_path_copy, gtk_widget_path_free)
87 typedef struct GtkPathElement GtkPathElement;
99 GArray *elems; /* First element contains the described widget */
103 * gtk_widget_path_new:
105 * Returns an empty widget path.
107 * Returns: (transfer full): A newly created, empty, #GtkWidgetPath
112 gtk_widget_path_new (void)
116 path = g_slice_new0 (GtkWidgetPath);
117 path->elems = g_array_new (FALSE, TRUE, sizeof (GtkPathElement));
123 * gtk_widget_path_copy:
124 * @path: a #GtkWidgetPath
126 * Returns a copy of @path
128 * Returns: (transfer full): a copy of @path
133 gtk_widget_path_copy (const GtkWidgetPath *path)
135 GtkWidgetPath *new_path;
138 g_return_val_if_fail (path != NULL, NULL);
140 new_path = gtk_widget_path_new ();
142 for (i = 0; i < path->elems->len; i++)
144 GtkPathElement *elem, new = { 0 };
146 elem = &g_array_index (path->elems, GtkPathElement, i);
148 new.type = elem->type;
149 new.name = elem->name;
156 g_hash_table_iter_init (&iter, elem->regions);
157 new.regions = g_hash_table_new (NULL, NULL);
159 while (g_hash_table_iter_next (&iter, &key, &value))
160 g_hash_table_insert (new.regions, key, value);
163 g_array_append_val (new_path->elems, new);
170 * gtk_widget_path_free:
171 * @path: a #GtkWidgetPath
173 * Frees a #GtkWidgetPath.
178 gtk_widget_path_free (GtkWidgetPath *path)
182 g_return_if_fail (path != NULL);
184 for (i = 0; i < path->elems->len; i++)
186 GtkPathElement *elem;
188 elem = &g_array_index (path->elems, GtkPathElement, i);
191 g_hash_table_destroy (elem->regions);
194 g_array_free (elem->classes, TRUE);
197 g_array_free (path->elems, TRUE);
198 g_slice_free (GtkWidgetPath, path);
202 * gtk_widget_path_length:
203 * @path: a #GtkWidgetPath
205 * Returns the number of #GtkWidget #GTypes between the represented
206 * widget and its topmost container.
208 * Returns: the number of elements in the path
213 gtk_widget_path_length (const GtkWidgetPath *path)
215 g_return_val_if_fail (path != NULL, 0);
217 return path->elems->len;
221 * gtk_widget_path_prepend_type:
222 * @path: a #GtkWidgetPath
223 * @type: widget type to prepend
225 * Prepends a widget type to the widget hierachy represented by @path.
227 * Returns: the position where the element was inserted
232 gtk_widget_path_prepend_type (GtkWidgetPath *path,
235 GtkPathElement new = { 0 };
237 g_return_val_if_fail (path != NULL, 0);
238 g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), 0);
241 g_array_prepend_val (path->elems, new);
247 * gtk_widget_path_append_type:
248 * @path: a #GtkWidgetPath
249 * @type: widget type to append
251 * Appends a widget type to the widget hierachy represented by @path.
253 * Returns: the position where the element was inserted
258 gtk_widget_path_append_type (GtkWidgetPath *path,
261 GtkPathElement new = { 0 };
263 g_return_val_if_fail (path != NULL, 0);
264 g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), 0);
267 g_array_append_val (path->elems, new);
269 return path->elems->len - 1;
273 * gtk_widget_path_iter_get_widget_type:
274 * @path: a #GtkWidgetPath
275 * @pos: position to get the widget type for
277 * Returns the widget #GType that is at position @pos in the widget
278 * hierarchy defined in @path.
280 * Returns: a widget type
285 gtk_widget_path_iter_get_widget_type (const GtkWidgetPath *path,
288 GtkPathElement *elem;
290 g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
291 g_return_val_if_fail (pos < path->elems->len, G_TYPE_INVALID);
293 elem = &g_array_index (path->elems, GtkPathElement, pos);
298 * gtk_widget_path_iter_set_widget_type:
299 * @path: a #GtkWidgetPath
300 * @pos: position to modify
301 * @type: widget type to set
303 * Sets the widget type for a given position in the widget hierarchy
304 * defined by @path. @type must be a #GtkWidget derived #GType.
309 gtk_widget_path_iter_set_widget_type (GtkWidgetPath *path,
313 GtkPathElement *elem;
315 g_return_if_fail (path != NULL);
316 g_return_if_fail (pos < path->elems->len);
317 g_return_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET));
319 elem = &g_array_index (path->elems, GtkPathElement, pos);
324 * gtk_widget_path_iter_get_name:
325 * @path: a #GtkWidgetPath
326 * @pos: position to get the widget name for
328 * Returns the name corresponding to the widget found at
329 * the position @pos in the widget hierarchy defined by
332 * Returns: The widget name, or %NULL if none was set.
334 G_CONST_RETURN gchar *
335 gtk_widget_path_iter_get_name (const GtkWidgetPath *path,
338 GtkPathElement *elem;
340 g_return_val_if_fail (path != NULL, NULL);
341 g_return_val_if_fail (pos < path->elems->len, NULL);
343 elem = &g_array_index (path->elems, GtkPathElement, pos);
344 return g_quark_to_string (elem->name);
348 * gtk_widget_path_iter_set_name:
349 * @path: a #GtkWidgetPath
350 * @pos: position to modify
353 * Sets the widget name for the widget found at position @pos
354 * in the widget hierarchy defined by @path.
359 gtk_widget_path_iter_set_name (GtkWidgetPath *path,
363 GtkPathElement *elem;
365 g_return_if_fail (path != NULL);
366 g_return_if_fail (pos < path->elems->len);
367 g_return_if_fail (name != NULL);
369 elem = &g_array_index (path->elems, GtkPathElement, pos);
371 elem->name = g_quark_from_string (name);
375 * gtk_widget_path_iter_has_qname:
376 * @path: a #GtkWidgetPath
377 * @pos: position to query
378 * @qname: widget name as a #GQuark
380 * See gtk_widget_path_iter_has_name(). This is a version
381 * that operates on #GQuark<!-- -->s.
383 * Returns: %TRUE if the widget at @pos has this name
388 gtk_widget_path_iter_has_qname (const GtkWidgetPath *path,
392 GtkPathElement *elem;
394 g_return_val_if_fail (path != NULL, FALSE);
395 g_return_val_if_fail (qname != 0, FALSE);
396 g_return_val_if_fail (pos < path->elems->len, FALSE);
398 elem = &g_array_index (path->elems, GtkPathElement, pos);
400 return (elem->name == qname);
404 * gtk_widget_path_iter_has_name:
405 * @path: a #GtkWidgetPath
406 * @pos: position to query
407 * @name: a widget name
409 * Returns %TRUE if the widget at position @pos has the name @name,
412 * Returns: %TRUE if the widget at @pos has this name
417 gtk_widget_path_iter_has_name (const GtkWidgetPath *path,
423 g_return_val_if_fail (path != NULL, FALSE);
424 g_return_val_if_fail (name != NULL, FALSE);
425 g_return_val_if_fail (pos < path->elems->len, FALSE);
427 qname = g_quark_try_string (name);
432 return gtk_widget_path_iter_has_qname (path, pos, qname);
436 * gtk_widget_path_iter_add_class:
437 * @path: a #GtkWidget
438 * @pos: position to modify
439 * @name: a class name
441 * Adds the class @name to the widget at position @pos in
442 * the hierarchy defined in @path. See
443 * gtk_style_context_add_class().
448 gtk_widget_path_iter_add_class (GtkWidgetPath *path,
452 GtkPathElement *elem;
453 gboolean added = FALSE;
457 g_return_if_fail (path != NULL);
458 g_return_if_fail (pos < path->elems->len);
459 g_return_if_fail (name != NULL);
461 elem = &g_array_index (path->elems, GtkPathElement, pos);
462 qname = g_quark_from_string (name);
465 elem->classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
467 for (i = 0; i < elem->classes->len; i++)
471 quark = g_array_index (elem->classes, GQuark, i);
481 g_array_insert_val (elem->classes, i, qname);
488 g_array_append_val (elem->classes, qname);
492 * gtk_widget_path_iter_remove_class:
493 * @path: a #GtkWidgetPath
494 * @pos: position to modify
497 * Removes the class @name from the widget at position @pos in
498 * the hierarchy defined in @path.
503 gtk_widget_path_iter_remove_class (GtkWidgetPath *path,
507 GtkPathElement *elem;
511 g_return_if_fail (path != NULL);
512 g_return_if_fail (pos < path->elems->len);
513 g_return_if_fail (name != NULL);
515 qname = g_quark_try_string (name);
520 elem = &g_array_index (path->elems, GtkPathElement, pos);
525 for (i = 0; i < elem->classes->len; i++)
529 quark = g_array_index (elem->classes, GQuark, i);
533 else if (quark == qname)
535 g_array_remove_index (elem->classes, i);
542 * gtk_widget_path_iter_clear_classes:
543 * @path: a #GtkWidget
544 * @pos: position to modify
546 * Removes all classes from the widget at position @pos in the
547 * hierarchy defined in @path.
552 gtk_widget_path_iter_clear_classes (GtkWidgetPath *path,
555 GtkPathElement *elem;
557 g_return_if_fail (path != NULL);
558 g_return_if_fail (pos < path->elems->len);
560 elem = &g_array_index (path->elems, GtkPathElement, pos);
565 if (elem->classes->len > 0)
566 g_array_remove_range (elem->classes, 0, elem->classes->len);
570 * gtk_widget_path_iter_list_classes:
571 * @path: a #GtkWidgetPath
572 * @pos: position to query
574 * Returns a list with all the class names defined for the widget
575 * at position @pos in the hierarchy defined in @path.
577 * Returns: (transfer container) (type utf8): The list of classes,
578 * This is a list of strings, the #GSList contents are
579 * owned by GTK+, but you should use g_slist_free() to
580 * free the list itself.
585 gtk_widget_path_iter_list_classes (const GtkWidgetPath *path,
588 GtkPathElement *elem;
592 g_return_val_if_fail (path != NULL, NULL);
593 g_return_val_if_fail (pos < path->elems->len, NULL);
595 elem = &g_array_index (path->elems, GtkPathElement, pos);
600 for (i = 0; i < elem->classes->len; i++)
604 quark = g_array_index (elem->classes, GQuark, i);
605 list = g_slist_prepend (list, (gchar *) g_quark_to_string (quark));
608 return g_slist_reverse (list);
612 * gtk_widget_path_iter_has_qclass:
613 * @path: a #GtkWidgetPath
614 * @pos: position to query
615 * @qname: class name as a #GQuark
617 * See gtk_widget_path_iter_has_class(). This is a version that operates
618 * with GQuark<!-- -->s.
620 * Returns: %TRUE if the widget at @pos has the class defined.
625 gtk_widget_path_iter_has_qclass (const GtkWidgetPath *path,
629 GtkPathElement *elem;
632 g_return_val_if_fail (path != NULL, FALSE);
633 g_return_val_if_fail (pos < path->elems->len, FALSE);
634 g_return_val_if_fail (qname != 0, FALSE);
636 elem = &g_array_index (path->elems, GtkPathElement, pos);
641 for (i = 0; i < elem->classes->len; i++)
645 quark = g_array_index (elem->classes, GQuark, i);
649 else if (quark > qname)
657 * gtk_widget_path_iter_has_class:
658 * @path: a #GtkWidgetPath
659 * @pos: position to query
662 * Returns %TRUE if the widget at position @pos has the class @name
663 * defined, %FALSE otherwise.
665 * Returns: %TRUE if the class @name is defined for the widget at @pos
670 gtk_widget_path_iter_has_class (const GtkWidgetPath *path,
676 g_return_val_if_fail (path != NULL, FALSE);
677 g_return_val_if_fail (pos < path->elems->len, FALSE);
678 g_return_val_if_fail (name != NULL, FALSE);
680 qname = g_quark_try_string (name);
685 return gtk_widget_path_iter_has_qclass (path, pos, qname);
689 * gtk_widget_path_iter_add_region:
690 * @path: a #GtkWidgetPath
691 * @pos: position to modify
693 * @flags: flags affecting the region
695 * Adds the region @name to the widget at position @pos in
696 * the hierarchy defined in @path. See
697 * gtk_style_context_add_region().
702 gtk_widget_path_iter_add_region (GtkWidgetPath *path,
705 GtkRegionFlags flags)
707 GtkPathElement *elem;
710 g_return_if_fail (path != NULL);
711 g_return_if_fail (pos < path->elems->len);
712 g_return_if_fail (name != NULL);
714 elem = &g_array_index (path->elems, GtkPathElement, pos);
715 qname = g_quark_from_string (name);
718 elem->regions = g_hash_table_new (NULL, NULL);
720 g_hash_table_insert (elem->regions,
721 GUINT_TO_POINTER (qname),
722 GUINT_TO_POINTER (flags));
726 * gtk_widget_path_iter_remove_region:
727 * @path: a #GtkWidgetPath
728 * @pos: position to modify
731 * Removes the region @name from the widget at position @pos in
732 * the hierarchy defined in @path.
737 gtk_widget_path_iter_remove_region (GtkWidgetPath *path,
741 GtkPathElement *elem;
744 g_return_if_fail (path != NULL);
745 g_return_if_fail (pos < path->elems->len);
746 g_return_if_fail (name != NULL);
748 qname = g_quark_try_string (name);
753 elem = &g_array_index (path->elems, GtkPathElement, pos);
756 g_hash_table_remove (elem->regions, GUINT_TO_POINTER (qname));
760 * gtk_widget_path_iter_clear_regions:
761 * @path: a #GtkWidgetPath
762 * @pos: position to modify
764 * Removes all regions from the widget at position @pos in the
765 * hierarchy defined in @path.
770 gtk_widget_path_iter_clear_regions (GtkWidgetPath *path,
773 GtkPathElement *elem;
775 g_return_if_fail (path != NULL);
776 g_return_if_fail (pos < path->elems->len);
778 elem = &g_array_index (path->elems, GtkPathElement, pos);
781 g_hash_table_remove_all (elem->regions);
785 * gtk_widget_path_iter_list_regions:
786 * @path: a #GtkWidgetPath
787 * @pos: position to query
789 * Returns a list with all the region names defined for the widget
790 * at position @pos in the hierarchy defined in @path.
792 * Returns: (transfer container) (type utf8): The list of regions,
793 * This is a list of strings, the #GSList contents are
794 * owned by GTK+, but you should use g_slist_free() to
795 * free the list itself.
800 gtk_widget_path_iter_list_regions (const GtkWidgetPath *path,
803 GtkPathElement *elem;
808 g_return_val_if_fail (path != NULL, NULL);
809 g_return_val_if_fail (pos < path->elems->len, NULL);
811 elem = &g_array_index (path->elems, GtkPathElement, pos);
816 g_hash_table_iter_init (&iter, elem->regions);
818 while (g_hash_table_iter_next (&iter, &key, NULL))
822 qname = GPOINTER_TO_UINT (key);
823 list = g_slist_prepend (list, (gchar *) g_quark_to_string (qname));
830 * gtk_widget_path_iter_has_qregion:
831 * @path: a #GtkWidgetPath
832 * @pos: position to query
833 * @qname: region name as a #GQuark
834 * @flags: (out): return location for the region flags
836 * See gtk_widget_path_iter_has_region(). This is a version that operates
837 * with GQuark<!-- -->s.
839 * Returns: %TRUE if the widget at @pos has the region defined.
844 gtk_widget_path_iter_has_qregion (const GtkWidgetPath *path,
847 GtkRegionFlags *flags)
849 GtkPathElement *elem;
852 g_return_val_if_fail (path != NULL, FALSE);
853 g_return_val_if_fail (pos < path->elems->len, FALSE);
854 g_return_val_if_fail (qname != 0, FALSE);
856 elem = &g_array_index (path->elems, GtkPathElement, pos);
861 if (!g_hash_table_lookup_extended (elem->regions,
862 GUINT_TO_POINTER (qname),
867 *flags = GPOINTER_TO_UINT (value);
873 * gtk_widget_path_iter_has_region:
874 * @path: a #GtkWidgetPath
875 * @pos: position to query
877 * @flags: (out): return location for the region flags
879 * Returns %TRUE if the widget at position @pos has the class @name
880 * defined, %FALSE otherwise.
882 * Returns: %TRUE if the class @name is defined for the widget at @pos
887 gtk_widget_path_iter_has_region (const GtkWidgetPath *path,
890 GtkRegionFlags *flags)
894 g_return_val_if_fail (path != NULL, FALSE);
895 g_return_val_if_fail (pos < path->elems->len, FALSE);
896 g_return_val_if_fail (name != NULL, FALSE);
898 qname = g_quark_try_string (name);
903 return gtk_widget_path_iter_has_qregion (path, pos, qname, flags);
907 * gtk_widget_path_get_widget_type:
908 * @path: a #GtkWidget
910 * Returns the topmost widget type, that is, the widget type this path
913 * Returns: The widget type
918 gtk_widget_path_get_widget_type (const GtkWidgetPath *path)
920 GtkPathElement *elem;
922 g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
924 elem = &g_array_index (path->elems, GtkPathElement,
925 path->elems->len - 1);
930 * gtk_widget_path_is_type:
931 * @path: a #GtkWidgetPath
932 * @type: widget type to match
934 * Returns %TRUE if the widget type represented by this path
935 * is @type, or a subtype of it.
937 * Returns: %TRUE if the widget represented by @path is of type @type
942 gtk_widget_path_is_type (const GtkWidgetPath *path,
945 GtkPathElement *elem;
947 g_return_val_if_fail (path != NULL, FALSE);
948 g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), FALSE);
950 elem = &g_array_index (path->elems, GtkPathElement,
951 path->elems->len - 1);
953 if (elem->type == type ||
954 g_type_is_a (elem->type, type))
961 * gtk_widget_path_has_parent:
962 * @path: a #GtkWidgetPath
963 * @type: widget type to check in parents
965 * Returns %TRUE if any of the parents of the widget represented
966 * in @path is of type @type, or any subtype of it.
968 * Returns: %TRUE if any parent is of type @type
973 gtk_widget_path_has_parent (const GtkWidgetPath *path,
978 g_return_val_if_fail (path != NULL, FALSE);
979 g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), FALSE);
981 for (i = 0; i < path->elems->len - 1; i++)
983 GtkPathElement *elem;
985 elem = &g_array_index (path->elems, GtkPathElement, i);
987 if (elem->type == type ||
988 g_type_is_a (elem->type, type))