3 * Copyright (C) 2010 Openismus GmbH
6 * Tristan Van Berkom <tristanvb@openismus.com>
8 * Based on some GtkComboBox menu code by Kristian Rietveld <kris@gtk.org>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the
22 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
28 * @Short_Description: A GtkMenu automatically created from a #GtkTreeModel
31 * The #GtkTreeMenu is used to display a drop-down menu allowing selection
32 * of every row in the model and is used by the #GtkComboBox for its drop-down
38 #include "gtktreemenu.h"
39 #include "gtkmarshalers.h"
40 #include "gtkmenuitem.h"
41 #include "gtkseparatormenuitem.h"
42 #include "gtkcellareabox.h"
43 #include "gtkcellareacontext.h"
44 #include "gtkcelllayout.h"
45 #include "gtkcellview.h"
46 #include "gtkmenushellprivate.h"
47 #include "gtkprivate.h"
49 #include "deprecated/gtktearoffmenuitem.h"
52 static GObject *gtk_tree_menu_constructor (GType type,
53 guint n_construct_properties,
54 GObjectConstructParam *construct_properties);
55 static void gtk_tree_menu_dispose (GObject *object);
56 static void gtk_tree_menu_finalize (GObject *object);
57 static void gtk_tree_menu_set_property (GObject *object,
61 static void gtk_tree_menu_get_property (GObject *object,
67 static void gtk_tree_menu_get_preferred_width (GtkWidget *widget,
70 static void gtk_tree_menu_get_preferred_height (GtkWidget *widget,
74 /* GtkCellLayoutIface */
75 static void gtk_tree_menu_cell_layout_init (GtkCellLayoutIface *iface);
76 static GtkCellArea *gtk_tree_menu_cell_layout_get_area (GtkCellLayout *layout);
79 /* TreeModel/DrawingArea callbacks and building menus/submenus */
80 static inline void rebuild_menu (GtkTreeMenu *menu);
81 static gboolean menu_occupied (GtkTreeMenu *menu,
86 static void relayout_item (GtkTreeMenu *menu,
90 static void gtk_tree_menu_populate (GtkTreeMenu *menu);
91 static GtkWidget *gtk_tree_menu_create_item (GtkTreeMenu *menu,
93 gboolean header_item);
94 static void gtk_tree_menu_create_submenu (GtkTreeMenu *menu,
97 static void gtk_tree_menu_set_area (GtkTreeMenu *menu,
99 static GtkWidget *gtk_tree_menu_get_path_item (GtkTreeMenu *menu,
101 static gboolean gtk_tree_menu_path_in_menu (GtkTreeMenu *menu,
103 gboolean *header_item);
104 static void row_inserted_cb (GtkTreeModel *model,
108 static void row_deleted_cb (GtkTreeModel *model,
111 static void row_reordered_cb (GtkTreeModel *model,
116 static void row_changed_cb (GtkTreeModel *model,
120 static void context_size_changed_cb (GtkCellAreaContext *context,
123 static void area_apply_attributes_cb (GtkCellArea *area,
124 GtkTreeModel *tree_model,
126 gboolean is_expander,
127 gboolean is_expanded,
129 static void item_activated_cb (GtkMenuItem *item,
131 static void submenu_activated_cb (GtkTreeMenu *submenu,
134 static void gtk_tree_menu_set_model_internal (GtkTreeMenu *menu,
135 GtkTreeModel *model);
139 struct _GtkTreeMenuPrivate
141 /* TreeModel and parent for this menu */
143 GtkTreeRowReference *root;
145 /* CellArea and context for this menu */
147 GtkCellAreaContext *context;
150 gulong size_changed_id;
151 gulong apply_attributes_id;
152 gulong row_inserted_id;
153 gulong row_deleted_id;
154 gulong row_reordered_id;
155 gulong row_changed_id;
163 guint32 menu_with_header : 1;
167 GtkTreeViewRowSeparatorFunc row_separator_func;
168 gpointer row_separator_data;
169 GDestroyNotify row_separator_destroy;
171 /* Submenu headers */
172 GtkTreeMenuHeaderFunc header_func;
173 gpointer header_data;
174 GDestroyNotify header_destroy;
189 SIGNAL_MENU_ACTIVATE,
193 static guint tree_menu_signals[N_SIGNALS] = { 0 };
194 static GQuark tree_menu_path_quark = 0;
196 G_DEFINE_TYPE_WITH_CODE (GtkTreeMenu, _gtk_tree_menu, GTK_TYPE_MENU,
197 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
198 gtk_tree_menu_cell_layout_init));
201 _gtk_tree_menu_init (GtkTreeMenu *menu)
203 GtkTreeMenuPrivate *priv;
205 menu->priv = G_TYPE_INSTANCE_GET_PRIVATE (menu,
213 priv->context = NULL;
215 priv->size_changed_id = 0;
216 priv->row_inserted_id = 0;
217 priv->row_deleted_id = 0;
218 priv->row_reordered_id = 0;
219 priv->row_changed_id = 0;
221 priv->wrap_width = 0;
222 priv->row_span_col = -1;
223 priv->col_span_col = -1;
225 priv->menu_with_header = FALSE;
226 priv->tearoff = FALSE;
228 priv->row_separator_func = NULL;
229 priv->row_separator_data = NULL;
230 priv->row_separator_destroy = NULL;
232 priv->header_func = NULL;
233 priv->header_data = NULL;
234 priv->header_destroy = NULL;
236 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
240 _gtk_tree_menu_class_init (GtkTreeMenuClass *class)
242 GObjectClass *object_class = G_OBJECT_CLASS (class);
243 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
245 tree_menu_path_quark = g_quark_from_static_string ("gtk-tree-menu-path");
247 object_class->constructor = gtk_tree_menu_constructor;
248 object_class->dispose = gtk_tree_menu_dispose;
249 object_class->finalize = gtk_tree_menu_finalize;
250 object_class->set_property = gtk_tree_menu_set_property;
251 object_class->get_property = gtk_tree_menu_get_property;
253 widget_class->get_preferred_width = gtk_tree_menu_get_preferred_width;
254 widget_class->get_preferred_height = gtk_tree_menu_get_preferred_height;
257 * GtkTreeMenu::menu-activate:
258 * @menu: a #GtkTreeMenu
259 * @path: the #GtkTreePath string for the item which was activated
260 * @user_data: the user data
262 * This signal is emitted to notify that a menu item in the #GtkTreeMenu
263 * was activated and provides the path string from the #GtkTreeModel
264 * to specify which row was selected.
268 tree_menu_signals[SIGNAL_MENU_ACTIVATE] =
269 g_signal_new (I_("menu-activate"),
270 G_OBJECT_CLASS_TYPE (object_class),
272 0, /* No class closure here */
274 _gtk_marshal_VOID__STRING,
275 G_TYPE_NONE, 1, G_TYPE_STRING);
280 * The #GtkTreeModel from which the menu is constructed.
284 g_object_class_install_property (object_class,
286 g_param_spec_object ("model",
287 P_("TreeMenu model"),
288 P_("The model for the tree menu"),
290 GTK_PARAM_READWRITE));
295 * The #GtkTreePath that is the root for this menu, or %NULL.
297 * The #GtkTreeMenu recursively creates submenus for #GtkTreeModel
298 * rows that have children and the "root" for each menu is provided
299 * by the parent menu.
301 * If you dont provide a root for the #GtkTreeMenu then the whole
302 * model will be added to the menu. Specifying a root allows you
303 * to build a menu for a given #GtkTreePath and its children.
307 g_object_class_install_property (object_class,
309 g_param_spec_boxed ("root",
310 P_("TreeMenu root row"),
311 P_("The TreeMenu will display children of the "
314 GTK_PARAM_READWRITE));
317 * GtkTreeMenu:cell-area:
319 * The #GtkCellArea used to render cells in the menu items.
321 * You can provide a different cell area at object construction
322 * time, otherwise the #GtkTreeMenu will use a #GtkCellAreaBox.
326 g_object_class_install_property (object_class,
328 g_param_spec_object ("cell-area",
330 P_("The GtkCellArea used to layout cells"),
332 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
335 * GtkTreeMenu:tearoff:
337 * Specifies whether this menu comes with a leading tearoff menu item
341 g_object_class_install_property (object_class,
343 g_param_spec_boolean ("tearoff",
345 P_("Whether the menu has a tearoff item"),
347 GTK_PARAM_READWRITE));
350 * GtkTreeMenu:wrap-width:
352 * If wrap-width is set to a positive value, the list will be
353 * displayed in multiple columns, the number of columns is
354 * determined by wrap-width.
358 g_object_class_install_property (object_class,
360 g_param_spec_int ("wrap-width",
362 P_("Wrap width for laying out items in a grid"),
366 GTK_PARAM_READWRITE));
369 * GtkTreeMenu:row-span-column:
371 * If this is set to a non-negative value, it must be the index of a column
372 * of type %G_TYPE_INT in the model.
374 * The values of that column are used to determine how many rows a value in
375 * the list will span. Therefore, the values in the model column pointed to
376 * by this property must be greater than zero and not larger than wrap-width.
380 g_object_class_install_property (object_class,
382 g_param_spec_int ("row-span-column",
383 P_("Row span column"),
384 P_("TreeModel column containing the row span values"),
388 GTK_PARAM_READWRITE));
391 * GtkTreeMenu:column-span-column:
393 * If this is set to a non-negative value, it must be the index of a column
394 * of type %G_TYPE_INT in the model.
396 * The values of that column are used to determine how many columns a value
397 * in the list will span.
401 g_object_class_install_property (object_class,
403 g_param_spec_int ("column-span-column",
404 P_("Column span column"),
405 P_("TreeModel column containing the column span values"),
409 GTK_PARAM_READWRITE));
411 g_type_class_add_private (object_class, sizeof (GtkTreeMenuPrivate));
414 /****************************************************************
416 ****************************************************************/
418 gtk_tree_menu_constructor (GType type,
419 guint n_construct_properties,
420 GObjectConstructParam *construct_properties)
424 GtkTreeMenuPrivate *priv;
426 object = G_OBJECT_CLASS (_gtk_tree_menu_parent_class)->constructor
427 (type, n_construct_properties, construct_properties);
429 menu = GTK_TREE_MENU (object);
434 GtkCellArea *area = gtk_cell_area_box_new ();
436 gtk_tree_menu_set_area (menu, area);
439 priv->context = gtk_cell_area_create_context (priv->area);
441 priv->size_changed_id =
442 g_signal_connect (priv->context, "notify",
443 G_CALLBACK (context_size_changed_cb), menu);
449 gtk_tree_menu_dispose (GObject *object)
452 GtkTreeMenuPrivate *priv;
454 menu = GTK_TREE_MENU (object);
457 _gtk_tree_menu_set_model (menu, NULL);
458 gtk_tree_menu_set_area (menu, NULL);
462 /* Disconnect signals */
463 g_signal_handler_disconnect (priv->context, priv->size_changed_id);
465 g_object_unref (priv->context);
466 priv->context = NULL;
467 priv->size_changed_id = 0;
470 G_OBJECT_CLASS (_gtk_tree_menu_parent_class)->dispose (object);
474 gtk_tree_menu_finalize (GObject *object)
477 GtkTreeMenuPrivate *priv;
479 menu = GTK_TREE_MENU (object);
482 _gtk_tree_menu_set_row_separator_func (menu, NULL, NULL, NULL);
483 _gtk_tree_menu_set_header_func (menu, NULL, NULL, NULL);
486 gtk_tree_row_reference_free (priv->root);
488 G_OBJECT_CLASS (_gtk_tree_menu_parent_class)->finalize (object);
492 gtk_tree_menu_set_property (GObject *object,
497 GtkTreeMenu *menu = GTK_TREE_MENU (object);
502 _gtk_tree_menu_set_model (menu, g_value_get_object (value));
506 _gtk_tree_menu_set_root (menu, g_value_get_boxed (value));
510 /* Construct-only, can only be assigned once */
511 gtk_tree_menu_set_area (menu, (GtkCellArea *)g_value_get_object (value));
515 _gtk_tree_menu_set_tearoff (menu, g_value_get_boolean (value));
518 case PROP_WRAP_WIDTH:
519 _gtk_tree_menu_set_wrap_width (menu, g_value_get_int (value));
522 case PROP_ROW_SPAN_COL:
523 _gtk_tree_menu_set_row_span_column (menu, g_value_get_int (value));
526 case PROP_COL_SPAN_COL:
527 _gtk_tree_menu_set_column_span_column (menu, g_value_get_int (value));
531 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
537 gtk_tree_menu_get_property (GObject *object,
542 GtkTreeMenu *menu = GTK_TREE_MENU (object);
543 GtkTreeMenuPrivate *priv = menu->priv;
548 g_value_set_object (value, priv->model);
552 g_value_set_boxed (value, priv->root);
556 g_value_set_object (value, priv->area);
560 g_value_set_boolean (value, priv->tearoff);
564 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
569 /****************************************************************
571 ****************************************************************/
573 /* We tell all the menu items to reserve space for the submenu
574 * indicator if there is at least one submenu, this way we ensure
575 * that every internal cell area gets allocated the
576 * same width (and requested height for the same appropriate width).
579 sync_reserve_submenu_size (GtkTreeMenu *menu)
582 gboolean has_submenu = FALSE;
584 children = gtk_container_get_children (GTK_CONTAINER (menu));
585 for (l = children; l; l = l->next)
587 GtkMenuItem *item = l->data;
589 if (gtk_menu_item_get_submenu (item) != NULL)
596 for (l = children; l; l = l->next)
598 GtkMenuItem *item = l->data;
600 gtk_menu_item_set_reserve_indicator (item, has_submenu);
603 g_list_free (children);
607 gtk_tree_menu_get_preferred_width (GtkWidget *widget,
611 GtkTreeMenu *menu = GTK_TREE_MENU (widget);
612 GtkTreeMenuPrivate *priv = menu->priv;
614 /* We leave the requesting work up to the cellviews which operate in the same
615 * context, reserving space for the submenu indicator if any of the items have
616 * submenus ensures that every cellview will receive the same allocated width.
618 * Since GtkMenu does hieght-for-width correctly, we know that the width of
619 * every cell will be requested before the height-for-widths are requested.
621 g_signal_handler_block (priv->context, priv->size_changed_id);
623 sync_reserve_submenu_size (menu);
625 GTK_WIDGET_CLASS (_gtk_tree_menu_parent_class)->get_preferred_width (widget, minimum_size, natural_size);
627 g_signal_handler_unblock (priv->context, priv->size_changed_id);
631 gtk_tree_menu_get_preferred_height (GtkWidget *widget,
635 GtkTreeMenu *menu = GTK_TREE_MENU (widget);
636 GtkTreeMenuPrivate *priv = menu->priv;
638 g_signal_handler_block (priv->context, priv->size_changed_id);
640 sync_reserve_submenu_size (menu);
642 GTK_WIDGET_CLASS (_gtk_tree_menu_parent_class)->get_preferred_height (widget, minimum_size, natural_size);
644 g_signal_handler_unblock (priv->context, priv->size_changed_id);
647 /****************************************************************
648 * GtkCellLayoutIface *
649 ****************************************************************/
651 gtk_tree_menu_cell_layout_init (GtkCellLayoutIface *iface)
653 iface->get_area = gtk_tree_menu_cell_layout_get_area;
657 gtk_tree_menu_cell_layout_get_area (GtkCellLayout *layout)
659 GtkTreeMenu *menu = GTK_TREE_MENU (layout);
660 GtkTreeMenuPrivate *priv = menu->priv;
666 /****************************************************************
667 * TreeModel callbacks/populating menus *
668 ****************************************************************/
670 gtk_tree_menu_get_path_item (GtkTreeMenu *menu,
673 GtkWidget *item = NULL;
676 children = gtk_container_get_children (GTK_CONTAINER (menu));
678 for (l = children; item == NULL && l != NULL; l = l->next)
680 GtkWidget *child = l->data;
681 GtkTreePath *path = NULL;
683 if (GTK_IS_SEPARATOR_MENU_ITEM (child))
685 GtkTreeRowReference *row =
686 g_object_get_qdata (G_OBJECT (child), tree_menu_path_quark);
690 path = gtk_tree_row_reference_get_path (row);
693 /* Return any first child where its row-reference became invalid,
694 * this is because row-references get null paths before we recieve
695 * the "row-deleted" signal.
700 else if (!GTK_IS_TEAROFF_MENU_ITEM (child))
702 GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
704 /* It's always a cellview */
705 if (GTK_IS_CELL_VIEW (view))
706 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view));
709 /* Return any first child where its row-reference became invalid,
710 * this is because row-references get null paths before we recieve
711 * the "row-deleted" signal.
718 if (gtk_tree_path_compare (search, path) == 0)
721 gtk_tree_path_free (path);
725 g_list_free (children);
731 gtk_tree_menu_path_in_menu (GtkTreeMenu *menu,
733 gboolean *header_item)
735 GtkTreeMenuPrivate *priv = menu->priv;
736 gboolean in_menu = FALSE;
737 gboolean is_header = FALSE;
739 /* Check if the is in root of the model */
740 if (gtk_tree_path_get_depth (path) == 1 && !priv->root)
742 /* If we are a submenu, compare the parent path */
745 GtkTreePath *root_path = gtk_tree_row_reference_get_path (priv->root);
746 GtkTreePath *search_path = gtk_tree_path_copy (path);
750 if (priv->menu_with_header &&
751 gtk_tree_path_compare (root_path, search_path) == 0)
756 else if (gtk_tree_path_get_depth (search_path) > 1)
758 gtk_tree_path_up (search_path);
760 if (gtk_tree_path_compare (root_path, search_path) == 0)
764 gtk_tree_path_free (root_path);
765 gtk_tree_path_free (search_path);
769 *header_item = is_header;
775 gtk_tree_menu_path_needs_submenu (GtkTreeMenu *menu,
778 GtkWidget *item = NULL;
780 GtkTreePath *parent_path;
782 if (gtk_tree_path_get_depth (search) <= 1)
785 parent_path = gtk_tree_path_copy (search);
786 gtk_tree_path_up (parent_path);
788 children = gtk_container_get_children (GTK_CONTAINER (menu));
790 for (l = children; item == NULL && l != NULL; l = l->next)
792 GtkWidget *child = l->data;
793 GtkTreePath *path = NULL;
795 /* Separators dont get submenus, if it already has a submenu then let
796 * the submenu handle inserted rows */
797 if (!GTK_IS_SEPARATOR_MENU_ITEM (child) &&
798 !gtk_menu_item_get_submenu (GTK_MENU_ITEM (child)))
800 GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
802 /* It's always a cellview */
803 if (GTK_IS_CELL_VIEW (view))
804 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view));
809 if (gtk_tree_path_compare (parent_path, path) == 0)
812 gtk_tree_path_free (path);
816 g_list_free (children);
817 gtk_tree_path_free (parent_path);
823 find_empty_submenu (GtkTreeMenu *menu)
825 GtkTreeMenuPrivate *priv = menu->priv;
827 GtkWidget *submenu = NULL;
829 children = gtk_container_get_children (GTK_CONTAINER (menu));
831 for (l = children; submenu == NULL && l != NULL; l = l->next)
833 GtkWidget *child = l->data;
834 GtkTreePath *path = NULL;
837 /* Separators dont get submenus, if it already has a submenu then let
838 * the submenu handle inserted rows */
839 if (!GTK_IS_SEPARATOR_MENU_ITEM (child) && !GTK_IS_TEAROFF_MENU_ITEM (child))
841 GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
843 /* It's always a cellview */
844 if (GTK_IS_CELL_VIEW (view))
845 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view));
850 if (gtk_tree_model_get_iter (priv->model, &iter, path) &&
851 !gtk_tree_model_iter_has_child (priv->model, &iter))
852 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (child));
854 gtk_tree_path_free (path);
858 g_list_free (children);
864 row_inserted_cb (GtkTreeModel *model,
869 GtkTreeMenuPrivate *priv = menu->priv;
870 gint *indices, index, depth;
873 /* If the iter should be in this menu then go ahead and insert it */
874 if (gtk_tree_menu_path_in_menu (menu, path, NULL))
876 if (priv->wrap_width > 0)
880 /* Get the index of the path for this depth */
881 indices = gtk_tree_path_get_indices (path);
882 depth = gtk_tree_path_get_depth (path);
883 index = indices[depth -1];
885 /* Menus with a header include a menuitem for its root node
886 * and a separator menu item */
887 if (priv->menu_with_header)
890 /* Index after the tearoff item for the root menu if
891 * there is a tearoff item
893 if (priv->root == NULL && priv->tearoff)
896 item = gtk_tree_menu_create_item (menu, iter, FALSE);
897 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, index);
899 /* Resize everything */
900 gtk_cell_area_context_reset (menu->priv->context);
905 /* Create submenus for iters if we need to */
906 item = gtk_tree_menu_path_needs_submenu (menu, path);
909 GtkTreePath *item_path = gtk_tree_path_copy (path);
911 gtk_tree_path_up (item_path);
912 gtk_tree_menu_create_submenu (menu, item, item_path);
913 gtk_tree_path_free (item_path);
919 row_deleted_cb (GtkTreeModel *model,
923 GtkTreeMenuPrivate *priv = menu->priv;
926 /* If it's the header item we leave it to the parent menu
927 * to remove us from its menu
929 item = gtk_tree_menu_get_path_item (menu, path);
933 if (priv->wrap_width > 0)
937 /* Get rid of the deleted item */
938 gtk_widget_destroy (item);
940 /* Resize everything */
941 gtk_cell_area_context_reset (menu->priv->context);
946 /* It's up to the parent menu to destroy a child menu that becomes empty
947 * since the topmost menu belongs to the user and is allowed to have no contents */
948 GtkWidget *submenu = find_empty_submenu (menu);
950 gtk_widget_destroy (submenu);
955 row_reordered_cb (GtkTreeModel *model,
961 GtkTreeMenuPrivate *priv = menu->priv;
962 gboolean this_menu = FALSE;
964 if (gtk_tree_path_get_depth (path) == 0 && !priv->root)
968 GtkTreePath *root_path =
969 gtk_tree_row_reference_get_path (priv->root);
971 if (gtk_tree_path_compare (root_path, path) == 0)
974 gtk_tree_path_free (root_path);
982 menu_item_position (GtkTreeMenu *menu,
988 children = gtk_container_get_children (GTK_CONTAINER (menu));
989 for (position = 0, l = children; l; position++, l = l->next)
991 GtkWidget *iitem = l->data;
997 g_list_free (children);
1003 row_changed_cb (GtkTreeModel *model,
1008 GtkTreeMenuPrivate *priv = menu->priv;
1009 gboolean is_separator = FALSE;
1010 gboolean has_header = FALSE;
1013 item = gtk_tree_menu_get_path_item (menu, path);
1017 GtkTreePath *root_path =
1018 gtk_tree_row_reference_get_path (priv->root);
1020 if (root_path && gtk_tree_path_compare (root_path, path) == 0)
1022 if (priv->header_func)
1024 priv->header_func (priv->model, iter, priv->header_data);
1026 if (has_header && !item)
1028 item = gtk_separator_menu_item_new ();
1029 gtk_widget_show (item);
1030 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
1032 item = gtk_tree_menu_create_item (menu, iter, TRUE);
1033 gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
1035 priv->menu_with_header = TRUE;
1037 else if (!has_header && item)
1039 /* Destroy the header item and then the following separator */
1040 gtk_widget_destroy (item);
1041 gtk_widget_destroy (GTK_MENU_SHELL (menu)->priv->children->data);
1043 priv->menu_with_header = FALSE;
1046 gtk_tree_path_free (root_path);
1052 if (priv->wrap_width > 0)
1053 /* Ugly, we need to rebuild the menu here if
1054 * the row-span/row-column values change
1056 rebuild_menu (menu);
1059 if (priv->row_separator_func)
1061 priv->row_separator_func (model, iter,
1062 priv->row_separator_data);
1065 if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
1067 gint position = menu_item_position (menu, item);
1069 gtk_widget_destroy (item);
1070 item = gtk_tree_menu_create_item (menu, iter, FALSE);
1071 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, position);
1078 context_size_changed_cb (GtkCellAreaContext *context,
1082 if (!strcmp (pspec->name, "minimum-width") ||
1083 !strcmp (pspec->name, "natural-width") ||
1084 !strcmp (pspec->name, "minimum-height") ||
1085 !strcmp (pspec->name, "natural-height"))
1086 gtk_widget_queue_resize (menu);
1090 area_is_sensitive (GtkCellArea *area)
1092 GList *cells, *list;
1093 gboolean sensitive = FALSE;
1095 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
1097 for (list = cells; list; list = list->next)
1099 g_object_get (list->data, "sensitive", &sensitive, NULL);
1104 g_list_free (cells);
1110 area_apply_attributes_cb (GtkCellArea *area,
1111 GtkTreeModel *tree_model,
1113 gboolean is_expander,
1114 gboolean is_expanded,
1117 /* If the menu for this iter has a submenu */
1118 GtkTreeMenuPrivate *priv = menu->priv;
1124 path = gtk_tree_model_get_path (tree_model, iter);
1126 if (gtk_tree_menu_path_in_menu (menu, path, &is_header))
1128 item = gtk_tree_menu_get_path_item (menu, path);
1130 /* If there is no submenu, go ahead and update item sensitivity,
1131 * items with submenus are always sensitive */
1132 if (item && !gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)))
1134 sensitive = area_is_sensitive (priv->area);
1136 gtk_widget_set_sensitive (item, sensitive);
1140 /* For header items we need to set the sensitivity
1141 * of the following separator item
1143 if (GTK_MENU_SHELL (menu)->priv->children &&
1144 GTK_MENU_SHELL (menu)->priv->children->next)
1146 GtkWidget *separator =
1147 GTK_MENU_SHELL (menu)->priv->children->next->data;
1149 gtk_widget_set_sensitive (separator, sensitive);
1155 gtk_tree_path_free (path);
1159 gtk_tree_menu_set_area (GtkTreeMenu *menu,
1162 GtkTreeMenuPrivate *priv = menu->priv;
1166 g_signal_handler_disconnect (priv->area,
1167 priv->apply_attributes_id);
1168 priv->apply_attributes_id = 0;
1170 g_object_unref (priv->area);
1177 g_object_ref_sink (priv->area);
1179 priv->apply_attributes_id =
1180 g_signal_connect (priv->area, "apply-attributes",
1181 G_CALLBACK (area_apply_attributes_cb), menu);
1186 menu_occupied (GtkTreeMenu *menu,
1190 guint bottom_attach)
1194 for (i = GTK_MENU_SHELL (menu)->priv->children; i; i = i->next)
1198 gtk_container_child_get (GTK_CONTAINER (menu),
1202 "bottom-attach", &b,
1206 /* look if this item intersects with the given coordinates */
1207 if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
1215 relayout_item (GtkTreeMenu *menu,
1220 GtkTreeMenuPrivate *priv = menu->priv;
1221 gint current_col = 0, current_row = 0;
1222 gint rows = 1, cols = 1;
1224 if (priv->col_span_col == -1 &&
1225 priv->row_span_col == -1 &&
1228 gtk_container_child_get (GTK_CONTAINER (menu), prev,
1229 "right-attach", ¤t_col,
1230 "top-attach", ¤t_row,
1232 if (current_col + cols > priv->wrap_width)
1240 if (priv->col_span_col != -1)
1241 gtk_tree_model_get (priv->model, iter,
1242 priv->col_span_col, &cols,
1244 if (priv->row_span_col != -1)
1245 gtk_tree_model_get (priv->model, iter,
1246 priv->row_span_col, &rows,
1251 if (current_col + cols > priv->wrap_width)
1257 if (!menu_occupied (menu,
1258 current_col, current_col + cols,
1259 current_row, current_row + rows))
1266 /* set attach props */
1267 gtk_menu_attach (GTK_MENU (menu), item,
1268 current_col, current_col + cols,
1269 current_row, current_row + rows);
1273 gtk_tree_menu_create_submenu (GtkTreeMenu *menu,
1277 GtkTreeMenuPrivate *priv = menu->priv;
1281 view = gtk_bin_get_child (GTK_BIN (item));
1282 gtk_cell_view_set_draw_sensitive (GTK_CELL_VIEW (view), TRUE);
1284 submenu = _gtk_tree_menu_new_with_area (priv->area);
1286 _gtk_tree_menu_set_row_separator_func (GTK_TREE_MENU (submenu),
1287 priv->row_separator_func,
1288 priv->row_separator_data,
1289 priv->row_separator_destroy);
1290 _gtk_tree_menu_set_header_func (GTK_TREE_MENU (submenu),
1293 priv->header_destroy);
1295 _gtk_tree_menu_set_wrap_width (GTK_TREE_MENU (submenu), priv->wrap_width);
1296 _gtk_tree_menu_set_row_span_column (GTK_TREE_MENU (submenu), priv->row_span_col);
1297 _gtk_tree_menu_set_column_span_column (GTK_TREE_MENU (submenu), priv->col_span_col);
1299 gtk_tree_menu_set_model_internal (GTK_TREE_MENU (submenu), priv->model);
1300 _gtk_tree_menu_set_root (GTK_TREE_MENU (submenu), path);
1301 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
1303 g_signal_connect (submenu, "menu-activate",
1304 G_CALLBACK (submenu_activated_cb), menu);
1308 gtk_tree_menu_create_item (GtkTreeMenu *menu,
1310 gboolean header_item)
1312 GtkTreeMenuPrivate *priv = menu->priv;
1313 GtkWidget *item, *view;
1315 gboolean is_separator = FALSE;
1317 path = gtk_tree_model_get_path (priv->model, iter);
1319 if (priv->row_separator_func)
1321 priv->row_separator_func (priv->model, iter,
1322 priv->row_separator_data);
1326 item = gtk_separator_menu_item_new ();
1327 gtk_widget_show (item);
1329 g_object_set_qdata_full (G_OBJECT (item),
1330 tree_menu_path_quark,
1331 gtk_tree_row_reference_new (priv->model, path),
1332 (GDestroyNotify)gtk_tree_row_reference_free);
1336 view = gtk_cell_view_new_with_context (priv->area, priv->context);
1337 item = gtk_menu_item_new ();
1338 gtk_widget_show (view);
1339 gtk_widget_show (item);
1341 gtk_cell_view_set_model (GTK_CELL_VIEW (view), priv->model);
1342 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (view), path);
1344 gtk_widget_show (view);
1345 gtk_container_add (GTK_CONTAINER (item), view);
1347 g_signal_connect (item, "activate", G_CALLBACK (item_activated_cb), menu);
1349 /* Add a GtkTreeMenu submenu to render the children of this row */
1350 if (header_item == FALSE &&
1351 gtk_tree_model_iter_has_child (priv->model, iter))
1352 gtk_tree_menu_create_submenu (menu, item, path);
1355 gtk_tree_path_free (path);
1361 rebuild_menu (GtkTreeMenu *menu)
1363 GtkTreeMenuPrivate *priv = menu->priv;
1365 /* Destroy all the menu items */
1366 gtk_container_foreach (GTK_CONTAINER (menu),
1367 (GtkCallback) gtk_widget_destroy, NULL);
1371 gtk_tree_menu_populate (menu);
1376 gtk_tree_menu_populate (GtkTreeMenu *menu)
1378 GtkTreeMenuPrivate *priv = menu->priv;
1379 GtkTreePath *path = NULL;
1382 gboolean valid = FALSE;
1383 GtkWidget *menu_item, *prev = NULL;
1389 path = gtk_tree_row_reference_get_path (priv->root);
1393 if (gtk_tree_model_get_iter (priv->model, &parent, path))
1395 valid = gtk_tree_model_iter_children (priv->model, &iter, &parent);
1397 if (priv->header_func &&
1398 priv->header_func (priv->model, &parent, priv->header_data))
1400 /* Add a submenu header for rows which desire one, used for
1401 * combo boxes to allow all rows to be activatable/selectable
1403 menu_item = gtk_tree_menu_create_item (menu, &parent, TRUE);
1404 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1406 menu_item = gtk_separator_menu_item_new ();
1407 gtk_widget_show (menu_item);
1408 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1411 priv->menu_with_header = TRUE;
1414 gtk_tree_path_free (path);
1418 /* Tearoff menu items only go in the root menu */
1421 menu_item = gtk_tearoff_menu_item_new ();
1422 gtk_widget_show (menu_item);
1424 if (priv->wrap_width > 0)
1425 gtk_menu_attach (GTK_MENU (menu), menu_item, 0, priv->wrap_width, 0, 1);
1427 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1432 valid = gtk_tree_model_iter_children (priv->model, &iter, NULL);
1435 /* Create a menu item for every row at the current depth, add a GtkTreeMenu
1436 * submenu for iters/items that have children */
1439 menu_item = gtk_tree_menu_create_item (menu, &iter, FALSE);
1441 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1443 if (priv->wrap_width > 0)
1444 relayout_item (menu, menu_item, &iter, prev);
1447 valid = gtk_tree_model_iter_next (priv->model, &iter);
1452 item_activated_cb (GtkMenuItem *item,
1459 /* Only activate leafs, not parents */
1460 if (!gtk_menu_item_get_submenu (item))
1462 view = GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)));
1463 path = gtk_cell_view_get_displayed_row (view);
1464 path_str = gtk_tree_path_to_string (path);
1466 g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path_str);
1469 gtk_tree_path_free (path);
1474 submenu_activated_cb (GtkTreeMenu *submenu,
1478 g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path);
1481 /* Sets the model without rebuilding the menu, prevents
1482 * infinite recursion while building submenus (we wait
1483 * until the root is set and then build the menu) */
1485 gtk_tree_menu_set_model_internal (GtkTreeMenu *menu,
1486 GtkTreeModel *model)
1488 GtkTreeMenuPrivate *priv;
1492 if (priv->model != model)
1496 /* Disconnect signals */
1497 g_signal_handler_disconnect (priv->model,
1498 priv->row_inserted_id);
1499 g_signal_handler_disconnect (priv->model,
1500 priv->row_deleted_id);
1501 g_signal_handler_disconnect (priv->model,
1502 priv->row_reordered_id);
1503 g_signal_handler_disconnect (priv->model,
1504 priv->row_changed_id);
1505 priv->row_inserted_id = 0;
1506 priv->row_deleted_id = 0;
1507 priv->row_reordered_id = 0;
1508 priv->row_changed_id = 0;
1510 g_object_unref (priv->model);
1513 priv->model = model;
1517 g_object_ref (priv->model);
1519 /* Connect signals */
1520 priv->row_inserted_id = g_signal_connect (priv->model, "row-inserted",
1521 G_CALLBACK (row_inserted_cb), menu);
1522 priv->row_deleted_id = g_signal_connect (priv->model, "row-deleted",
1523 G_CALLBACK (row_deleted_cb), menu);
1524 priv->row_reordered_id = g_signal_connect (priv->model, "rows-reordered",
1525 G_CALLBACK (row_reordered_cb), menu);
1526 priv->row_changed_id = g_signal_connect (priv->model, "row-changed",
1527 G_CALLBACK (row_changed_cb), menu);
1532 /****************************************************************
1534 ****************************************************************/
1537 * _gtk_tree_menu_new:
1539 * Creates a new #GtkTreeMenu.
1541 * Return value: A newly created #GtkTreeMenu with no model or root.
1546 _gtk_tree_menu_new (void)
1548 return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU, NULL);
1552 * _gtk_tree_menu_new_with_area:
1553 * @area: the #GtkCellArea to use to render cells in the menu
1555 * Creates a new #GtkTreeMenu using @area to render its cells.
1557 * Return value: A newly created #GtkTreeMenu with no model or root.
1562 _gtk_tree_menu_new_with_area (GtkCellArea *area)
1564 return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU,
1570 * _gtk_tree_menu_new_full:
1571 * @area: (allow-none): the #GtkCellArea to use to render cells in the menu, or %NULL.
1572 * @model: (allow-none): the #GtkTreeModel to build the menu heirarchy from, or %NULL.
1573 * @root: (allow-none): the #GtkTreePath indicating the root row for this menu, or %NULL.
1575 * Creates a new #GtkTreeMenu hierarchy from the provided @model and @root using @area to render its cells.
1577 * Return value: A newly created #GtkTreeMenu.
1582 _gtk_tree_menu_new_full (GtkCellArea *area,
1583 GtkTreeModel *model,
1586 return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU,
1594 * _gtk_tree_menu_set_model:
1595 * @menu: a #GtkTreeMenu
1596 * @model: (allow-none): the #GtkTreeModel to build the menu hierarchy from, or %NULL.
1598 * Sets @model to be used to build the menu heirarhcy.
1603 _gtk_tree_menu_set_model (GtkTreeMenu *menu,
1604 GtkTreeModel *model)
1606 g_return_if_fail (GTK_IS_TREE_MENU (menu));
1607 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
1609 gtk_tree_menu_set_model_internal (menu, model);
1611 rebuild_menu (menu);
1615 * _gtk_tree_menu_get_model:
1616 * @menu: a #GtkTreeMenu
1618 * Gets the @model currently used for the menu heirarhcy.
1620 * Return value: (transfer none): the #GtkTreeModel which is used
1621 * for @menu's hierarchy.
1626 _gtk_tree_menu_get_model (GtkTreeMenu *menu)
1628 GtkTreeMenuPrivate *priv;
1630 g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1638 * _gtk_tree_menu_set_root:
1639 * @menu: a #GtkTreeMenu
1640 * @path: (allow-none): the #GtkTreePath which is the root of @menu, or %NULL.
1642 * Sets the root of a @menu's hierarchy to be @path. @menu must already
1643 * have a model set and @path must point to a valid path inside the model.
1648 _gtk_tree_menu_set_root (GtkTreeMenu *menu,
1651 GtkTreeMenuPrivate *priv;
1653 g_return_if_fail (GTK_IS_TREE_MENU (menu));
1654 g_return_if_fail (menu->priv->model != NULL || path == NULL);
1659 gtk_tree_row_reference_free (priv->root);
1662 priv->root = gtk_tree_row_reference_new (priv->model, path);
1666 rebuild_menu (menu);
1670 * _gtk_tree_menu_get_root:
1671 * @menu: a #GtkTreeMenu
1673 * Gets the @root path for @menu's hierarchy, or returns %NULL if @menu
1674 * has no model or is building a heirarchy for the entire model. *
1676 * Return value: (transfer full) (allow-none): A newly created #GtkTreePath
1677 * pointing to the root of @menu which must be freed with gtk_tree_path_free().
1682 _gtk_tree_menu_get_root (GtkTreeMenu *menu)
1684 GtkTreeMenuPrivate *priv;
1686 g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1691 return gtk_tree_row_reference_get_path (priv->root);
1697 * _gtk_tree_menu_get_tearoff:
1698 * @menu: a #GtkTreeMenu
1700 * Gets whether this menu is build with a leading tearoff menu item.
1702 * Return value: %TRUE if the menu has a tearoff item.
1707 _gtk_tree_menu_get_tearoff (GtkTreeMenu *menu)
1709 GtkTreeMenuPrivate *priv;
1711 g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1715 return priv->tearoff;
1719 * _gtk_tree_menu_set_tearoff:
1720 * @menu: a #GtkTreeMenu
1721 * @tearoff: whether the menu should have a leading tearoff menu item.
1723 * Sets whether this menu has a leading tearoff menu item.
1728 _gtk_tree_menu_set_tearoff (GtkTreeMenu *menu,
1731 GtkTreeMenuPrivate *priv;
1733 g_return_if_fail (GTK_IS_TREE_MENU (menu));
1737 if (priv->tearoff != tearoff)
1739 priv->tearoff = tearoff;
1741 rebuild_menu (menu);
1743 g_object_notify (G_OBJECT (menu), "tearoff");
1748 * _gtk_tree_menu_get_wrap_width:
1749 * @menu: a #GtkTreeMenu
1751 * Gets the wrap width which is used to determine the number of columns
1752 * for @menu. If the wrap width is larger than 1, @menu is in table mode.
1754 * Return value: the wrap width.
1759 _gtk_tree_menu_get_wrap_width (GtkTreeMenu *menu)
1761 GtkTreeMenuPrivate *priv;
1763 g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1767 return priv->wrap_width;
1771 * _gtk_tree_menu_set_wrap_width:
1772 * @menu: a #GtkTreeMenu
1773 * @width: the wrap width
1775 * Sets the wrap width which is used to determine the number of columns
1776 * for @menu. If the wrap width is larger than 1, @menu is in table mode.
1781 _gtk_tree_menu_set_wrap_width (GtkTreeMenu *menu,
1784 GtkTreeMenuPrivate *priv;
1786 g_return_if_fail (GTK_IS_TREE_MENU (menu));
1787 g_return_if_fail (width >= 0);
1791 if (priv->wrap_width != width)
1793 priv->wrap_width = width;
1795 rebuild_menu (menu);
1797 g_object_notify (G_OBJECT (menu), "wrap-width");
1802 * _gtk_tree_menu_get_row_span_column:
1803 * @menu: a #GtkTreeMenu
1805 * Gets the column with row span information for @menu.
1806 * The row span column contains integers which indicate how many rows
1807 * a menu item should span.
1809 * Return value: the column in @menu's model containing row span information, or -1.
1814 _gtk_tree_menu_get_row_span_column (GtkTreeMenu *menu)
1816 GtkTreeMenuPrivate *priv;
1818 g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1822 return priv->row_span_col;
1826 * _gtk_tree_menu_set_row_span_column:
1827 * @menu: a #GtkTreeMenu
1828 * @row_span: the column in the model to fetch the row span for a given menu item.
1830 * Sets the column with row span information for @menu to be @row_span.
1831 * The row span column contains integers which indicate how many rows
1832 * a menu item should span.
1837 _gtk_tree_menu_set_row_span_column (GtkTreeMenu *menu,
1840 GtkTreeMenuPrivate *priv;
1842 g_return_if_fail (GTK_IS_TREE_MENU (menu));
1846 if (priv->row_span_col != row_span)
1848 priv->row_span_col = row_span;
1850 if (priv->wrap_width > 0)
1851 rebuild_menu (menu);
1853 g_object_notify (G_OBJECT (menu), "row-span-column");
1858 * _gtk_tree_menu_get_column_span_column:
1859 * @menu: a #GtkTreeMenu
1861 * Gets the column with column span information for @menu.
1862 * The column span column contains integers which indicate how many columns
1863 * a menu item should span.
1865 * Return value: the column in @menu's model containing column span information, or -1.
1870 _gtk_tree_menu_get_column_span_column (GtkTreeMenu *menu)
1872 GtkTreeMenuPrivate *priv;
1874 g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1878 return priv->col_span_col;
1882 * _gtk_tree_menu_set_column_span_column:
1883 * @menu: a #GtkTreeMenu
1884 * @column_span: the column in the model to fetch the column span for a given menu item.
1886 * Sets the column with column span information for @menu to be @column_span.
1887 * The column span column contains integers which indicate how many columns
1888 * a menu item should span.
1893 _gtk_tree_menu_set_column_span_column (GtkTreeMenu *menu,
1896 GtkTreeMenuPrivate *priv;
1898 g_return_if_fail (GTK_IS_TREE_MENU (menu));
1902 if (priv->col_span_col != column_span)
1904 priv->col_span_col = column_span;
1906 if (priv->wrap_width > 0)
1907 rebuild_menu (menu);
1909 g_object_notify (G_OBJECT (menu), "column-span-column");
1914 * _gtk_tree_menu_get_row_separator_func:
1915 * @menu: a #GtkTreeMenu
1917 * Gets the current #GtkTreeViewRowSeparatorFunc separator function.
1919 * Return value: the current row separator function.
1923 GtkTreeViewRowSeparatorFunc
1924 _gtk_tree_menu_get_row_separator_func (GtkTreeMenu *menu)
1926 GtkTreeMenuPrivate *priv;
1928 g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1932 return priv->row_separator_func;
1936 * _gtk_tree_menu_set_row_separator_func:
1937 * @menu: a #GtkTreeMenu
1938 * @func: (allow-none): a #GtkTreeViewRowSeparatorFunc, or %NULL to unset the separator function.
1939 * @data: (allow-none): user data to pass to @func, or %NULL
1940 * @destroy: (allow-none): destroy notifier for @data, or %NULL
1942 * Sets the row separator function, which is used to determine
1943 * whether a row should be drawn as a separator. If the row separator
1944 * function is %NULL, no separators are drawn. This is the default value.
1949 _gtk_tree_menu_set_row_separator_func (GtkTreeMenu *menu,
1950 GtkTreeViewRowSeparatorFunc func,
1952 GDestroyNotify destroy)
1954 GtkTreeMenuPrivate *priv;
1956 g_return_if_fail (GTK_IS_TREE_MENU (menu));
1960 if (priv->row_separator_destroy)
1961 priv->row_separator_destroy (priv->row_separator_data);
1963 priv->row_separator_func = func;
1964 priv->row_separator_data = data;
1965 priv->row_separator_destroy = destroy;
1967 rebuild_menu (menu);
1971 * _gtk_tree_menu_get_header_func:
1972 * @menu: a #GtkTreeMenu
1974 * Gets the current #GtkTreeMenuHeaderFunc header function.
1976 * Return value: the current header function.
1980 GtkTreeMenuHeaderFunc
1981 _gtk_tree_menu_get_header_func (GtkTreeMenu *menu)
1983 GtkTreeMenuPrivate *priv;
1985 g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1989 return priv->header_func;
1993 * _gtk_tree_menu_set_header_func:
1994 * @menu: a #GtkTreeMenu
1995 * @func: (allow-none): a #GtkTreeMenuHeaderFunc, or %NULL to unset the header function.
1996 * @data: (allow-none): user data to pass to @func, or %NULL
1997 * @destroy: (allow-none): destroy notifier for @data, or %NULL
1999 * Sets the header function, which is used to determine
2000 * whether a row width children should contain a leading header
2001 * menu item to allow that row to be selectable as an independant
2002 * menu item. If the header function is %NULL, no rows with children
2003 * have menu items which can be activated as leafs.
2004 * This is the default value.
2009 _gtk_tree_menu_set_header_func (GtkTreeMenu *menu,
2010 GtkTreeMenuHeaderFunc func,
2012 GDestroyNotify destroy)
2014 GtkTreeMenuPrivate *priv;
2016 g_return_if_fail (GTK_IS_TREE_MENU (menu));
2020 if (priv->header_destroy)
2021 priv->header_destroy (priv->header_data);
2023 priv->header_func = func;
2024 priv->header_data = data;
2025 priv->header_destroy = destroy;
2027 rebuild_menu (menu);