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 an struct 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 typedef struct GtkPathElement GtkPathElement;
95 GArray *elems; /* First element contains the described widget */
99 * gtk_widget_path_new:
101 * Returns an empty widget path.
103 * Returns: (transfer full): A newly created, empty, #GtkWidgetPath
108 gtk_widget_path_new (void)
112 path = g_slice_new0 (GtkWidgetPath);
113 path->elems = g_array_new (FALSE, TRUE, sizeof (GtkPathElement));
119 * gtk_widget_path_copy:
120 * @path: a #GtkWidgetPath
122 * Returns a copy of @path
124 * Returns: (transfer full): a copy of @path
129 gtk_widget_path_copy (const GtkWidgetPath *path)
131 GtkWidgetPath *new_path;
134 g_return_val_if_fail (path != NULL, NULL);
136 new_path = gtk_widget_path_new ();
138 for (i = 0; i < path->elems->len; i++)
140 GtkPathElement *elem, new = { 0 };
142 elem = &g_array_index (path->elems, GtkPathElement, i);
144 new.type = elem->type;
145 new.name = elem->name;
152 g_hash_table_iter_init (&iter, elem->regions);
153 new.regions = g_hash_table_new (NULL, NULL);
155 while (g_hash_table_iter_next (&iter, &key, &value))
156 g_hash_table_insert (new.regions, key, value);
159 g_array_append_val (new_path->elems, new);
166 * gtk_widget_path_free:
167 * @path: a #GtkWidgetPath
169 * Frees a #GtkWidgetPath.
174 gtk_widget_path_free (GtkWidgetPath *path)
178 g_return_if_fail (path != NULL);
180 for (i = 0; i < path->elems->len; i++)
182 GtkPathElement *elem;
184 elem = &g_array_index (path->elems, GtkPathElement, i);
187 g_hash_table_destroy (elem->regions);
190 g_array_free (elem->classes, TRUE);
193 g_array_free (path->elems, TRUE);
194 g_slice_free (GtkWidgetPath, path);
198 * gtk_widget_path_length:
199 * @path: a #GtkWidgetPath
201 * Returns the number of #GtkWidget #GTypes between the represented
202 * widget and its topmost container.
204 * Returns: the number of elements in the path
209 gtk_widget_path_length (const GtkWidgetPath *path)
211 g_return_val_if_fail (path != NULL, 0);
213 return path->elems->len;
217 * gtk_widget_path_prepend_type:
218 * @path: a #GtkWidgetPath
219 * @type: widget type to prepend
221 * Prepends a widget type to the widget hierachy represented by @path.
223 * Returns: the position where the element was inserted
228 gtk_widget_path_prepend_type (GtkWidgetPath *path,
231 GtkPathElement new = { 0 };
233 g_return_val_if_fail (path != NULL, 0);
234 g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), 0);
237 g_array_prepend_val (path->elems, new);
243 * gtk_widget_path_append_type:
244 * @path: a #GtkWidgetPath
245 * @type: widget type to append
247 * Appends a widget type to the widget hierachy represented by @path.
249 * Returns: the position where the element was inserted
254 gtk_widget_path_append_type (GtkWidgetPath *path,
257 GtkPathElement new = { 0 };
259 g_return_val_if_fail (path != NULL, 0);
260 g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), 0);
263 g_array_append_val (path->elems, new);
265 return path->elems->len - 1;
269 * gtk_widget_path_iter_get_widget_type:
270 * @path: a #GtkWidgetPath
271 * @pos: position to get the widget type for
273 * Returns the widget #GType that is at position @pos in the widget
274 * hierarchy defined in @path.
276 * Returns: a widget type
281 gtk_widget_path_iter_get_widget_type (const GtkWidgetPath *path,
284 GtkPathElement *elem;
286 g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
287 g_return_val_if_fail (pos < path->elems->len, G_TYPE_INVALID);
289 elem = &g_array_index (path->elems, GtkPathElement, pos);
294 * gtk_widget_path_iter_set_widget_type:
295 * @path: a #GtkWidgetPath
296 * @pos: position to modify
297 * @type: widget type to set
299 * Sets the widget type for a given position in the widget hierarchy
300 * defined by @path. @type must be a #GtkWidget derived #GType.
305 gtk_widget_path_iter_set_widget_type (GtkWidgetPath *path,
309 GtkPathElement *elem;
311 g_return_if_fail (path != NULL);
312 g_return_if_fail (pos < path->elems->len);
313 g_return_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET));
315 elem = &g_array_index (path->elems, GtkPathElement, pos);
320 * gtk_widget_path_iter_get_name:
321 * @path: a #GtkWidgetPath
322 * @pos: position to get the widget name for
324 * Returns the name corresponding to the widget found at
325 * the position @pos in the widget hierarchy defined by
328 * Returns: The widget name, or %NULL if none was set.
330 G_CONST_RETURN gchar *
331 gtk_widget_path_iter_get_name (const GtkWidgetPath *path,
334 GtkPathElement *elem;
336 g_return_val_if_fail (path != NULL, NULL);
337 g_return_val_if_fail (pos < path->elems->len, NULL);
339 elem = &g_array_index (path->elems, GtkPathElement, pos);
340 return g_quark_to_string (elem->name);
344 * gtk_widget_path_iter_set_name:
345 * @path: a #GtkWidgetPath
346 * @pos: position to modify
349 * Sets the widget name for the widget found at position @pos
350 * in the widget hierarchy defined by @path.
355 gtk_widget_path_iter_set_name (GtkWidgetPath *path,
359 GtkPathElement *elem;
361 g_return_if_fail (path != NULL);
362 g_return_if_fail (pos < path->elems->len);
363 g_return_if_fail (name != NULL);
365 elem = &g_array_index (path->elems, GtkPathElement, pos);
367 elem->name = g_quark_from_string (name);
371 * gtk_widget_path_iter_has_qname:
372 * @path: a #GtkWidgetPath
373 * @pos: position to query
374 * @qname: widget name as a #GQuark
376 * See gtk_widget_path_iter_has_name(). This is a version
377 * that operates on #GQuark<!-- -->s.
379 * Returns: %TRUE if the widget at @pos has this name
384 gtk_widget_path_iter_has_qname (const GtkWidgetPath *path,
388 GtkPathElement *elem;
390 g_return_val_if_fail (path != NULL, FALSE);
391 g_return_val_if_fail (qname != 0, FALSE);
392 g_return_val_if_fail (pos < path->elems->len, FALSE);
394 elem = &g_array_index (path->elems, GtkPathElement, pos);
396 return (elem->name == qname);
400 * gtk_widget_path_iter_has_name:
401 * @path: a #GtkWidgetPath
402 * @pos: position to query
403 * @name: a widget name
405 * Returns %TRUE if the widget at position @pos has the name @name,
408 * Returns: %TRUE if the widget at @pos has this name
413 gtk_widget_path_iter_has_name (const GtkWidgetPath *path,
419 g_return_val_if_fail (path != NULL, FALSE);
420 g_return_val_if_fail (name != NULL, FALSE);
421 g_return_val_if_fail (pos < path->elems->len, FALSE);
423 qname = g_quark_try_string (name);
428 return gtk_widget_path_iter_has_qname (path, pos, qname);
432 * gtk_widget_path_iter_add_class:
433 * @path: a #GtkWidget
434 * @pos: position to modify
435 * @name: a class name
437 * Adds the class @name to the widget at position @pos in
438 * the hierarchy defined in @path. See
439 * gtk_style_context_add_class().
444 gtk_widget_path_iter_add_class (GtkWidgetPath *path,
448 GtkPathElement *elem;
449 gboolean added = FALSE;
453 g_return_if_fail (path != NULL);
454 g_return_if_fail (pos < path->elems->len);
455 g_return_if_fail (name != NULL);
457 elem = &g_array_index (path->elems, GtkPathElement, pos);
458 qname = g_quark_from_string (name);
461 elem->classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
463 for (i = 0; i < elem->classes->len; i++)
467 quark = g_array_index (elem->classes, GQuark, i);
477 g_array_insert_val (elem->classes, i, qname);
484 g_array_append_val (elem->classes, qname);
488 * gtk_widget_path_iter_remove_class:
489 * @path: a #GtkWidgetPath
490 * @pos: position to modify
493 * Removes the class @name from the widget at position @pos in
494 * the hierarchy defined in @path.
499 gtk_widget_path_iter_remove_class (GtkWidgetPath *path,
503 GtkPathElement *elem;
507 g_return_if_fail (path != NULL);
508 g_return_if_fail (pos < path->elems->len);
509 g_return_if_fail (name != NULL);
511 qname = g_quark_try_string (name);
516 elem = &g_array_index (path->elems, GtkPathElement, pos);
521 for (i = 0; i < elem->classes->len; i++)
525 quark = g_array_index (elem->classes, GQuark, i);
529 else if (quark == qname)
531 g_array_remove_index (elem->classes, i);
538 * gtk_widget_path_iter_clear_classes:
539 * @path: a #GtkWidget
540 * @pos: position to modify
542 * Removes all classes from the widget at position @pos in the
543 * hierarchy defined in @path.
548 gtk_widget_path_iter_clear_classes (GtkWidgetPath *path,
551 GtkPathElement *elem;
553 g_return_if_fail (path != NULL);
554 g_return_if_fail (pos < path->elems->len);
556 elem = &g_array_index (path->elems, GtkPathElement, pos);
561 if (elem->classes->len > 0)
562 g_array_remove_range (elem->classes, 0, elem->classes->len);
566 * gtk_widget_path_iter_list_classes:
567 * @path: a #GtkWidgetPath
568 * @pos: position to query
570 * Returns a list with all the class names defined for the widget
571 * at position @pos in the hierarchy defined in @path.
573 * Returns: (transfer container) (type utf8): The list of classes,
574 * This is a list of strings, the #GSList contents are
575 * owned by GTK+, but you should use g_slist_free() to
576 * free the list itself.
581 gtk_widget_path_iter_list_classes (const GtkWidgetPath *path,
584 GtkPathElement *elem;
588 g_return_val_if_fail (path != NULL, NULL);
589 g_return_val_if_fail (pos < path->elems->len, NULL);
591 elem = &g_array_index (path->elems, GtkPathElement, pos);
596 for (i = 0; i < elem->classes->len; i++)
600 quark = g_array_index (elem->classes, GQuark, i);
601 list = g_slist_prepend (list, (gchar *) g_quark_to_string (quark));
604 return g_slist_reverse (list);
608 * gtk_widget_path_iter_has_qclass:
609 * @path: a #GtkWidgetPath
610 * @pos: position to query
611 * @qname: class name as a #GQuark
613 * See gtk_widget_path_iter_has_class(). This is a version that operates
614 * with GQuark<!-- -->s.
616 * Returns: %TRUE if the widget at @pos has the class defined.
621 gtk_widget_path_iter_has_qclass (const GtkWidgetPath *path,
625 GtkPathElement *elem;
628 g_return_val_if_fail (path != NULL, FALSE);
629 g_return_val_if_fail (pos < path->elems->len, FALSE);
630 g_return_val_if_fail (qname != 0, FALSE);
632 elem = &g_array_index (path->elems, GtkPathElement, pos);
637 for (i = 0; i < elem->classes->len; i++)
641 quark = g_array_index (elem->classes, GQuark, i);
645 else if (quark > qname)
653 * gtk_widget_path_iter_has_class:
654 * @path: a #GtkWidgetPath
655 * @pos: position to query
658 * Returns %TRUE if the widget at position @pos has the class @name
659 * defined, %FALSE otherwise.
661 * Returns: %TRUE if the class @name is defined for the widget at @pos
666 gtk_widget_path_iter_has_class (const GtkWidgetPath *path,
672 g_return_val_if_fail (path != NULL, FALSE);
673 g_return_val_if_fail (pos < path->elems->len, FALSE);
674 g_return_val_if_fail (name != NULL, FALSE);
676 qname = g_quark_try_string (name);
681 return gtk_widget_path_iter_has_qclass (path, pos, qname);
685 * gtk_widget_path_iter_add_region:
686 * @path: a #GtkWidgetPath
687 * @pos: position to modify
689 * @flags: flags affecting the region
691 * Adds the region @name to the widget at position @pos in
692 * the hierarchy defined in @path. See
693 * gtk_style_context_add_region().
698 gtk_widget_path_iter_add_region (GtkWidgetPath *path,
701 GtkRegionFlags flags)
703 GtkPathElement *elem;
706 g_return_if_fail (path != NULL);
707 g_return_if_fail (pos < path->elems->len);
708 g_return_if_fail (name != NULL);
710 elem = &g_array_index (path->elems, GtkPathElement, pos);
711 qname = g_quark_from_string (name);
714 elem->regions = g_hash_table_new (NULL, NULL);
716 g_hash_table_insert (elem->regions,
717 GUINT_TO_POINTER (qname),
718 GUINT_TO_POINTER (flags));
722 * gtk_widget_path_iter_remove_region:
723 * @path: a #GtkWidgetPath
724 * @pos: position to modify
727 * Removes the region @name from the widget at position @pos in
728 * the hierarchy defined in @path.
733 gtk_widget_path_iter_remove_region (GtkWidgetPath *path,
737 GtkPathElement *elem;
740 g_return_if_fail (path != NULL);
741 g_return_if_fail (pos < path->elems->len);
742 g_return_if_fail (name != NULL);
744 qname = g_quark_try_string (name);
749 elem = &g_array_index (path->elems, GtkPathElement, pos);
752 g_hash_table_remove (elem->regions, GUINT_TO_POINTER (qname));
756 * gtk_widget_path_iter_clear_regions:
757 * @path: a #GtkWidgetPath
758 * @pos: position to modify
760 * Removes all regions from the widget at position @pos in the
761 * hierarchy defined in @path.
766 gtk_widget_path_iter_clear_regions (GtkWidgetPath *path,
769 GtkPathElement *elem;
771 g_return_if_fail (path != NULL);
772 g_return_if_fail (pos < path->elems->len);
774 elem = &g_array_index (path->elems, GtkPathElement, pos);
777 g_hash_table_remove_all (elem->regions);
781 * gtk_widget_path_iter_list_regions:
782 * @path: a #GtkWidgetPath
783 * @pos: position to query
785 * Returns a list with all the region names defined for the widget
786 * at position @pos in the hierarchy defined in @path.
788 * Returns: (transfer container) (type utf8): The list of regions,
789 * This is a list of strings, the #GSList contents are
790 * owned by GTK+, but you should use g_slist_free() to
791 * free the list itself.
796 gtk_widget_path_iter_list_regions (const GtkWidgetPath *path,
799 GtkPathElement *elem;
804 g_return_val_if_fail (path != NULL, NULL);
805 g_return_val_if_fail (pos < path->elems->len, NULL);
807 elem = &g_array_index (path->elems, GtkPathElement, pos);
812 g_hash_table_iter_init (&iter, elem->regions);
814 while (g_hash_table_iter_next (&iter, &key, NULL))
818 qname = GPOINTER_TO_UINT (key);
819 list = g_slist_prepend (list, (gchar *) g_quark_to_string (qname));
826 * gtk_widget_path_iter_has_qregion:
827 * @path: a #GtkWidgetPath
828 * @pos: position to query
829 * @qname: region name as a #GQuark
830 * @flags: (out): return location for the region flags
832 * See gtk_widget_path_iter_has_region(). This is a version that operates
833 * with GQuark<!-- -->s.
835 * Returns: %TRUE if the widget at @pos has the region defined.
840 gtk_widget_path_iter_has_qregion (const GtkWidgetPath *path,
843 GtkRegionFlags *flags)
845 GtkPathElement *elem;
848 g_return_val_if_fail (path != NULL, FALSE);
849 g_return_val_if_fail (pos < path->elems->len, FALSE);
850 g_return_val_if_fail (qname != 0, FALSE);
852 elem = &g_array_index (path->elems, GtkPathElement, pos);
857 if (!g_hash_table_lookup_extended (elem->regions,
858 GUINT_TO_POINTER (qname),
863 *flags = GPOINTER_TO_UINT (value);
869 * gtk_widget_path_iter_has_region:
870 * @path: a #GtkWidgetPath
871 * @pos: position to query
873 * @flags: (out): return location for the region flags
875 * Returns %TRUE if the widget at position @pos has the class @name
876 * defined, %FALSE otherwise.
878 * Returns: %TRUE if the class @name is defined for the widget at @pos
883 gtk_widget_path_iter_has_region (const GtkWidgetPath *path,
886 GtkRegionFlags *flags)
890 g_return_val_if_fail (path != NULL, FALSE);
891 g_return_val_if_fail (pos < path->elems->len, FALSE);
892 g_return_val_if_fail (name != NULL, FALSE);
894 qname = g_quark_try_string (name);
899 return gtk_widget_path_iter_has_qregion (path, pos, qname, flags);
903 * gtk_widget_path_get_widget_type:
904 * @path: a #GtkWidget
906 * Returns the topmost widget type, that is, the widget type this path
909 * Returns: The widget type
914 gtk_widget_path_get_widget_type (const GtkWidgetPath *path)
916 GtkPathElement *elem;
918 g_return_val_if_fail (path != NULL, G_TYPE_INVALID);
920 elem = &g_array_index (path->elems, GtkPathElement,
921 path->elems->len - 1);
926 * gtk_widget_path_is_type:
927 * @path: a #GtkWidgetPath
928 * @type: widget type to match
930 * Returns %TRUE if the widget type represented by this path
931 * is @type, or a subtype of it.
933 * Returns: %TRUE if the widget represented by @path is of type @type
938 gtk_widget_path_is_type (const GtkWidgetPath *path,
941 GtkPathElement *elem;
943 g_return_val_if_fail (path != NULL, FALSE);
944 g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), FALSE);
946 elem = &g_array_index (path->elems, GtkPathElement,
947 path->elems->len - 1);
949 if (elem->type == type ||
950 g_type_is_a (elem->type, type))
957 * gtk_widget_path_has_parent:
958 * @path: a #GtkWidgetPath
959 * @type: widget type to check in parents
961 * Returns %TRUE if any of the parents of the widget represented
962 * in @path is of type @type, or any subtype of it.
964 * Returns: %TRUE if any parent is of type @type
969 gtk_widget_path_has_parent (const GtkWidgetPath *path,
974 g_return_val_if_fail (path != NULL, FALSE);
975 g_return_val_if_fail (g_type_is_a (type, GTK_TYPE_WIDGET), FALSE);
977 for (i = 0; i < path->elems->len - 1; i++)
979 GtkPathElement *elem;
981 elem = &g_array_index (path->elems, GtkPathElement, i);
983 if (elem->type == type ||
984 g_type_is_a (elem->type, type))