]> Pileus Git - ~andy/gtk/blob - gtk/gtktreemenu.c
Make GtkTreeMenu update menu item sensitivity when "apply-attributes" signal is fired...
[~andy/gtk] / gtk / gtktreemenu.c
1 /* gtktreemenu.c
2  *
3  * Copyright (C) 2010 Openismus GmbH
4  *
5  * Authors:
6  *      Tristan Van Berkom <tristanvb@openismus.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include "config.h"
25 #include "gtkintl.h"
26 #include "gtktreemenu.h"
27 #include "gtkmarshalers.h"
28 #include "gtkmenuitem.h"
29 #include "gtktearoffmenuitem.h"
30 #include "gtkseparatormenuitem.h"
31 #include "gtkcellareabox.h"
32 #include "gtkcellareacontext.h"
33 #include "gtkcelllayout.h"
34 #include "gtkcellview.h"
35 #include "gtkprivate.h"
36
37
38 /* GObjectClass */
39 static GObject  *gtk_tree_menu_constructor                    (GType                  type,
40                                                                guint                  n_construct_properties,
41                                                                GObjectConstructParam *construct_properties);
42 static void      gtk_tree_menu_dispose                        (GObject            *object);
43 static void      gtk_tree_menu_finalize                       (GObject            *object);
44 static void      gtk_tree_menu_set_property                   (GObject            *object,
45                                                                guint               prop_id,
46                                                                const GValue       *value,
47                                                                GParamSpec         *pspec);
48 static void      gtk_tree_menu_get_property                   (GObject            *object,
49                                                                guint               prop_id,
50                                                                GValue             *value,
51                                                                GParamSpec         *pspec);
52
53 /* GtkWidgetClass */
54 static void      gtk_tree_menu_get_preferred_width            (GtkWidget           *widget,
55                                                                gint                *minimum_size,
56                                                                gint                *natural_size);
57 static void      gtk_tree_menu_get_preferred_height           (GtkWidget           *widget,
58                                                                gint                *minimum_size,
59                                                                gint                *natural_size);
60
61 /* GtkCellLayoutIface */
62 static void      gtk_tree_menu_cell_layout_init               (GtkCellLayoutIface  *iface);
63 static GtkCellArea *gtk_tree_menu_cell_layout_get_area        (GtkCellLayout        *layout);
64
65
66 /* TreeModel/DrawingArea callbacks and building menus/submenus */
67 static inline void rebuild_menu                               (GtkTreeMenu          *menu);
68 static gboolean   menu_occupied                               (GtkTreeMenu          *menu,
69                                                                guint                 left_attach,
70                                                                guint                 right_attach,
71                                                                guint                 top_attach,
72                                                                guint                 bottom_attach);
73 static void       relayout_item                               (GtkTreeMenu          *menu,
74                                                                GtkWidget            *item,
75                                                                GtkTreeIter          *iter,
76                                                                GtkWidget            *prev);
77 static void       gtk_tree_menu_populate                      (GtkTreeMenu          *menu);
78 static GtkWidget *gtk_tree_menu_create_item                   (GtkTreeMenu          *menu,
79                                                                GtkTreeIter          *iter,
80                                                                gboolean              header_item);
81 static void       gtk_tree_menu_set_area                      (GtkTreeMenu          *menu,
82                                                                GtkCellArea          *area);
83 static GtkWidget *gtk_tree_menu_get_path_item                 (GtkTreeMenu          *menu,
84                                                                GtkTreePath          *path);
85 static gboolean   gtk_tree_menu_path_in_menu                  (GtkTreeMenu          *menu,
86                                                                GtkTreePath          *path,
87                                                                gboolean             *header_item);
88 static void       row_inserted_cb                             (GtkTreeModel         *model,
89                                                                GtkTreePath          *path,
90                                                                GtkTreeIter          *iter,
91                                                                GtkTreeMenu          *menu);
92 static void       row_deleted_cb                              (GtkTreeModel         *model,
93                                                                GtkTreePath          *path,
94                                                                GtkTreeMenu          *menu);
95 static void       row_reordered_cb                            (GtkTreeModel         *model,
96                                                                GtkTreePath          *path,
97                                                                GtkTreeIter          *iter,
98                                                                gint                 *new_order,
99                                                                GtkTreeMenu          *menu);
100 static void       row_changed_cb                              (GtkTreeModel         *model,
101                                                                GtkTreePath          *path,
102                                                                GtkTreeIter          *iter,
103                                                                GtkTreeMenu          *menu);
104 static void       context_size_changed_cb                     (GtkCellAreaContext   *context,
105                                                                GParamSpec           *pspec,
106                                                                GtkWidget            *menu);
107 static void       area_apply_attributes_cb                    (GtkCellArea          *area,
108                                                                GtkTreeModel         *tree_model,
109                                                                GtkTreeIter          *iter,
110                                                                gboolean              is_expander,
111                                                                gboolean              is_expanded,
112                                                                GtkTreeMenu          *menu);
113 static void       item_activated_cb                           (GtkMenuItem          *item,
114                                                                GtkTreeMenu          *menu);
115 static void       submenu_activated_cb                        (GtkTreeMenu          *submenu,
116                                                                const gchar          *path,
117                                                                GtkTreeMenu          *menu);
118
119
120
121 struct _GtkTreeMenuPrivate
122 {
123   /* TreeModel and parent for this menu */
124   GtkTreeModel        *model;
125   GtkTreeRowReference *root;
126
127   /* CellArea and context for this menu */
128   GtkCellArea         *area;
129   GtkCellAreaContext  *context;
130   
131   /* Signals */
132   gulong               size_changed_id;
133   gulong               apply_attributes_id;
134   gulong               row_inserted_id;
135   gulong               row_deleted_id;
136   gulong               row_reordered_id;
137   gulong               row_changed_id;
138
139   /* Grid menu mode */
140   gint                 wrap_width;
141   gint                 row_span_col;
142   gint                 col_span_col;
143
144   /* Flags */
145   guint32              menu_with_header : 1;
146   guint32              tearoff     : 1;
147
148   /* Row separators */
149   GtkTreeViewRowSeparatorFunc row_separator_func;
150   gpointer                    row_separator_data;
151   GDestroyNotify              row_separator_destroy;
152
153   /* Submenu headers */
154   GtkTreeMenuHeaderFunc header_func;
155   gpointer              header_data;
156   GDestroyNotify        header_destroy;
157 };
158
159 enum {
160   PROP_0,
161   PROP_MODEL,
162   PROP_ROOT,
163   PROP_CELL_AREA,
164   PROP_TEAROFF,
165   PROP_WRAP_WIDTH,
166   PROP_ROW_SPAN_COL,
167   PROP_COL_SPAN_COL
168 };
169
170 enum {
171   SIGNAL_MENU_ACTIVATE,
172   N_SIGNALS
173 };
174
175 static guint   tree_menu_signals[N_SIGNALS] = { 0 };
176 static GQuark  tree_menu_path_quark = 0;
177
178 G_DEFINE_TYPE_WITH_CODE (GtkTreeMenu, gtk_tree_menu, GTK_TYPE_MENU,
179                          G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
180                                                 gtk_tree_menu_cell_layout_init));
181
182 static void
183 gtk_tree_menu_init (GtkTreeMenu *menu)
184 {
185   GtkTreeMenuPrivate *priv;
186
187   menu->priv = G_TYPE_INSTANCE_GET_PRIVATE (menu,
188                                             GTK_TYPE_TREE_MENU,
189                                             GtkTreeMenuPrivate);
190   priv = menu->priv;
191
192   priv->model     = NULL;
193   priv->root      = NULL;
194   priv->area      = NULL;
195   priv->context   = NULL;
196
197   priv->size_changed_id  = 0;
198   priv->row_inserted_id  = 0;
199   priv->row_deleted_id   = 0;
200   priv->row_reordered_id = 0;
201   priv->row_changed_id   = 0;
202
203   priv->wrap_width   = 0;
204   priv->row_span_col = -1;
205   priv->col_span_col = -1;
206
207   priv->menu_with_header = FALSE;
208   priv->tearoff          = FALSE;
209
210   priv->row_separator_func    = NULL;
211   priv->row_separator_data    = NULL;
212   priv->row_separator_destroy = NULL;
213
214   priv->header_func    = NULL;
215   priv->header_data    = NULL;
216   priv->header_destroy = NULL;
217
218   gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
219 }
220
221 static void 
222 gtk_tree_menu_class_init (GtkTreeMenuClass *class)
223 {
224   GObjectClass   *object_class = G_OBJECT_CLASS (class);
225   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
226
227   tree_menu_path_quark = g_quark_from_static_string ("gtk-tree-menu-path");
228
229   object_class->constructor  = gtk_tree_menu_constructor;
230   object_class->dispose      = gtk_tree_menu_dispose;
231   object_class->finalize     = gtk_tree_menu_finalize;
232   object_class->set_property = gtk_tree_menu_set_property;
233   object_class->get_property = gtk_tree_menu_get_property;
234
235   widget_class->get_preferred_width  = gtk_tree_menu_get_preferred_width;
236   widget_class->get_preferred_height = gtk_tree_menu_get_preferred_height;
237
238   tree_menu_signals[SIGNAL_MENU_ACTIVATE] =
239     g_signal_new (I_("menu-activate"),
240                   G_OBJECT_CLASS_TYPE (object_class),
241                   G_SIGNAL_RUN_FIRST,
242                   0, /* No class closure here */
243                   NULL, NULL,
244                   _gtk_marshal_VOID__STRING,
245                   G_TYPE_NONE, 1, G_TYPE_STRING);
246
247   g_object_class_install_property (object_class,
248                                    PROP_MODEL,
249                                    g_param_spec_object ("model",
250                                                         P_("TreeMenu model"),
251                                                         P_("The model for the tree menu"),
252                                                         GTK_TYPE_TREE_MODEL,
253                                                         GTK_PARAM_READWRITE));
254
255   g_object_class_install_property (object_class,
256                                    PROP_ROOT,
257                                    g_param_spec_boxed ("root",
258                                                        P_("TreeMenu root row"),
259                                                        P_("The TreeMenu will display children of the "
260                                                           "specified root"),
261                                                        GTK_TYPE_TREE_PATH,
262                                                        GTK_PARAM_READWRITE));
263
264   g_object_class_install_property (object_class,
265                                    PROP_CELL_AREA,
266                                    g_param_spec_object ("cell-area",
267                                                         P_("Cell Area"),
268                                                         P_("The GtkCellArea used to layout cells"),
269                                                         GTK_TYPE_CELL_AREA,
270                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
271
272   g_object_class_install_property (object_class,
273                                    PROP_TEAROFF,
274                                    g_param_spec_boolean ("tearoff",
275                                                          P_("Tearoff"),
276                                                          P_("Whether the menu has a tearoff item"),
277                                                          FALSE,
278                                                          GTK_PARAM_READWRITE));
279
280   /**
281    * GtkTreeMenu:wrap-width:
282    *
283    * If wrap-width is set to a positive value, the list will be
284    * displayed in multiple columns, the number of columns is
285    * determined by wrap-width.
286    *
287    * Since: 3.0
288    */
289   g_object_class_install_property (object_class,
290                                    PROP_WRAP_WIDTH,
291                                    g_param_spec_int ("wrap-width",
292                                                      P_("Wrap Width"),
293                                                      P_("Wrap width for laying out items in a grid"),
294                                                      0,
295                                                      G_MAXINT,
296                                                      0,
297                                                      GTK_PARAM_READWRITE));
298
299   /**
300    * GtkTreeMenu:row-span-column:
301    *
302    * If this is set to a non-negative value, it must be the index of a column 
303    * of type %G_TYPE_INT in the model. 
304    *
305    * The values of that column are used to determine how many rows a value in 
306    * the list will span. Therefore, the values in the model column pointed to 
307    * by this property must be greater than zero and not larger than wrap-width.
308    *
309    * Since: 3.0
310    */
311   g_object_class_install_property (object_class,
312                                    PROP_ROW_SPAN_COL,
313                                    g_param_spec_int ("row-span-column",
314                                                      P_("Row span column"),
315                                                      P_("TreeModel column containing the row span values"),
316                                                      -1,
317                                                      G_MAXINT,
318                                                      -1,
319                                                      GTK_PARAM_READWRITE));
320
321   /**
322    * GtkTreeMenu:column-span-column:
323    *
324    * If this is set to a non-negative value, it must be the index of a column 
325    * of type %G_TYPE_INT in the model. 
326    *
327    * The values of that column are used to determine how many columns a value 
328    * in the list will span. 
329    *
330    * Since: 3.0
331    */
332   g_object_class_install_property (object_class,
333                                    PROP_COL_SPAN_COL,
334                                    g_param_spec_int ("column-span-column",
335                                                      P_("Column span column"),
336                                                      P_("TreeModel column containing the column span values"),
337                                                      -1,
338                                                      G_MAXINT,
339                                                      -1,
340                                                      GTK_PARAM_READWRITE));
341
342   g_type_class_add_private (object_class, sizeof (GtkTreeMenuPrivate));
343 }
344
345 /****************************************************************
346  *                         GObjectClass                         *
347  ****************************************************************/
348 static GObject  *
349 gtk_tree_menu_constructor (GType                  type,
350                            guint                  n_construct_properties,
351                            GObjectConstructParam *construct_properties)
352 {
353   GObject            *object;
354   GtkTreeMenu        *menu;
355   GtkTreeMenuPrivate *priv;
356
357   object = G_OBJECT_CLASS (gtk_tree_menu_parent_class)->constructor
358     (type, n_construct_properties, construct_properties);
359
360   menu = GTK_TREE_MENU (object);
361   priv = menu->priv;
362
363   if (!priv->area)
364     {
365       GtkCellArea *area = gtk_cell_area_box_new ();
366
367       gtk_tree_menu_set_area (menu, area);
368     }
369
370   priv->context = gtk_cell_area_create_context (priv->area);
371
372   priv->size_changed_id = 
373     g_signal_connect (priv->context, "notify",
374                       G_CALLBACK (context_size_changed_cb), menu);
375
376   return object;
377 }
378
379 static void
380 gtk_tree_menu_dispose (GObject *object)
381 {
382   GtkTreeMenu        *menu;
383   GtkTreeMenuPrivate *priv;
384
385   menu = GTK_TREE_MENU (object);
386   priv = menu->priv;
387
388   gtk_tree_menu_set_model (menu, NULL);
389   gtk_tree_menu_set_area (menu, NULL);
390
391   if (priv->context)
392     {
393       /* Disconnect signals */
394       g_signal_handler_disconnect (priv->context, priv->size_changed_id);
395
396       g_object_unref (priv->context);
397       priv->context = NULL;
398       priv->size_changed_id = 0;
399     }
400
401   G_OBJECT_CLASS (gtk_tree_menu_parent_class)->dispose (object);
402 }
403
404 static void
405 gtk_tree_menu_finalize (GObject *object)
406 {
407   GtkTreeMenu        *menu;
408   GtkTreeMenuPrivate *priv;
409
410   menu = GTK_TREE_MENU (object);
411   priv = menu->priv;
412
413   gtk_tree_menu_set_row_separator_func (menu, NULL, NULL, NULL);
414   gtk_tree_menu_set_header_func (menu, NULL, NULL, NULL);
415
416   if (priv->root) 
417     gtk_tree_row_reference_free (priv->root);
418
419   G_OBJECT_CLASS (gtk_tree_menu_parent_class)->finalize (object);
420 }
421
422 static void
423 gtk_tree_menu_set_property (GObject            *object,
424                             guint               prop_id,
425                             const GValue       *value,
426                             GParamSpec         *pspec)
427 {
428   GtkTreeMenu *menu = GTK_TREE_MENU (object);
429
430   switch (prop_id)
431     {
432     case PROP_MODEL:
433       gtk_tree_menu_set_model (menu, g_value_get_object (value));
434       break;
435
436     case PROP_ROOT:
437       gtk_tree_menu_set_root (menu, g_value_get_boxed (value));
438       break;
439
440     case PROP_CELL_AREA:
441       /* Construct-only, can only be assigned once */
442       gtk_tree_menu_set_area (menu, (GtkCellArea *)g_value_get_object (value));
443       break;
444
445     case PROP_TEAROFF:
446       gtk_tree_menu_set_tearoff (menu, g_value_get_boolean (value));
447       break;
448
449     case PROP_WRAP_WIDTH:
450       gtk_tree_menu_set_wrap_width (menu, g_value_get_int (value));
451       break;
452
453      case PROP_ROW_SPAN_COL:
454       gtk_tree_menu_set_row_span_column (menu, g_value_get_int (value));
455       break;
456
457      case PROP_COL_SPAN_COL:
458       gtk_tree_menu_set_column_span_column (menu, g_value_get_int (value));
459       break;
460
461     default:
462       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
463       break;
464     }
465 }
466
467 static void
468 gtk_tree_menu_get_property (GObject            *object,
469                             guint               prop_id,
470                             GValue             *value,
471                             GParamSpec         *pspec)
472 {
473   GtkTreeMenu        *menu = GTK_TREE_MENU (object);
474   GtkTreeMenuPrivate *priv = menu->priv;
475
476   switch (prop_id)
477     {
478     case PROP_MODEL:
479       g_value_set_object (value, priv->model);
480       break;
481       
482     case PROP_ROOT:
483       g_value_set_boxed (value, priv->root);
484       break;
485       
486     case PROP_CELL_AREA:
487       g_value_set_object (value, priv->area);
488       break;
489
490     case PROP_TEAROFF:
491       g_value_set_boolean (value, priv->tearoff);
492       break;
493
494     default:
495       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
496       break;
497     }
498 }
499
500 /****************************************************************
501  *                         GtkWidgetClass                       *
502  ****************************************************************/
503
504 /* We tell all the menu items to reserve space for the submenu
505  * indicator if there is at least one submenu, this way we ensure
506  * that every internal cell area gets allocated the
507  * same width (and requested height for the same appropriate width).
508  */
509 static void
510 sync_reserve_submenu_size (GtkTreeMenu *menu)
511 {
512   GList              *children, *l;
513   gboolean            has_submenu = FALSE;
514
515   children = gtk_container_get_children (GTK_CONTAINER (menu));
516   for (l = children; l; l = l->next)
517     {
518       GtkMenuItem *item = l->data;
519
520       if (gtk_menu_item_get_submenu (item) != NULL)
521         {
522           has_submenu = TRUE;
523           break;
524         }
525     }
526
527   for (l = children; l; l = l->next)
528     {
529       GtkMenuItem *item = l->data;
530
531       gtk_menu_item_set_reserve_indicator (item, has_submenu);
532     }
533
534   g_list_free (children);
535 }
536
537 static void
538 gtk_tree_menu_get_preferred_width (GtkWidget           *widget,
539                                    gint                *minimum_size,
540                                    gint                *natural_size)
541 {
542   GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
543   GtkTreeMenuPrivate *priv = menu->priv;
544
545   /* We leave the requesting work up to the cellviews which operate in the same
546    * context, reserving space for the submenu indicator if any of the items have
547    * submenus ensures that every cellview will receive the same allocated width.
548    *
549    * Since GtkMenu does hieght-for-width correctly, we know that the width of
550    * every cell will be requested before the height-for-widths are requested.
551    */
552   g_signal_handler_block (priv->context, priv->size_changed_id);
553
554   sync_reserve_submenu_size (menu);
555
556   GTK_WIDGET_CLASS (gtk_tree_menu_parent_class)->get_preferred_width (widget, minimum_size, natural_size);
557
558   g_signal_handler_unblock (priv->context, priv->size_changed_id);
559 }
560
561 static void
562 gtk_tree_menu_get_preferred_height (GtkWidget           *widget,
563                                     gint                *minimum_size,
564                                     gint                *natural_size)
565 {
566   GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
567   GtkTreeMenuPrivate *priv = menu->priv;
568
569   g_signal_handler_block (priv->context, priv->size_changed_id);
570
571   sync_reserve_submenu_size (menu);
572
573   GTK_WIDGET_CLASS (gtk_tree_menu_parent_class)->get_preferred_height (widget, minimum_size, natural_size);
574
575   g_signal_handler_unblock (priv->context, priv->size_changed_id);
576 }
577
578 /****************************************************************
579  *                      GtkCellLayoutIface                      *
580  ****************************************************************/
581 static void
582 gtk_tree_menu_cell_layout_init (GtkCellLayoutIface  *iface)
583 {
584   iface->get_area = gtk_tree_menu_cell_layout_get_area;
585 }
586
587 static GtkCellArea *
588 gtk_tree_menu_cell_layout_get_area (GtkCellLayout *layout)
589 {
590   GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
591   GtkTreeMenuPrivate *priv = menu->priv;
592
593   return priv->area;
594 }
595
596
597 /****************************************************************
598  *             TreeModel callbacks/populating menus             *
599  ****************************************************************/
600 static GtkWidget *
601 gtk_tree_menu_get_path_item (GtkTreeMenu          *menu,
602                              GtkTreePath          *search)
603 {
604   GtkWidget *item = NULL;
605   GList     *children, *l;
606
607   children = gtk_container_get_children (GTK_CONTAINER (menu));
608
609   for (l = children; item == NULL && l != NULL; l = l->next)
610     {
611       GtkWidget   *child = l->data;
612       GtkTreePath *path  = NULL;
613
614       if (GTK_IS_SEPARATOR_MENU_ITEM (child))
615         {
616           GtkTreeRowReference *row =
617             g_object_get_qdata (G_OBJECT (child), tree_menu_path_quark);
618
619           if (row && gtk_tree_row_reference_valid (row))
620             path = gtk_tree_row_reference_get_path (row);
621         }
622       else
623         {
624           GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
625
626           /* It's always a cellview */
627           if (GTK_IS_CELL_VIEW (view))
628             path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view));
629         }
630
631       if (path)
632         {
633           if (gtk_tree_path_compare (search, path) == 0)
634             item = child;
635
636           gtk_tree_path_free (path);
637         }
638     }
639
640   g_list_free (children);
641
642   return item;
643 }
644
645 static gboolean
646 gtk_tree_menu_path_in_menu (GtkTreeMenu  *menu,
647                             GtkTreePath  *path,
648                             gboolean     *header_item)
649 {
650   GtkTreeMenuPrivate *priv = menu->priv;
651   GtkTreePath        *parent_path = gtk_tree_path_copy (path);
652   gboolean            in_menu = FALSE;
653   gboolean            is_header = FALSE;
654
655   /* Check if the is in root of the model */
656   if (gtk_tree_path_get_depth (parent_path) == 1)
657     {
658       if (!priv->root)
659         in_menu = TRUE;
660     }
661   /* If we are a submenu, compare the parent path */
662   else if (priv->root && gtk_tree_path_up (parent_path))
663     {
664       GtkTreePath *root_path =
665         gtk_tree_row_reference_get_path (priv->root);
666
667       if (gtk_tree_path_compare (root_path, parent_path) == 0)
668         in_menu = TRUE;
669
670       if (!in_menu && priv->menu_with_header && 
671           gtk_tree_path_compare (root_path, path) == 0)
672         {
673           in_menu   = TRUE;
674           is_header = TRUE;
675         }
676
677       gtk_tree_path_free (root_path);
678     }
679
680   gtk_tree_path_free (parent_path);
681
682   if (header_item)
683     *header_item = is_header;
684
685   return in_menu;
686 }
687
688 static void
689 row_inserted_cb (GtkTreeModel     *model,
690                  GtkTreePath      *path,
691                  GtkTreeIter      *iter,
692                  GtkTreeMenu      *menu)
693 {
694   GtkTreeMenuPrivate *priv = menu->priv;
695   gint               *indices, index, depth;
696
697   /* If the iter should be in this menu then go ahead and insert it */
698   if (gtk_tree_menu_path_in_menu (menu, path, NULL))
699     {
700       GtkWidget *item;
701
702       if (priv->wrap_width > 0)
703         rebuild_menu (menu);
704       else
705         {
706           /* Get the index of the path for this depth */
707           indices = gtk_tree_path_get_indices (path);
708           depth   = gtk_tree_path_get_depth (path);
709           index   = indices[depth -1];
710           
711           /* Menus with a header include a menuitem for it's root node
712            * and a separator menu item */
713           if (priv->menu_with_header)
714             index += 2;
715           
716           /* Index after the tearoff item for the root menu if
717            * there is a tearoff item
718            */
719           if (priv->root == NULL && priv->tearoff)
720             index += 1;
721           
722           item = gtk_tree_menu_create_item (menu, iter, FALSE);
723           gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, index);
724           
725           /* Resize everything */
726           gtk_cell_area_context_flush (menu->priv->context);
727         }
728     }
729 }
730
731 static void
732 row_deleted_cb (GtkTreeModel     *model,
733                 GtkTreePath      *path,
734                 GtkTreeMenu      *menu)
735 {
736   GtkTreeMenuPrivate *priv = menu->priv;
737   GtkWidget          *item;
738   gboolean            header_item;
739   gboolean            in_menu;
740
741   in_menu = gtk_tree_menu_path_in_menu (menu, path, &header_item);
742
743   /* If it's the header item we leave it to the parent menu 
744    * to remove us from its menu
745    */
746   if (!header_item && in_menu)
747     {
748       item = gtk_tree_menu_get_path_item (menu, path);
749       if (item)
750         {
751           if (priv->wrap_width > 0)
752             rebuild_menu (menu);
753           else
754             {
755               /* Get rid of the deleted item */
756               gtk_widget_destroy (item);
757               
758               /* Resize everything */
759               gtk_cell_area_context_flush (menu->priv->context);
760             }
761         }
762     }
763 }
764
765 static void
766 row_reordered_cb (GtkTreeModel    *model,
767                   GtkTreePath     *path,
768                   GtkTreeIter     *iter,
769                   gint            *new_order,
770                   GtkTreeMenu     *menu)
771 {
772   GtkTreeMenuPrivate *priv = menu->priv;
773   gboolean            this_menu = FALSE;
774
775   if (path == NULL && priv->root == NULL)
776     this_menu = TRUE;
777   else if (priv->root)
778     {
779       GtkTreePath *root_path =
780         gtk_tree_row_reference_get_path (priv->root);
781
782       if (gtk_tree_path_compare (root_path, path) == 0)
783         this_menu = TRUE;
784
785       gtk_tree_path_free (root_path);
786     }
787
788   if (this_menu)
789     rebuild_menu (menu);
790 }
791
792 static gint 
793 menu_item_position (GtkTreeMenu *menu,
794                     GtkWidget   *item)
795 {
796   GList *children, *l;
797   gint   position;
798
799   children = gtk_container_get_children (GTK_CONTAINER (menu));
800   for (position = 0, l = children; l; position++, l = l->next)
801     {
802       GtkWidget *iitem = l->data;
803
804       if (item == iitem)
805         break;
806     }
807
808   g_list_free (children);
809
810   return position;
811 }
812
813 static void
814 row_changed_cb (GtkTreeModel         *model,
815                 GtkTreePath          *path,
816                 GtkTreeIter          *iter,
817                 GtkTreeMenu          *menu)
818 {
819   GtkTreeMenuPrivate *priv = menu->priv;
820   gboolean            is_separator = FALSE;
821   gboolean            has_header = FALSE;
822   GtkWidget          *item;
823
824   item = gtk_tree_menu_get_path_item (menu, path);
825
826   if (priv->root)
827     {
828       GtkTreePath *root_path =
829         gtk_tree_row_reference_get_path (priv->root);
830       
831       if (gtk_tree_path_compare (root_path, path) == 0)
832         {
833           if (priv->header_func)
834             has_header = 
835               priv->header_func (priv->model, iter, priv->header_data);
836           
837           if (has_header && !item)
838             {
839               item = gtk_separator_menu_item_new ();
840               gtk_widget_show (item);
841               gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
842
843               item = gtk_tree_menu_create_item (menu, iter, TRUE);
844               gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
845
846               priv->menu_with_header = TRUE;
847             }
848           else if (!has_header && item)
849             {
850               /* Destroy the header item and then the following separator */
851               gtk_widget_destroy (item);
852               gtk_widget_destroy (GTK_MENU_SHELL (menu)->children->data);
853
854               priv->menu_with_header = FALSE;
855             }
856         }
857       
858       gtk_tree_path_free (root_path);
859     }
860   
861   if (item)
862     {
863       if (priv->wrap_width > 0)
864         /* Ugly, we need to rebuild the menu here if
865          * the row-span/row-column values change 
866          */
867         rebuild_menu (menu);
868       else
869         {
870           if (priv->row_separator_func)
871             is_separator = 
872               priv->row_separator_func (model, iter,
873                                         priv->row_separator_data);
874
875
876           if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
877             {
878               gint position = menu_item_position (menu, item);
879
880               gtk_widget_destroy (item);
881               item = gtk_tree_menu_create_item (menu, iter, FALSE);
882               gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, position);
883             }
884         }
885     }
886 }
887
888 static void
889 context_size_changed_cb (GtkCellAreaContext  *context,
890                          GParamSpec          *pspec,
891                          GtkWidget           *menu)
892 {
893   if (!strcmp (pspec->name, "minimum-width") ||
894       !strcmp (pspec->name, "natural-width") ||
895       !strcmp (pspec->name, "minimum-height") ||
896       !strcmp (pspec->name, "natural-height"))
897     gtk_widget_queue_resize (menu);
898 }
899
900 static gboolean
901 area_is_sensitive (GtkCellArea *area)
902 {
903   GList    *cells, *list;
904   gboolean  sensitive = FALSE;
905   
906   cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
907
908   for (list = cells; list; list = list->next)
909     {
910       g_object_get (list->data, "sensitive", &sensitive, NULL);
911       
912       if (sensitive)
913         break;
914     }
915   g_list_free (cells);
916
917   return sensitive;
918 }
919
920 static void
921 area_apply_attributes_cb (GtkCellArea          *area,
922                           GtkTreeModel         *tree_model,
923                           GtkTreeIter          *iter,
924                           gboolean              is_expander,
925                           gboolean              is_expanded,
926                           GtkTreeMenu          *menu)
927 {
928   /* If the menu for this iter has a submenu */
929   GtkTreeMenuPrivate *priv = menu->priv;
930   GtkTreePath        *path;
931   GtkWidget          *item;
932   gboolean            is_header;
933   gboolean            sensitive;
934
935   path = gtk_tree_model_get_path (tree_model, iter);
936
937   if (gtk_tree_menu_path_in_menu (menu, path, &is_header))
938     {
939       item = gtk_tree_menu_get_path_item (menu, path);
940
941       /* If there is no submenu, go ahead and update item sensitivity,
942        * items with submenus are always sensitive */
943       if (!gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)))
944         {
945           sensitive = area_is_sensitive (priv->area);
946
947           gtk_widget_set_sensitive (item, sensitive);
948
949           if (is_header)
950             {
951               /* For header items we need to set the sensitivity
952                * of the following separator item 
953                */
954               if (GTK_MENU_SHELL (menu)->children && 
955                   GTK_MENU_SHELL (menu)->children->next)
956                 {
957                   GtkWidget *separator = 
958                     GTK_MENU_SHELL (menu)->children->next->data;
959
960                   gtk_widget_set_sensitive (separator, sensitive);
961                 }
962             }
963         }
964     }
965
966   gtk_tree_path_free (path);
967 }
968
969 static void
970 gtk_tree_menu_set_area (GtkTreeMenu *menu,
971                         GtkCellArea *area)
972 {
973   GtkTreeMenuPrivate *priv = menu->priv;
974
975   if (priv->area)
976     {
977       g_signal_handler_disconnect (priv->area,
978                                    priv->apply_attributes_id);
979       priv->apply_attributes_id = 0;
980
981       g_object_unref (priv->area);
982     }
983
984   priv->area = area;
985
986   if (priv->area)
987     {
988       g_object_ref_sink (priv->area);
989
990       priv->apply_attributes_id =
991         g_signal_connect (priv->area, "apply-attributes",
992                           G_CALLBACK (area_apply_attributes_cb), menu);
993     }
994 }
995
996 static gboolean
997 menu_occupied (GtkTreeMenu *menu,
998                guint        left_attach,
999                guint        right_attach,
1000                guint        top_attach,
1001                guint        bottom_attach)
1002 {
1003   GList *i;
1004
1005   for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
1006     {
1007       guint l, r, b, t;
1008
1009       gtk_container_child_get (GTK_CONTAINER (menu), 
1010                                i->data,
1011                                "left-attach", &l,
1012                                "right-attach", &r,
1013                                "bottom-attach", &b,
1014                                "top-attach", &t,
1015                                NULL);
1016
1017       /* look if this item intersects with the given coordinates */
1018       if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
1019         return TRUE;
1020     }
1021
1022   return FALSE;
1023 }
1024
1025 static void
1026 relayout_item (GtkTreeMenu *menu,
1027                GtkWidget   *item,
1028                GtkTreeIter *iter,
1029                GtkWidget   *prev)
1030 {
1031   GtkTreeMenuPrivate *priv = menu->priv;
1032   gint                current_col = 0, current_row = 0;
1033   gint                rows = 1, cols = 1;
1034   
1035   if (priv->col_span_col == -1 &&
1036       priv->row_span_col == -1 &&
1037       prev)
1038     {
1039       gtk_container_child_get (GTK_CONTAINER (menu), prev,
1040                                "right-attach", &current_col,
1041                                "top-attach", &current_row,
1042                                NULL);
1043       if (current_col + cols > priv->wrap_width)
1044         {
1045           current_col = 0;
1046           current_row++;
1047         }
1048     }
1049   else
1050     {
1051       if (priv->col_span_col != -1)
1052         gtk_tree_model_get (priv->model, iter,
1053                             priv->col_span_col, &cols,
1054                             -1);
1055       if (priv->row_span_col != -1)
1056         gtk_tree_model_get (priv->model, iter,
1057                             priv->row_span_col, &rows,
1058                             -1);
1059
1060       while (1)
1061         {
1062           if (current_col + cols > priv->wrap_width)
1063             {
1064               current_col = 0;
1065               current_row++;
1066             }
1067           
1068           if (!menu_occupied (menu, 
1069                               current_col, current_col + cols,
1070                               current_row, current_row + rows))
1071             break;
1072
1073           current_col++;
1074         }
1075     }
1076
1077   /* set attach props */
1078   gtk_menu_attach (GTK_MENU (menu), item,
1079                    current_col, current_col + cols,
1080                    current_row, current_row + rows);
1081 }
1082
1083 static GtkWidget *
1084 gtk_tree_menu_create_item (GtkTreeMenu *menu,
1085                            GtkTreeIter *iter,
1086                            gboolean     header_item)
1087 {
1088   GtkTreeMenuPrivate *priv = menu->priv;
1089   GtkWidget          *item, *view;
1090   GtkTreePath        *path;
1091   gboolean            is_separator = FALSE;
1092
1093   path = gtk_tree_model_get_path (priv->model, iter);
1094
1095   if (priv->row_separator_func)
1096     is_separator = 
1097       priv->row_separator_func (priv->model, iter,
1098                                 priv->row_separator_data);
1099
1100   if (is_separator)
1101     {
1102       item = gtk_separator_menu_item_new ();
1103
1104       g_object_set_qdata_full (G_OBJECT (item),
1105                                tree_menu_path_quark,
1106                                gtk_tree_row_reference_new (priv->model, path),
1107                                (GDestroyNotify)gtk_tree_row_reference_free);
1108     }
1109   else
1110     {
1111       view = gtk_cell_view_new_with_context (priv->area, priv->context);
1112       item = gtk_menu_item_new ();
1113       gtk_widget_show (view);
1114       gtk_widget_show (item);
1115       
1116       gtk_cell_view_set_model (GTK_CELL_VIEW (view), priv->model);
1117       gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (view), path);
1118       
1119       gtk_widget_show (view);
1120       gtk_container_add (GTK_CONTAINER (item), view);
1121       
1122       g_signal_connect (item, "activate", G_CALLBACK (item_activated_cb), menu);
1123
1124       /* Add a GtkTreeMenu submenu to render the children of this row */
1125       if (header_item == FALSE &&
1126           gtk_tree_model_iter_has_child (priv->model, iter))
1127         {
1128           GtkWidget           *submenu;
1129
1130           submenu = gtk_tree_menu_new_with_area (priv->area);
1131
1132           gtk_tree_menu_set_row_separator_func (GTK_TREE_MENU (submenu), 
1133                                                 priv->row_separator_func,
1134                                                 priv->row_separator_data,
1135                                                 priv->row_separator_destroy);
1136           gtk_tree_menu_set_header_func (GTK_TREE_MENU (submenu), 
1137                                          priv->header_func,
1138                                          priv->header_data,
1139                                          priv->header_destroy);
1140
1141           gtk_tree_menu_set_model (GTK_TREE_MENU (submenu), priv->model);
1142           gtk_tree_menu_set_root (GTK_TREE_MENU (submenu), path);
1143
1144           gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
1145
1146           g_signal_connect (submenu, "menu-activate", 
1147                             G_CALLBACK (submenu_activated_cb), menu);
1148         }
1149     }
1150
1151   gtk_tree_path_free (path);
1152
1153   return item;
1154 }
1155
1156 static inline void 
1157 rebuild_menu (GtkTreeMenu *menu)
1158 {
1159   GtkTreeMenuPrivate *priv = menu->priv;
1160
1161   /* Destroy all the menu items */
1162   gtk_container_foreach (GTK_CONTAINER (menu), 
1163                          (GtkCallback) gtk_widget_destroy, NULL);
1164   
1165   /* Populate */
1166   if (priv->model)
1167     gtk_tree_menu_populate (menu);
1168 }
1169
1170
1171 static void
1172 gtk_tree_menu_populate (GtkTreeMenu *menu)
1173 {
1174   GtkTreeMenuPrivate *priv = menu->priv;
1175   GtkTreePath        *path = NULL;
1176   GtkTreeIter         parent;
1177   GtkTreeIter         iter;
1178   gboolean            valid = FALSE;
1179   GtkWidget          *menu_item, *prev = NULL;
1180
1181   if (!priv->model)
1182     return;
1183
1184   if (priv->root)
1185     path = gtk_tree_row_reference_get_path (priv->root);
1186
1187   if (path)
1188     {
1189       if (gtk_tree_model_get_iter (priv->model, &parent, path))
1190         {
1191           valid = gtk_tree_model_iter_children (priv->model, &iter, &parent);
1192
1193           if (priv->header_func && 
1194               priv->header_func (priv->model, &parent, priv->header_data))
1195             {
1196               /* Add a submenu header for rows which desire one, used for
1197                * combo boxes to allow all rows to be activatable/selectable 
1198                */
1199               menu_item = gtk_tree_menu_create_item (menu, &parent, TRUE);
1200               gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1201               
1202               menu_item = gtk_separator_menu_item_new ();
1203               gtk_widget_show (menu_item);
1204               gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1205
1206               prev = menu_item;
1207               priv->menu_with_header = TRUE;
1208             }
1209         }
1210       gtk_tree_path_free (path);
1211     }
1212   else
1213     {
1214       /* Tearoff menu items only go in the root menu */
1215       if (priv->tearoff)
1216         {
1217           menu_item = gtk_tearoff_menu_item_new ();
1218           gtk_widget_show (menu_item);
1219
1220           if (priv->wrap_width > 0)
1221             gtk_menu_attach (GTK_MENU (menu), menu_item, 0, priv->wrap_width, 0, 1);
1222           else
1223             gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1224
1225           prev = menu_item;
1226         }
1227
1228       valid = gtk_tree_model_iter_children (priv->model, &iter, NULL);
1229     }
1230
1231   /* Create a menu item for every row at the current depth, add a GtkTreeMenu
1232    * submenu for iters/items that have children */
1233   while (valid)
1234     {
1235       menu_item = gtk_tree_menu_create_item (menu, &iter, FALSE);
1236
1237       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1238
1239       if (priv->wrap_width > 0)
1240         relayout_item (menu, menu_item, &iter, prev);
1241
1242       prev  = menu_item;
1243       valid = gtk_tree_model_iter_next (priv->model, &iter);
1244     }
1245 }
1246
1247 static void
1248 item_activated_cb (GtkMenuItem          *item,
1249                    GtkTreeMenu          *menu)
1250 {
1251   GtkCellView *view;
1252   GtkTreePath *path;
1253   gchar       *path_str;
1254
1255   /* Only activate leafs, not parents */
1256   if (!gtk_menu_item_get_submenu (item))
1257     {
1258       view     = GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)));
1259       path     = gtk_cell_view_get_displayed_row (view);
1260       path_str = gtk_tree_path_to_string (path);
1261       
1262       g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path_str);
1263       
1264       g_free (path_str);
1265       gtk_tree_path_free (path);
1266     }
1267 }
1268
1269 static void
1270 submenu_activated_cb (GtkTreeMenu          *submenu,
1271                       const gchar          *path,
1272                       GtkTreeMenu          *menu)
1273 {
1274   g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path);
1275 }
1276
1277 /****************************************************************
1278  *                            API                               *
1279  ****************************************************************/
1280 GtkWidget *
1281 gtk_tree_menu_new (void)
1282 {
1283   return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU, NULL);
1284 }
1285
1286 GtkWidget *
1287 gtk_tree_menu_new_with_area (GtkCellArea    *area)
1288 {
1289   return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU, 
1290                                     "cell-area", area, 
1291                                     NULL);
1292 }
1293
1294 GtkWidget *
1295 gtk_tree_menu_new_full (GtkCellArea         *area,
1296                         GtkTreeModel        *model,
1297                         GtkTreePath         *root)
1298 {
1299   return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU, 
1300                                     "cell-area", area, 
1301                                     "model", model,
1302                                     "root", root,
1303                                     NULL);
1304 }
1305
1306 void
1307 gtk_tree_menu_set_model (GtkTreeMenu  *menu,
1308                          GtkTreeModel *model)
1309 {
1310   GtkTreeMenuPrivate *priv;
1311
1312   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1313   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
1314
1315   priv = menu->priv;
1316
1317   if (priv->model != model)
1318     {
1319       if (priv->model)
1320         {
1321           /* Disconnect signals */
1322           g_signal_handler_disconnect (priv->model,
1323                                        priv->row_inserted_id);
1324           g_signal_handler_disconnect (priv->model,
1325                                        priv->row_deleted_id);
1326           g_signal_handler_disconnect (priv->model,
1327                                        priv->row_reordered_id);
1328           g_signal_handler_disconnect (priv->model,
1329                                        priv->row_changed_id);
1330           priv->row_inserted_id  = 0;
1331           priv->row_deleted_id   = 0;
1332           priv->row_reordered_id = 0;
1333           priv->row_changed_id = 0;
1334
1335           g_object_unref (priv->model);
1336         }
1337
1338       priv->model = model;
1339
1340       if (priv->model)
1341         {
1342           g_object_ref (priv->model);
1343
1344           /* Connect signals */
1345           priv->row_inserted_id  = g_signal_connect (priv->model, "row-inserted",
1346                                                      G_CALLBACK (row_inserted_cb), menu);
1347           priv->row_deleted_id   = g_signal_connect (priv->model, "row-deleted",
1348                                                      G_CALLBACK (row_deleted_cb), menu);
1349           priv->row_reordered_id = g_signal_connect (priv->model, "rows-reordered",
1350                                                      G_CALLBACK (row_reordered_cb), menu);
1351           priv->row_changed_id   = g_signal_connect (priv->model, "row-changed",
1352                                                      G_CALLBACK (row_changed_cb), menu);
1353         }
1354     }
1355 }
1356
1357 GtkTreeModel *
1358 gtk_tree_menu_get_model (GtkTreeMenu *menu)
1359 {
1360   GtkTreeMenuPrivate *priv;
1361
1362   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1363
1364   priv = menu->priv;
1365
1366   return priv->model;
1367 }
1368
1369 void
1370 gtk_tree_menu_set_root (GtkTreeMenu         *menu,
1371                         GtkTreePath         *path)
1372 {
1373   GtkTreeMenuPrivate *priv;
1374
1375   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1376   g_return_if_fail (menu->priv->model != NULL || path == NULL);
1377
1378   priv = menu->priv;
1379
1380   if (priv->root) 
1381     gtk_tree_row_reference_free (priv->root);
1382
1383   if (path)
1384     priv->root = gtk_tree_row_reference_new (priv->model, path);
1385   else
1386     priv->root = NULL;
1387
1388   rebuild_menu (menu);
1389 }
1390
1391 GtkTreePath *
1392 gtk_tree_menu_get_root (GtkTreeMenu *menu)
1393 {
1394   GtkTreeMenuPrivate *priv;
1395
1396   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1397
1398   priv = menu->priv;
1399
1400   if (priv->root)
1401     return gtk_tree_row_reference_get_path (priv->root);
1402
1403   return NULL;
1404 }
1405
1406 gboolean
1407 gtk_tree_menu_get_tearoff (GtkTreeMenu *menu)
1408 {
1409   GtkTreeMenuPrivate *priv;
1410
1411   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1412
1413   priv = menu->priv;
1414
1415   return priv->tearoff;
1416 }
1417
1418 void
1419 gtk_tree_menu_set_tearoff (GtkTreeMenu *menu,
1420                            gboolean     tearoff)
1421 {
1422   GtkTreeMenuPrivate *priv;
1423
1424   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1425
1426   priv = menu->priv;
1427
1428   if (priv->tearoff != tearoff)
1429     {
1430       priv->tearoff = tearoff;
1431
1432       rebuild_menu (menu);
1433
1434       g_object_notify (G_OBJECT (menu), "tearoff");
1435     }
1436 }
1437
1438 gint
1439 gtk_tree_menu_get_wrap_width (GtkTreeMenu *menu)
1440 {
1441   GtkTreeMenuPrivate *priv;
1442
1443   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1444
1445   priv = menu->priv;
1446
1447   return priv->wrap_width;
1448 }
1449
1450 void
1451 gtk_tree_menu_set_wrap_width (GtkTreeMenu *menu,
1452                               gint         width)
1453 {
1454   GtkTreeMenuPrivate *priv;
1455
1456   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1457   g_return_if_fail (width >= 0);
1458
1459   priv = menu->priv;
1460
1461   if (priv->wrap_width != width)
1462     {
1463       priv->wrap_width = width;
1464
1465       rebuild_menu (menu);
1466
1467       g_object_notify (G_OBJECT (menu), "wrap-width");
1468     }
1469 }
1470
1471 gint
1472 gtk_tree_menu_get_row_span_column (GtkTreeMenu *menu)
1473 {
1474   GtkTreeMenuPrivate *priv;
1475
1476   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1477
1478   priv = menu->priv;
1479
1480   return priv->row_span_col;
1481 }
1482
1483 void
1484 gtk_tree_menu_set_row_span_column (GtkTreeMenu *menu,
1485                                    gint         row_span)
1486 {
1487   GtkTreeMenuPrivate *priv;
1488
1489   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1490
1491   priv = menu->priv;
1492
1493   if (priv->row_span_col != row_span)
1494     {
1495       priv->row_span_col = row_span;
1496
1497       if (priv->wrap_width > 0)
1498         rebuild_menu (menu);
1499
1500       g_object_notify (G_OBJECT (menu), "row-span-column");
1501     }
1502 }
1503
1504 gint
1505 gtk_tree_menu_get_column_span_column (GtkTreeMenu *menu)
1506 {
1507   GtkTreeMenuPrivate *priv;
1508
1509   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1510
1511   priv = menu->priv;
1512
1513   return priv->col_span_col;
1514 }
1515
1516 void
1517 gtk_tree_menu_set_column_span_column (GtkTreeMenu *menu,
1518                                       gint         column_span)
1519 {
1520   GtkTreeMenuPrivate *priv;
1521
1522   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1523
1524   priv = menu->priv;
1525
1526   if (priv->col_span_col != column_span)
1527     {
1528       priv->col_span_col = column_span;
1529
1530       if (priv->wrap_width > 0)
1531         rebuild_menu (menu);
1532
1533       g_object_notify (G_OBJECT (menu), "column-span-column");
1534     }
1535 }
1536
1537 GtkTreeViewRowSeparatorFunc
1538 gtk_tree_menu_get_row_separator_func (GtkTreeMenu *menu)
1539 {
1540   GtkTreeMenuPrivate *priv;
1541
1542   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1543
1544   priv = menu->priv;
1545
1546   return priv->row_separator_func;
1547 }
1548
1549 void
1550 gtk_tree_menu_set_row_separator_func (GtkTreeMenu          *menu,
1551                                       GtkTreeViewRowSeparatorFunc func,
1552                                       gpointer              data,
1553                                       GDestroyNotify        destroy)
1554 {
1555   GtkTreeMenuPrivate *priv;
1556
1557   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1558
1559   priv = menu->priv;
1560
1561   if (priv->row_separator_destroy)
1562     priv->row_separator_destroy (priv->row_separator_data);
1563
1564   priv->row_separator_func    = func;
1565   priv->row_separator_data    = data;
1566   priv->row_separator_destroy = destroy;
1567
1568   rebuild_menu (menu);
1569 }
1570
1571 GtkTreeMenuHeaderFunc
1572 gtk_tree_menu_get_header_func (GtkTreeMenu *menu)
1573 {
1574   GtkTreeMenuPrivate *priv;
1575
1576   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1577
1578   priv = menu->priv;
1579
1580   return priv->header_func;
1581 }
1582
1583 void
1584 gtk_tree_menu_set_header_func (GtkTreeMenu          *menu,
1585                                GtkTreeMenuHeaderFunc func,
1586                                gpointer              data,
1587                                GDestroyNotify        destroy)
1588 {
1589   GtkTreeMenuPrivate *priv;
1590
1591   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1592
1593   priv = menu->priv;
1594
1595   if (priv->header_destroy)
1596     priv->header_destroy (priv->header_data);
1597
1598   priv->header_func    = func;
1599   priv->header_data    = data;
1600   priv->header_destroy = destroy;
1601
1602   rebuild_menu (menu);
1603 }