]> Pileus Git - ~andy/gtk/blob - gtk/gtktreemenu.c
Move wholly deprecated files to a subdirectory
[~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  * Based on some GtkComboBox menu code by Kristian Rietveld <kris@gtk.org>
9  *
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.
14  *
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.
19  *
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.
24  */
25
26 /*
27  * SECTION:gtktreemenu
28  * @Short_Description: A GtkMenu automatically created from a #GtkTreeModel
29  * @Title: GtkTreeMenu
30  *
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
33  * menu.
34  */
35
36 #include "config.h"
37 #include "gtkintl.h"
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"
48
49 #include "deprecated/gtktearoffmenuitem.h"
50
51 /* GObjectClass */
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,
58                                                                guint               prop_id,
59                                                                const GValue       *value,
60                                                                GParamSpec         *pspec);
61 static void      gtk_tree_menu_get_property                   (GObject            *object,
62                                                                guint               prop_id,
63                                                                GValue             *value,
64                                                                GParamSpec         *pspec);
65
66 /* GtkWidgetClass */
67 static void      gtk_tree_menu_get_preferred_width            (GtkWidget           *widget,
68                                                                gint                *minimum_size,
69                                                                gint                *natural_size);
70 static void      gtk_tree_menu_get_preferred_height           (GtkWidget           *widget,
71                                                                gint                *minimum_size,
72                                                                gint                *natural_size);
73
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);
77
78
79 /* TreeModel/DrawingArea callbacks and building menus/submenus */
80 static inline void rebuild_menu                               (GtkTreeMenu          *menu);
81 static gboolean   menu_occupied                               (GtkTreeMenu          *menu,
82                                                                guint                 left_attach,
83                                                                guint                 right_attach,
84                                                                guint                 top_attach,
85                                                                guint                 bottom_attach);
86 static void       relayout_item                               (GtkTreeMenu          *menu,
87                                                                GtkWidget            *item,
88                                                                GtkTreeIter          *iter,
89                                                                GtkWidget            *prev);
90 static void       gtk_tree_menu_populate                      (GtkTreeMenu          *menu);
91 static GtkWidget *gtk_tree_menu_create_item                   (GtkTreeMenu          *menu,
92                                                                GtkTreeIter          *iter,
93                                                                gboolean              header_item);
94 static void       gtk_tree_menu_create_submenu                (GtkTreeMenu          *menu,
95                                                                GtkWidget            *item,
96                                                                GtkTreePath          *path);
97 static void       gtk_tree_menu_set_area                      (GtkTreeMenu          *menu,
98                                                                GtkCellArea          *area);
99 static GtkWidget *gtk_tree_menu_get_path_item                 (GtkTreeMenu          *menu,
100                                                                GtkTreePath          *path);
101 static gboolean   gtk_tree_menu_path_in_menu                  (GtkTreeMenu          *menu,
102                                                                GtkTreePath          *path,
103                                                                gboolean             *header_item);
104 static void       row_inserted_cb                             (GtkTreeModel         *model,
105                                                                GtkTreePath          *path,
106                                                                GtkTreeIter          *iter,
107                                                                GtkTreeMenu          *menu);
108 static void       row_deleted_cb                              (GtkTreeModel         *model,
109                                                                GtkTreePath          *path,
110                                                                GtkTreeMenu          *menu);
111 static void       row_reordered_cb                            (GtkTreeModel         *model,
112                                                                GtkTreePath          *path,
113                                                                GtkTreeIter          *iter,
114                                                                gint                 *new_order,
115                                                                GtkTreeMenu          *menu);
116 static void       row_changed_cb                              (GtkTreeModel         *model,
117                                                                GtkTreePath          *path,
118                                                                GtkTreeIter          *iter,
119                                                                GtkTreeMenu          *menu);
120 static void       context_size_changed_cb                     (GtkCellAreaContext   *context,
121                                                                GParamSpec           *pspec,
122                                                                GtkWidget            *menu);
123 static void       area_apply_attributes_cb                    (GtkCellArea          *area,
124                                                                GtkTreeModel         *tree_model,
125                                                                GtkTreeIter          *iter,
126                                                                gboolean              is_expander,
127                                                                gboolean              is_expanded,
128                                                                GtkTreeMenu          *menu);
129 static void       item_activated_cb                           (GtkMenuItem          *item,
130                                                                GtkTreeMenu          *menu);
131 static void       submenu_activated_cb                        (GtkTreeMenu          *submenu,
132                                                                const gchar          *path,
133                                                                GtkTreeMenu          *menu);
134 static void       gtk_tree_menu_set_model_internal            (GtkTreeMenu          *menu,
135                                                                GtkTreeModel         *model);
136
137
138
139 struct _GtkTreeMenuPrivate
140 {
141   /* TreeModel and parent for this menu */
142   GtkTreeModel        *model;
143   GtkTreeRowReference *root;
144
145   /* CellArea and context for this menu */
146   GtkCellArea         *area;
147   GtkCellAreaContext  *context;
148
149   /* Signals */
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;
156
157   /* Grid menu mode */
158   gint                 wrap_width;
159   gint                 row_span_col;
160   gint                 col_span_col;
161
162   /* Flags */
163   guint32              menu_with_header : 1;
164   guint32              tearoff     : 1;
165
166   /* Row separators */
167   GtkTreeViewRowSeparatorFunc row_separator_func;
168   gpointer                    row_separator_data;
169   GDestroyNotify              row_separator_destroy;
170
171   /* Submenu headers */
172   GtkTreeMenuHeaderFunc header_func;
173   gpointer              header_data;
174   GDestroyNotify        header_destroy;
175 };
176
177 enum {
178   PROP_0,
179   PROP_MODEL,
180   PROP_ROOT,
181   PROP_CELL_AREA,
182   PROP_TEAROFF,
183   PROP_WRAP_WIDTH,
184   PROP_ROW_SPAN_COL,
185   PROP_COL_SPAN_COL
186 };
187
188 enum {
189   SIGNAL_MENU_ACTIVATE,
190   N_SIGNALS
191 };
192
193 static guint   tree_menu_signals[N_SIGNALS] = { 0 };
194 static GQuark  tree_menu_path_quark = 0;
195
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));
199
200 static void
201 _gtk_tree_menu_init (GtkTreeMenu *menu)
202 {
203   GtkTreeMenuPrivate *priv;
204
205   menu->priv = G_TYPE_INSTANCE_GET_PRIVATE (menu,
206                                             GTK_TYPE_TREE_MENU,
207                                             GtkTreeMenuPrivate);
208   priv = menu->priv;
209
210   priv->model     = NULL;
211   priv->root      = NULL;
212   priv->area      = NULL;
213   priv->context   = NULL;
214
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;
220
221   priv->wrap_width   = 0;
222   priv->row_span_col = -1;
223   priv->col_span_col = -1;
224
225   priv->menu_with_header = FALSE;
226   priv->tearoff          = FALSE;
227
228   priv->row_separator_func    = NULL;
229   priv->row_separator_data    = NULL;
230   priv->row_separator_destroy = NULL;
231
232   priv->header_func    = NULL;
233   priv->header_data    = NULL;
234   priv->header_destroy = NULL;
235
236   gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
237 }
238
239 static void
240 _gtk_tree_menu_class_init (GtkTreeMenuClass *class)
241 {
242   GObjectClass   *object_class = G_OBJECT_CLASS (class);
243   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
244
245   tree_menu_path_quark = g_quark_from_static_string ("gtk-tree-menu-path");
246
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;
252
253   widget_class->get_preferred_width  = gtk_tree_menu_get_preferred_width;
254   widget_class->get_preferred_height = gtk_tree_menu_get_preferred_height;
255
256   /*
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
261    *
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.
265    *
266    * Since: 3.0
267    */
268   tree_menu_signals[SIGNAL_MENU_ACTIVATE] =
269     g_signal_new (I_("menu-activate"),
270                   G_OBJECT_CLASS_TYPE (object_class),
271                   G_SIGNAL_RUN_FIRST,
272                   0, /* No class closure here */
273                   NULL, NULL,
274                   _gtk_marshal_VOID__STRING,
275                   G_TYPE_NONE, 1, G_TYPE_STRING);
276
277   /*
278    * GtkTreeMenu:model:
279    *
280    * The #GtkTreeModel from which the menu is constructed.
281    *
282    * Since: 3.0
283    */
284   g_object_class_install_property (object_class,
285                                    PROP_MODEL,
286                                    g_param_spec_object ("model",
287                                                         P_("TreeMenu model"),
288                                                         P_("The model for the tree menu"),
289                                                         GTK_TYPE_TREE_MODEL,
290                                                         GTK_PARAM_READWRITE));
291
292   /*
293    * GtkTreeMenu:root:
294    *
295    * The #GtkTreePath that is the root for this menu, or %NULL.
296    *
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.
300    *
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.
304    * 
305    * Since: 3.0
306    */
307   g_object_class_install_property (object_class,
308                                    PROP_ROOT,
309                                    g_param_spec_boxed ("root",
310                                                        P_("TreeMenu root row"),
311                                                        P_("The TreeMenu will display children of the "
312                                                           "specified root"),
313                                                        GTK_TYPE_TREE_PATH,
314                                                        GTK_PARAM_READWRITE));
315
316   /*
317    * GtkTreeMenu:cell-area:
318    *
319    * The #GtkCellArea used to render cells in the menu items.
320    *
321    * You can provide a different cell area at object construction
322    * time, otherwise the #GtkTreeMenu will use a #GtkCellAreaBox.
323    *
324    * Since: 3.0
325    */
326   g_object_class_install_property (object_class,
327                                    PROP_CELL_AREA,
328                                    g_param_spec_object ("cell-area",
329                                                         P_("Cell Area"),
330                                                         P_("The GtkCellArea used to layout cells"),
331                                                         GTK_TYPE_CELL_AREA,
332                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
333
334   /*
335    * GtkTreeMenu:tearoff:
336    *
337    * Specifies whether this menu comes with a leading tearoff menu item
338    *
339    * Since: 3.0
340    */
341   g_object_class_install_property (object_class,
342                                    PROP_TEAROFF,
343                                    g_param_spec_boolean ("tearoff",
344                                                          P_("Tearoff"),
345                                                          P_("Whether the menu has a tearoff item"),
346                                                          FALSE,
347                                                          GTK_PARAM_READWRITE));
348
349   /*
350    * GtkTreeMenu:wrap-width:
351    *
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.
355    *
356    * Since: 3.0
357    */
358   g_object_class_install_property (object_class,
359                                    PROP_WRAP_WIDTH,
360                                    g_param_spec_int ("wrap-width",
361                                                      P_("Wrap Width"),
362                                                      P_("Wrap width for laying out items in a grid"),
363                                                      0,
364                                                      G_MAXINT,
365                                                      0,
366                                                      GTK_PARAM_READWRITE));
367
368   /*
369    * GtkTreeMenu:row-span-column:
370    *
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.
373    *
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.
377    *
378    * Since: 3.0
379    */
380   g_object_class_install_property (object_class,
381                                    PROP_ROW_SPAN_COL,
382                                    g_param_spec_int ("row-span-column",
383                                                      P_("Row span column"),
384                                                      P_("TreeModel column containing the row span values"),
385                                                      -1,
386                                                      G_MAXINT,
387                                                      -1,
388                                                      GTK_PARAM_READWRITE));
389
390   /*
391    * GtkTreeMenu:column-span-column:
392    *
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.
395    *
396    * The values of that column are used to determine how many columns a value
397    * in the list will span.
398    *
399    * Since: 3.0
400    */
401   g_object_class_install_property (object_class,
402                                    PROP_COL_SPAN_COL,
403                                    g_param_spec_int ("column-span-column",
404                                                      P_("Column span column"),
405                                                      P_("TreeModel column containing the column span values"),
406                                                      -1,
407                                                      G_MAXINT,
408                                                      -1,
409                                                      GTK_PARAM_READWRITE));
410
411   g_type_class_add_private (object_class, sizeof (GtkTreeMenuPrivate));
412 }
413
414 /****************************************************************
415  *                         GObjectClass                         *
416  ****************************************************************/
417 static GObject  *
418 gtk_tree_menu_constructor (GType                  type,
419                            guint                  n_construct_properties,
420                            GObjectConstructParam *construct_properties)
421 {
422   GObject            *object;
423   GtkTreeMenu        *menu;
424   GtkTreeMenuPrivate *priv;
425
426   object = G_OBJECT_CLASS (_gtk_tree_menu_parent_class)->constructor
427     (type, n_construct_properties, construct_properties);
428
429   menu = GTK_TREE_MENU (object);
430   priv = menu->priv;
431
432   if (!priv->area)
433     {
434       GtkCellArea *area = gtk_cell_area_box_new ();
435
436       gtk_tree_menu_set_area (menu, area);
437     }
438
439   priv->context = gtk_cell_area_create_context (priv->area);
440
441   priv->size_changed_id =
442     g_signal_connect (priv->context, "notify",
443                       G_CALLBACK (context_size_changed_cb), menu);
444
445   return object;
446 }
447
448 static void
449 gtk_tree_menu_dispose (GObject *object)
450 {
451   GtkTreeMenu        *menu;
452   GtkTreeMenuPrivate *priv;
453
454   menu = GTK_TREE_MENU (object);
455   priv = menu->priv;
456
457   _gtk_tree_menu_set_model (menu, NULL);
458   gtk_tree_menu_set_area (menu, NULL);
459
460   if (priv->context)
461     {
462       /* Disconnect signals */
463       g_signal_handler_disconnect (priv->context, priv->size_changed_id);
464
465       g_object_unref (priv->context);
466       priv->context = NULL;
467       priv->size_changed_id = 0;
468     }
469
470   G_OBJECT_CLASS (_gtk_tree_menu_parent_class)->dispose (object);
471 }
472
473 static void
474 gtk_tree_menu_finalize (GObject *object)
475 {
476   GtkTreeMenu        *menu;
477   GtkTreeMenuPrivate *priv;
478
479   menu = GTK_TREE_MENU (object);
480   priv = menu->priv;
481
482   _gtk_tree_menu_set_row_separator_func (menu, NULL, NULL, NULL);
483   _gtk_tree_menu_set_header_func (menu, NULL, NULL, NULL);
484
485   if (priv->root)
486     gtk_tree_row_reference_free (priv->root);
487
488   G_OBJECT_CLASS (_gtk_tree_menu_parent_class)->finalize (object);
489 }
490
491 static void
492 gtk_tree_menu_set_property (GObject            *object,
493                             guint               prop_id,
494                             const GValue       *value,
495                             GParamSpec         *pspec)
496 {
497   GtkTreeMenu *menu = GTK_TREE_MENU (object);
498
499   switch (prop_id)
500     {
501     case PROP_MODEL:
502       _gtk_tree_menu_set_model (menu, g_value_get_object (value));
503       break;
504
505     case PROP_ROOT:
506       _gtk_tree_menu_set_root (menu, g_value_get_boxed (value));
507       break;
508
509     case PROP_CELL_AREA:
510       /* Construct-only, can only be assigned once */
511       gtk_tree_menu_set_area (menu, (GtkCellArea *)g_value_get_object (value));
512       break;
513
514     case PROP_TEAROFF:
515       _gtk_tree_menu_set_tearoff (menu, g_value_get_boolean (value));
516       break;
517
518     case PROP_WRAP_WIDTH:
519       _gtk_tree_menu_set_wrap_width (menu, g_value_get_int (value));
520       break;
521
522      case PROP_ROW_SPAN_COL:
523       _gtk_tree_menu_set_row_span_column (menu, g_value_get_int (value));
524       break;
525
526      case PROP_COL_SPAN_COL:
527       _gtk_tree_menu_set_column_span_column (menu, g_value_get_int (value));
528       break;
529
530     default:
531       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
532       break;
533     }
534 }
535
536 static void
537 gtk_tree_menu_get_property (GObject            *object,
538                             guint               prop_id,
539                             GValue             *value,
540                             GParamSpec         *pspec)
541 {
542   GtkTreeMenu        *menu = GTK_TREE_MENU (object);
543   GtkTreeMenuPrivate *priv = menu->priv;
544
545   switch (prop_id)
546     {
547     case PROP_MODEL:
548       g_value_set_object (value, priv->model);
549       break;
550
551     case PROP_ROOT:
552       g_value_set_boxed (value, priv->root);
553       break;
554
555     case PROP_CELL_AREA:
556       g_value_set_object (value, priv->area);
557       break;
558
559     case PROP_TEAROFF:
560       g_value_set_boolean (value, priv->tearoff);
561       break;
562
563     default:
564       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
565       break;
566     }
567 }
568
569 /****************************************************************
570  *                         GtkWidgetClass                       *
571  ****************************************************************/
572
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).
577  */
578 static void
579 sync_reserve_submenu_size (GtkTreeMenu *menu)
580 {
581   GList              *children, *l;
582   gboolean            has_submenu = FALSE;
583
584   children = gtk_container_get_children (GTK_CONTAINER (menu));
585   for (l = children; l; l = l->next)
586     {
587       GtkMenuItem *item = l->data;
588
589       if (gtk_menu_item_get_submenu (item) != NULL)
590         {
591           has_submenu = TRUE;
592           break;
593         }
594     }
595
596   for (l = children; l; l = l->next)
597     {
598       GtkMenuItem *item = l->data;
599
600       gtk_menu_item_set_reserve_indicator (item, has_submenu);
601     }
602
603   g_list_free (children);
604 }
605
606 static void
607 gtk_tree_menu_get_preferred_width (GtkWidget           *widget,
608                                    gint                *minimum_size,
609                                    gint                *natural_size)
610 {
611   GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
612   GtkTreeMenuPrivate *priv = menu->priv;
613
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.
617    *
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.
620    */
621   g_signal_handler_block (priv->context, priv->size_changed_id);
622
623   sync_reserve_submenu_size (menu);
624
625   GTK_WIDGET_CLASS (_gtk_tree_menu_parent_class)->get_preferred_width (widget, minimum_size, natural_size);
626
627   g_signal_handler_unblock (priv->context, priv->size_changed_id);
628 }
629
630 static void
631 gtk_tree_menu_get_preferred_height (GtkWidget           *widget,
632                                     gint                *minimum_size,
633                                     gint                *natural_size)
634 {
635   GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
636   GtkTreeMenuPrivate *priv = menu->priv;
637
638   g_signal_handler_block (priv->context, priv->size_changed_id);
639
640   sync_reserve_submenu_size (menu);
641
642   GTK_WIDGET_CLASS (_gtk_tree_menu_parent_class)->get_preferred_height (widget, minimum_size, natural_size);
643
644   g_signal_handler_unblock (priv->context, priv->size_changed_id);
645 }
646
647 /****************************************************************
648  *                      GtkCellLayoutIface                      *
649  ****************************************************************/
650 static void
651 gtk_tree_menu_cell_layout_init (GtkCellLayoutIface  *iface)
652 {
653   iface->get_area = gtk_tree_menu_cell_layout_get_area;
654 }
655
656 static GtkCellArea *
657 gtk_tree_menu_cell_layout_get_area (GtkCellLayout *layout)
658 {
659   GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
660   GtkTreeMenuPrivate *priv = menu->priv;
661
662   return priv->area;
663 }
664
665
666 /****************************************************************
667  *             TreeModel callbacks/populating menus             *
668  ****************************************************************/
669 static GtkWidget *
670 gtk_tree_menu_get_path_item (GtkTreeMenu          *menu,
671                              GtkTreePath          *search)
672 {
673   GtkWidget *item = NULL;
674   GList     *children, *l;
675
676   children = gtk_container_get_children (GTK_CONTAINER (menu));
677
678   for (l = children; item == NULL && l != NULL; l = l->next)
679     {
680       GtkWidget   *child = l->data;
681       GtkTreePath *path  = NULL;
682
683       if (GTK_IS_SEPARATOR_MENU_ITEM (child))
684         {
685           GtkTreeRowReference *row =
686             g_object_get_qdata (G_OBJECT (child), tree_menu_path_quark);
687
688           if (row)
689             {
690               path = gtk_tree_row_reference_get_path (row);
691
692               if (!path)
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.
696                  */
697                 item = child;
698             }
699         }
700       else if (!GTK_IS_TEAROFF_MENU_ITEM (child))
701         {
702           GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
703
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));
707
708           if (!path)
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.
712              */
713             item = child;
714         }
715
716       if (path)
717         {
718           if (gtk_tree_path_compare (search, path) == 0)
719             item = child;
720
721           gtk_tree_path_free (path);
722         }
723     }
724
725   g_list_free (children);
726
727   return item;
728 }
729
730 static gboolean
731 gtk_tree_menu_path_in_menu (GtkTreeMenu  *menu,
732                             GtkTreePath  *path,
733                             gboolean     *header_item)
734 {
735   GtkTreeMenuPrivate *priv = menu->priv;
736   gboolean            in_menu = FALSE;
737   gboolean            is_header = FALSE;
738
739   /* Check if the is in root of the model */
740   if (gtk_tree_path_get_depth (path) == 1 && !priv->root)
741     in_menu = TRUE;
742   /* If we are a submenu, compare the parent path */
743   else if (priv->root)
744     {
745       GtkTreePath *root_path   = gtk_tree_row_reference_get_path (priv->root);
746       GtkTreePath *search_path = gtk_tree_path_copy (path);
747
748       if (root_path)
749         {
750           if (priv->menu_with_header &&
751               gtk_tree_path_compare (root_path, search_path) == 0)
752             {
753               in_menu   = TRUE;
754               is_header = TRUE;
755             }
756           else if (gtk_tree_path_get_depth (search_path) > 1)
757             {
758               gtk_tree_path_up (search_path);
759
760               if (gtk_tree_path_compare (root_path, search_path) == 0)
761                 in_menu = TRUE;
762             }
763         }
764       gtk_tree_path_free (root_path);
765       gtk_tree_path_free (search_path);
766     }
767
768   if (header_item)
769     *header_item = is_header;
770
771   return in_menu;
772 }
773
774 static GtkWidget *
775 gtk_tree_menu_path_needs_submenu (GtkTreeMenu *menu,
776                                   GtkTreePath *search)
777 {
778   GtkWidget   *item = NULL;
779   GList       *children, *l;
780   GtkTreePath *parent_path;
781
782   if (gtk_tree_path_get_depth (search) <= 1)
783     return NULL;
784
785   parent_path = gtk_tree_path_copy (search);
786   gtk_tree_path_up (parent_path);
787
788   children    = gtk_container_get_children (GTK_CONTAINER (menu));
789
790   for (l = children; item == NULL && l != NULL; l = l->next)
791     {
792       GtkWidget   *child = l->data;
793       GtkTreePath *path  = NULL;
794
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)))
799         {
800           GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
801
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));
805         }
806
807       if (path)
808         {
809           if (gtk_tree_path_compare (parent_path, path) == 0)
810             item = child;
811
812           gtk_tree_path_free (path);
813         }
814     }
815
816   g_list_free (children);
817   gtk_tree_path_free (parent_path);
818
819   return item;
820 }
821
822 static GtkWidget *
823 find_empty_submenu (GtkTreeMenu  *menu)
824 {
825   GtkTreeMenuPrivate *priv = menu->priv;
826   GList              *children, *l;
827   GtkWidget          *submenu = NULL;
828
829   children = gtk_container_get_children (GTK_CONTAINER (menu));
830
831   for (l = children; submenu == NULL && l != NULL; l = l->next)
832     {
833       GtkWidget   *child = l->data;
834       GtkTreePath *path  = NULL;
835       GtkTreeIter  iter;
836
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))
840         {
841           GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
842
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));
846         }
847
848       if (path)
849         {
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));
853
854           gtk_tree_path_free (path);
855         }
856     }
857
858   g_list_free (children);
859
860   return submenu;
861 }
862
863 static void
864 row_inserted_cb (GtkTreeModel     *model,
865                  GtkTreePath      *path,
866                  GtkTreeIter      *iter,
867                  GtkTreeMenu      *menu)
868 {
869   GtkTreeMenuPrivate *priv = menu->priv;
870   gint               *indices, index, depth;
871   GtkWidget          *item;
872
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))
875     {
876       if (priv->wrap_width > 0)
877         rebuild_menu (menu);
878       else
879         {
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];
884
885           /* Menus with a header include a menuitem for its root node
886            * and a separator menu item */
887           if (priv->menu_with_header)
888             index += 2;
889
890           /* Index after the tearoff item for the root menu if
891            * there is a tearoff item
892            */
893           if (priv->root == NULL && priv->tearoff)
894             index += 1;
895
896           item = gtk_tree_menu_create_item (menu, iter, FALSE);
897           gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, index);
898
899           /* Resize everything */
900           gtk_cell_area_context_reset (menu->priv->context);
901         }
902     }
903   else
904     {
905       /* Create submenus for iters if we need to */
906       item = gtk_tree_menu_path_needs_submenu (menu, path);
907       if (item)
908         {
909           GtkTreePath *item_path = gtk_tree_path_copy (path);
910
911           gtk_tree_path_up (item_path);
912           gtk_tree_menu_create_submenu (menu, item, item_path);
913           gtk_tree_path_free (item_path);
914         }
915     }
916 }
917
918 static void
919 row_deleted_cb (GtkTreeModel     *model,
920                 GtkTreePath      *path,
921                 GtkTreeMenu      *menu)
922 {
923   GtkTreeMenuPrivate *priv = menu->priv;
924   GtkWidget          *item;
925
926   /* If it's the header item we leave it to the parent menu
927    * to remove us from its menu
928    */
929   item = gtk_tree_menu_get_path_item (menu, path);
930
931   if (item)
932     {
933       if (priv->wrap_width > 0)
934         rebuild_menu (menu);
935       else
936         {
937           /* Get rid of the deleted item */
938           gtk_widget_destroy (item);
939
940           /* Resize everything */
941           gtk_cell_area_context_reset (menu->priv->context);
942         }
943     }
944   else
945     {
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);
949       if (submenu)
950         gtk_widget_destroy (submenu);
951     }
952 }
953
954 static void
955 row_reordered_cb (GtkTreeModel    *model,
956                   GtkTreePath     *path,
957                   GtkTreeIter     *iter,
958                   gint            *new_order,
959                   GtkTreeMenu     *menu)
960 {
961   GtkTreeMenuPrivate *priv = menu->priv;
962   gboolean            this_menu = FALSE;
963
964   if (gtk_tree_path_get_depth (path) == 0 && !priv->root)
965     this_menu = TRUE;
966   else if (priv->root)
967     {
968       GtkTreePath *root_path =
969         gtk_tree_row_reference_get_path (priv->root);
970
971       if (gtk_tree_path_compare (root_path, path) == 0)
972         this_menu = TRUE;
973
974       gtk_tree_path_free (root_path);
975     }
976
977   if (this_menu)
978     rebuild_menu (menu);
979 }
980
981 static gint
982 menu_item_position (GtkTreeMenu *menu,
983                     GtkWidget   *item)
984 {
985   GList *children, *l;
986   gint   position;
987
988   children = gtk_container_get_children (GTK_CONTAINER (menu));
989   for (position = 0, l = children; l; position++, l = l->next)
990     {
991       GtkWidget *iitem = l->data;
992
993       if (item == iitem)
994         break;
995     }
996
997   g_list_free (children);
998
999   return position;
1000 }
1001
1002 static void
1003 row_changed_cb (GtkTreeModel         *model,
1004                 GtkTreePath          *path,
1005                 GtkTreeIter          *iter,
1006                 GtkTreeMenu          *menu)
1007 {
1008   GtkTreeMenuPrivate *priv = menu->priv;
1009   gboolean            is_separator = FALSE;
1010   gboolean            has_header = FALSE;
1011   GtkWidget          *item;
1012
1013   item = gtk_tree_menu_get_path_item (menu, path);
1014
1015   if (priv->root)
1016     {
1017       GtkTreePath *root_path =
1018         gtk_tree_row_reference_get_path (priv->root);
1019
1020       if (root_path && gtk_tree_path_compare (root_path, path) == 0)
1021         {
1022           if (priv->header_func)
1023             has_header =
1024               priv->header_func (priv->model, iter, priv->header_data);
1025
1026           if (has_header && !item)
1027             {
1028               item = gtk_separator_menu_item_new ();
1029               gtk_widget_show (item);
1030               gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
1031
1032               item = gtk_tree_menu_create_item (menu, iter, TRUE);
1033               gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
1034
1035               priv->menu_with_header = TRUE;
1036             }
1037           else if (!has_header && item)
1038             {
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);
1042
1043               priv->menu_with_header = FALSE;
1044             }
1045
1046           gtk_tree_path_free (root_path);
1047         }
1048     }
1049
1050   if (item)
1051     {
1052       if (priv->wrap_width > 0)
1053         /* Ugly, we need to rebuild the menu here if
1054          * the row-span/row-column values change
1055          */
1056         rebuild_menu (menu);
1057       else
1058         {
1059           if (priv->row_separator_func)
1060             is_separator =
1061               priv->row_separator_func (model, iter,
1062                                         priv->row_separator_data);
1063
1064
1065           if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
1066             {
1067               gint position = menu_item_position (menu, item);
1068
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);
1072             }
1073         }
1074     }
1075 }
1076
1077 static void
1078 context_size_changed_cb (GtkCellAreaContext  *context,
1079                          GParamSpec          *pspec,
1080                          GtkWidget           *menu)
1081 {
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);
1087 }
1088
1089 static gboolean
1090 area_is_sensitive (GtkCellArea *area)
1091 {
1092   GList    *cells, *list;
1093   gboolean  sensitive = FALSE;
1094
1095   cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
1096
1097   for (list = cells; list; list = list->next)
1098     {
1099       g_object_get (list->data, "sensitive", &sensitive, NULL);
1100
1101       if (sensitive)
1102         break;
1103     }
1104   g_list_free (cells);
1105
1106   return sensitive;
1107 }
1108
1109 static void
1110 area_apply_attributes_cb (GtkCellArea          *area,
1111                           GtkTreeModel         *tree_model,
1112                           GtkTreeIter          *iter,
1113                           gboolean              is_expander,
1114                           gboolean              is_expanded,
1115                           GtkTreeMenu          *menu)
1116 {
1117   /* If the menu for this iter has a submenu */
1118   GtkTreeMenuPrivate *priv = menu->priv;
1119   GtkTreePath        *path;
1120   GtkWidget          *item;
1121   gboolean            is_header;
1122   gboolean            sensitive;
1123
1124   path = gtk_tree_model_get_path (tree_model, iter);
1125
1126   if (gtk_tree_menu_path_in_menu (menu, path, &is_header))
1127     {
1128       item = gtk_tree_menu_get_path_item (menu, path);
1129
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)))
1133         {
1134           sensitive = area_is_sensitive (priv->area);
1135
1136           gtk_widget_set_sensitive (item, sensitive);
1137
1138           if (is_header)
1139             {
1140               /* For header items we need to set the sensitivity
1141                * of the following separator item
1142                */
1143               if (GTK_MENU_SHELL (menu)->priv->children &&
1144                   GTK_MENU_SHELL (menu)->priv->children->next)
1145                 {
1146                   GtkWidget *separator =
1147                     GTK_MENU_SHELL (menu)->priv->children->next->data;
1148
1149                   gtk_widget_set_sensitive (separator, sensitive);
1150                 }
1151             }
1152         }
1153     }
1154
1155   gtk_tree_path_free (path);
1156 }
1157
1158 static void
1159 gtk_tree_menu_set_area (GtkTreeMenu *menu,
1160                         GtkCellArea *area)
1161 {
1162   GtkTreeMenuPrivate *priv = menu->priv;
1163
1164   if (priv->area)
1165     {
1166       g_signal_handler_disconnect (priv->area,
1167                                    priv->apply_attributes_id);
1168       priv->apply_attributes_id = 0;
1169
1170       g_object_unref (priv->area);
1171     }
1172
1173   priv->area = area;
1174
1175   if (priv->area)
1176     {
1177       g_object_ref_sink (priv->area);
1178
1179       priv->apply_attributes_id =
1180         g_signal_connect (priv->area, "apply-attributes",
1181                           G_CALLBACK (area_apply_attributes_cb), menu);
1182     }
1183 }
1184
1185 static gboolean
1186 menu_occupied (GtkTreeMenu *menu,
1187                guint        left_attach,
1188                guint        right_attach,
1189                guint        top_attach,
1190                guint        bottom_attach)
1191 {
1192   GList *i;
1193
1194   for (i = GTK_MENU_SHELL (menu)->priv->children; i; i = i->next)
1195     {
1196       guint l, r, b, t;
1197
1198       gtk_container_child_get (GTK_CONTAINER (menu),
1199                                i->data,
1200                                "left-attach", &l,
1201                                "right-attach", &r,
1202                                "bottom-attach", &b,
1203                                "top-attach", &t,
1204                                NULL);
1205
1206       /* look if this item intersects with the given coordinates */
1207       if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
1208         return TRUE;
1209     }
1210
1211   return FALSE;
1212 }
1213
1214 static void
1215 relayout_item (GtkTreeMenu *menu,
1216                GtkWidget   *item,
1217                GtkTreeIter *iter,
1218                GtkWidget   *prev)
1219 {
1220   GtkTreeMenuPrivate *priv = menu->priv;
1221   gint                current_col = 0, current_row = 0;
1222   gint                rows = 1, cols = 1;
1223
1224   if (priv->col_span_col == -1 &&
1225       priv->row_span_col == -1 &&
1226       prev)
1227     {
1228       gtk_container_child_get (GTK_CONTAINER (menu), prev,
1229                                "right-attach", &current_col,
1230                                "top-attach", &current_row,
1231                                NULL);
1232       if (current_col + cols > priv->wrap_width)
1233         {
1234           current_col = 0;
1235           current_row++;
1236         }
1237     }
1238   else
1239     {
1240       if (priv->col_span_col != -1)
1241         gtk_tree_model_get (priv->model, iter,
1242                             priv->col_span_col, &cols,
1243                             -1);
1244       if (priv->row_span_col != -1)
1245         gtk_tree_model_get (priv->model, iter,
1246                             priv->row_span_col, &rows,
1247                             -1);
1248
1249       while (1)
1250         {
1251           if (current_col + cols > priv->wrap_width)
1252             {
1253               current_col = 0;
1254               current_row++;
1255             }
1256
1257           if (!menu_occupied (menu,
1258                               current_col, current_col + cols,
1259                               current_row, current_row + rows))
1260             break;
1261
1262           current_col++;
1263         }
1264     }
1265
1266   /* set attach props */
1267   gtk_menu_attach (GTK_MENU (menu), item,
1268                    current_col, current_col + cols,
1269                    current_row, current_row + rows);
1270 }
1271
1272 static void
1273 gtk_tree_menu_create_submenu (GtkTreeMenu *menu,
1274                               GtkWidget   *item,
1275                               GtkTreePath *path)
1276 {
1277   GtkTreeMenuPrivate *priv = menu->priv;
1278   GtkWidget          *view;
1279   GtkWidget          *submenu;
1280
1281   view = gtk_bin_get_child (GTK_BIN (item));
1282   gtk_cell_view_set_draw_sensitive (GTK_CELL_VIEW (view), TRUE);
1283
1284   submenu = _gtk_tree_menu_new_with_area (priv->area);
1285
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),
1291                                   priv->header_func,
1292                                   priv->header_data,
1293                                   priv->header_destroy);
1294
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);
1298
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);
1302
1303   g_signal_connect (submenu, "menu-activate",
1304                     G_CALLBACK (submenu_activated_cb), menu);
1305 }
1306
1307 static GtkWidget *
1308 gtk_tree_menu_create_item (GtkTreeMenu *menu,
1309                            GtkTreeIter *iter,
1310                            gboolean     header_item)
1311 {
1312   GtkTreeMenuPrivate *priv = menu->priv;
1313   GtkWidget          *item, *view;
1314   GtkTreePath        *path;
1315   gboolean            is_separator = FALSE;
1316
1317   path = gtk_tree_model_get_path (priv->model, iter);
1318
1319   if (priv->row_separator_func)
1320     is_separator =
1321       priv->row_separator_func (priv->model, iter,
1322                                 priv->row_separator_data);
1323
1324   if (is_separator)
1325     {
1326       item = gtk_separator_menu_item_new ();
1327       gtk_widget_show (item);
1328
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);
1333     }
1334   else
1335     {
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);
1340
1341       gtk_cell_view_set_model (GTK_CELL_VIEW (view), priv->model);
1342       gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (view), path);
1343
1344       gtk_widget_show (view);
1345       gtk_container_add (GTK_CONTAINER (item), view);
1346
1347       g_signal_connect (item, "activate", G_CALLBACK (item_activated_cb), menu);
1348
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);
1353     }
1354
1355   gtk_tree_path_free (path);
1356
1357   return item;
1358 }
1359
1360 static inline void
1361 rebuild_menu (GtkTreeMenu *menu)
1362 {
1363   GtkTreeMenuPrivate *priv = menu->priv;
1364
1365   /* Destroy all the menu items */
1366   gtk_container_foreach (GTK_CONTAINER (menu),
1367                          (GtkCallback) gtk_widget_destroy, NULL);
1368
1369   /* Populate */
1370   if (priv->model)
1371     gtk_tree_menu_populate (menu);
1372 }
1373
1374
1375 static void
1376 gtk_tree_menu_populate (GtkTreeMenu *menu)
1377 {
1378   GtkTreeMenuPrivate *priv = menu->priv;
1379   GtkTreePath        *path = NULL;
1380   GtkTreeIter         parent;
1381   GtkTreeIter         iter;
1382   gboolean            valid = FALSE;
1383   GtkWidget          *menu_item, *prev = NULL;
1384
1385   if (!priv->model)
1386     return;
1387
1388   if (priv->root)
1389     path = gtk_tree_row_reference_get_path (priv->root);
1390
1391   if (path)
1392     {
1393       if (gtk_tree_model_get_iter (priv->model, &parent, path))
1394         {
1395           valid = gtk_tree_model_iter_children (priv->model, &iter, &parent);
1396
1397           if (priv->header_func &&
1398               priv->header_func (priv->model, &parent, priv->header_data))
1399             {
1400               /* Add a submenu header for rows which desire one, used for
1401                * combo boxes to allow all rows to be activatable/selectable
1402                */
1403               menu_item = gtk_tree_menu_create_item (menu, &parent, TRUE);
1404               gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1405
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);
1409
1410               prev = menu_item;
1411               priv->menu_with_header = TRUE;
1412             }
1413         }
1414       gtk_tree_path_free (path);
1415     }
1416   else
1417     {
1418       /* Tearoff menu items only go in the root menu */
1419       if (priv->tearoff)
1420         {
1421           menu_item = gtk_tearoff_menu_item_new ();
1422           gtk_widget_show (menu_item);
1423
1424           if (priv->wrap_width > 0)
1425             gtk_menu_attach (GTK_MENU (menu), menu_item, 0, priv->wrap_width, 0, 1);
1426           else
1427             gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1428
1429           prev = menu_item;
1430         }
1431
1432       valid = gtk_tree_model_iter_children (priv->model, &iter, NULL);
1433     }
1434
1435   /* Create a menu item for every row at the current depth, add a GtkTreeMenu
1436    * submenu for iters/items that have children */
1437   while (valid)
1438     {
1439       menu_item = gtk_tree_menu_create_item (menu, &iter, FALSE);
1440
1441       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1442
1443       if (priv->wrap_width > 0)
1444         relayout_item (menu, menu_item, &iter, prev);
1445
1446       prev  = menu_item;
1447       valid = gtk_tree_model_iter_next (priv->model, &iter);
1448     }
1449 }
1450
1451 static void
1452 item_activated_cb (GtkMenuItem          *item,
1453                    GtkTreeMenu          *menu)
1454 {
1455   GtkCellView *view;
1456   GtkTreePath *path;
1457   gchar       *path_str;
1458
1459   /* Only activate leafs, not parents */
1460   if (!gtk_menu_item_get_submenu (item))
1461     {
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);
1465
1466       g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path_str);
1467
1468       g_free (path_str);
1469       gtk_tree_path_free (path);
1470     }
1471 }
1472
1473 static void
1474 submenu_activated_cb (GtkTreeMenu          *submenu,
1475                       const gchar          *path,
1476                       GtkTreeMenu          *menu)
1477 {
1478   g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path);
1479 }
1480
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) */
1484 static void
1485 gtk_tree_menu_set_model_internal (GtkTreeMenu  *menu,
1486                                   GtkTreeModel *model)
1487 {
1488   GtkTreeMenuPrivate *priv;
1489
1490   priv = menu->priv;
1491
1492   if (priv->model != model)
1493     {
1494       if (priv->model)
1495         {
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;
1509
1510           g_object_unref (priv->model);
1511         }
1512
1513       priv->model = model;
1514
1515       if (priv->model)
1516         {
1517           g_object_ref (priv->model);
1518
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);
1528         }
1529     }
1530 }
1531
1532 /****************************************************************
1533  *                            API                               *
1534  ****************************************************************/
1535
1536 /**
1537  * _gtk_tree_menu_new:
1538  *
1539  * Creates a new #GtkTreeMenu.
1540  *
1541  * Return value: A newly created #GtkTreeMenu with no model or root.
1542  *
1543  * Since: 3.0
1544  */
1545 GtkWidget *
1546 _gtk_tree_menu_new (void)
1547 {
1548   return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU, NULL);
1549 }
1550
1551 /*
1552  * _gtk_tree_menu_new_with_area:
1553  * @area: the #GtkCellArea to use to render cells in the menu
1554  *
1555  * Creates a new #GtkTreeMenu using @area to render its cells.
1556  *
1557  * Return value: A newly created #GtkTreeMenu with no model or root.
1558  *
1559  * Since: 3.0
1560  */
1561 GtkWidget *
1562 _gtk_tree_menu_new_with_area (GtkCellArea    *area)
1563 {
1564   return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU,
1565                                     "cell-area", area,
1566                                     NULL);
1567 }
1568
1569 /*
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.
1574  *
1575  * Creates a new #GtkTreeMenu hierarchy from the provided @model and @root using @area to render its cells.
1576  *
1577  * Return value: A newly created #GtkTreeMenu.
1578  *
1579  * Since: 3.0
1580  */
1581 GtkWidget *
1582 _gtk_tree_menu_new_full (GtkCellArea         *area,
1583                          GtkTreeModel        *model,
1584                          GtkTreePath         *root)
1585 {
1586   return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU,
1587                                     "cell-area", area,
1588                                     "model", model,
1589                                     "root", root,
1590                                     NULL);
1591 }
1592
1593 /*
1594  * _gtk_tree_menu_set_model:
1595  * @menu: a #GtkTreeMenu
1596  * @model: (allow-none): the #GtkTreeModel to build the menu hierarchy from, or %NULL.
1597  *
1598  * Sets @model to be used to build the menu heirarhcy.
1599  *
1600  * Since: 3.0
1601  */
1602 void
1603 _gtk_tree_menu_set_model (GtkTreeMenu  *menu,
1604                           GtkTreeModel *model)
1605 {
1606   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1607   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
1608
1609   gtk_tree_menu_set_model_internal (menu, model);
1610
1611   rebuild_menu (menu);
1612 }
1613
1614 /*
1615  * _gtk_tree_menu_get_model:
1616  * @menu: a #GtkTreeMenu
1617  *
1618  * Gets the @model currently used for the menu heirarhcy.
1619  *
1620  * Return value: (transfer none): the #GtkTreeModel which is used
1621  * for @menu's hierarchy.
1622  *
1623  * Since: 3.0
1624  */
1625 GtkTreeModel *
1626 _gtk_tree_menu_get_model (GtkTreeMenu *menu)
1627 {
1628   GtkTreeMenuPrivate *priv;
1629
1630   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1631
1632   priv = menu->priv;
1633
1634   return priv->model;
1635 }
1636
1637 /*
1638  * _gtk_tree_menu_set_root:
1639  * @menu: a #GtkTreeMenu
1640  * @path: (allow-none): the #GtkTreePath which is the root of @menu, or %NULL.
1641  *
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.
1644  *
1645  * Since: 3.0
1646  */
1647 void
1648 _gtk_tree_menu_set_root (GtkTreeMenu *menu,
1649                          GtkTreePath *path)
1650 {
1651   GtkTreeMenuPrivate *priv;
1652
1653   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1654   g_return_if_fail (menu->priv->model != NULL || path == NULL);
1655
1656   priv = menu->priv;
1657
1658   if (priv->root)
1659     gtk_tree_row_reference_free (priv->root);
1660
1661   if (path)
1662     priv->root = gtk_tree_row_reference_new (priv->model, path);
1663   else
1664     priv->root = NULL;
1665
1666   rebuild_menu (menu);
1667 }
1668
1669 /*
1670  * _gtk_tree_menu_get_root:
1671  * @menu: a #GtkTreeMenu
1672  *
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. *
1675  *
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().
1678  *
1679  * Since: 3.0
1680  */
1681 GtkTreePath *
1682 _gtk_tree_menu_get_root (GtkTreeMenu *menu)
1683 {
1684   GtkTreeMenuPrivate *priv;
1685
1686   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1687
1688   priv = menu->priv;
1689
1690   if (priv->root)
1691     return gtk_tree_row_reference_get_path (priv->root);
1692
1693   return NULL;
1694 }
1695
1696 /*
1697  * _gtk_tree_menu_get_tearoff:
1698  * @menu: a #GtkTreeMenu
1699  *
1700  * Gets whether this menu is build with a leading tearoff menu item.
1701  *
1702  * Return value: %TRUE if the menu has a tearoff item.
1703  *
1704  * Since: 3.0
1705  */
1706 gboolean
1707 _gtk_tree_menu_get_tearoff (GtkTreeMenu *menu)
1708 {
1709   GtkTreeMenuPrivate *priv;
1710
1711   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1712
1713   priv = menu->priv;
1714
1715   return priv->tearoff;
1716 }
1717
1718 /*
1719  * _gtk_tree_menu_set_tearoff:
1720  * @menu: a #GtkTreeMenu
1721  * @tearoff: whether the menu should have a leading tearoff menu item.
1722  *
1723  * Sets whether this menu has a leading tearoff menu item.
1724  *
1725  * Since: 3.0
1726  */
1727 void
1728 _gtk_tree_menu_set_tearoff (GtkTreeMenu *menu,
1729                             gboolean     tearoff)
1730 {
1731   GtkTreeMenuPrivate *priv;
1732
1733   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1734
1735   priv = menu->priv;
1736
1737   if (priv->tearoff != tearoff)
1738     {
1739       priv->tearoff = tearoff;
1740
1741       rebuild_menu (menu);
1742
1743       g_object_notify (G_OBJECT (menu), "tearoff");
1744     }
1745 }
1746
1747 /*
1748  * _gtk_tree_menu_get_wrap_width:
1749  * @menu: a #GtkTreeMenu
1750  *
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.
1753  *
1754  * Return value: the wrap width.
1755  *
1756  * Since: 3.0
1757  */
1758 gint
1759 _gtk_tree_menu_get_wrap_width (GtkTreeMenu *menu)
1760 {
1761   GtkTreeMenuPrivate *priv;
1762
1763   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1764
1765   priv = menu->priv;
1766
1767   return priv->wrap_width;
1768 }
1769
1770 /*
1771  * _gtk_tree_menu_set_wrap_width:
1772  * @menu: a #GtkTreeMenu
1773  * @width: the wrap width
1774  *
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.
1777  *
1778  * Since: 3.0
1779  */
1780 void
1781 _gtk_tree_menu_set_wrap_width (GtkTreeMenu *menu,
1782                                gint         width)
1783 {
1784   GtkTreeMenuPrivate *priv;
1785
1786   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1787   g_return_if_fail (width >= 0);
1788
1789   priv = menu->priv;
1790
1791   if (priv->wrap_width != width)
1792     {
1793       priv->wrap_width = width;
1794
1795       rebuild_menu (menu);
1796
1797       g_object_notify (G_OBJECT (menu), "wrap-width");
1798     }
1799 }
1800
1801 /*
1802  * _gtk_tree_menu_get_row_span_column:
1803  * @menu: a #GtkTreeMenu
1804  *
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.
1808  *
1809  * Return value: the column in @menu's model containing row span information, or -1.
1810  *
1811  * Since: 3.0
1812  */
1813 gint
1814 _gtk_tree_menu_get_row_span_column (GtkTreeMenu *menu)
1815 {
1816   GtkTreeMenuPrivate *priv;
1817
1818   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1819
1820   priv = menu->priv;
1821
1822   return priv->row_span_col;
1823 }
1824
1825 /*
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.
1829  *
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.
1833  *
1834  * Since: 3.0
1835  */
1836 void
1837 _gtk_tree_menu_set_row_span_column (GtkTreeMenu *menu,
1838                                     gint         row_span)
1839 {
1840   GtkTreeMenuPrivate *priv;
1841
1842   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1843
1844   priv = menu->priv;
1845
1846   if (priv->row_span_col != row_span)
1847     {
1848       priv->row_span_col = row_span;
1849
1850       if (priv->wrap_width > 0)
1851         rebuild_menu (menu);
1852
1853       g_object_notify (G_OBJECT (menu), "row-span-column");
1854     }
1855 }
1856
1857 /*
1858  * _gtk_tree_menu_get_column_span_column:
1859  * @menu: a #GtkTreeMenu
1860  *
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.
1864  *
1865  * Return value: the column in @menu's model containing column span information, or -1.
1866  *
1867  * Since: 3.0
1868  */
1869 gint
1870 _gtk_tree_menu_get_column_span_column (GtkTreeMenu *menu)
1871 {
1872   GtkTreeMenuPrivate *priv;
1873
1874   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1875
1876   priv = menu->priv;
1877
1878   return priv->col_span_col;
1879 }
1880
1881 /*
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.
1885  *
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.
1889  *
1890  * Since: 3.0
1891  */
1892 void
1893 _gtk_tree_menu_set_column_span_column (GtkTreeMenu *menu,
1894                                        gint         column_span)
1895 {
1896   GtkTreeMenuPrivate *priv;
1897
1898   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1899
1900   priv = menu->priv;
1901
1902   if (priv->col_span_col != column_span)
1903     {
1904       priv->col_span_col = column_span;
1905
1906       if (priv->wrap_width > 0)
1907         rebuild_menu (menu);
1908
1909       g_object_notify (G_OBJECT (menu), "column-span-column");
1910     }
1911 }
1912
1913 /*
1914  * _gtk_tree_menu_get_row_separator_func:
1915  * @menu: a #GtkTreeMenu
1916  *
1917  * Gets the current #GtkTreeViewRowSeparatorFunc separator function.
1918  *
1919  * Return value: the current row separator function.
1920  *
1921  * Since: 3.0
1922  */
1923 GtkTreeViewRowSeparatorFunc
1924 _gtk_tree_menu_get_row_separator_func (GtkTreeMenu *menu)
1925 {
1926   GtkTreeMenuPrivate *priv;
1927
1928   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1929
1930   priv = menu->priv;
1931
1932   return priv->row_separator_func;
1933 }
1934
1935 /*
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
1941  *
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.
1945  *
1946  * Since: 3.0
1947  */
1948 void
1949 _gtk_tree_menu_set_row_separator_func (GtkTreeMenu          *menu,
1950                                        GtkTreeViewRowSeparatorFunc func,
1951                                        gpointer              data,
1952                                        GDestroyNotify        destroy)
1953 {
1954   GtkTreeMenuPrivate *priv;
1955
1956   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1957
1958   priv = menu->priv;
1959
1960   if (priv->row_separator_destroy)
1961     priv->row_separator_destroy (priv->row_separator_data);
1962
1963   priv->row_separator_func    = func;
1964   priv->row_separator_data    = data;
1965   priv->row_separator_destroy = destroy;
1966
1967   rebuild_menu (menu);
1968 }
1969
1970 /*
1971  * _gtk_tree_menu_get_header_func:
1972  * @menu: a #GtkTreeMenu
1973  *
1974  * Gets the current #GtkTreeMenuHeaderFunc header function.
1975  *
1976  * Return value: the current header function.
1977  *
1978  * Since: 3.0
1979  */
1980 GtkTreeMenuHeaderFunc
1981 _gtk_tree_menu_get_header_func (GtkTreeMenu *menu)
1982 {
1983   GtkTreeMenuPrivate *priv;
1984
1985   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1986
1987   priv = menu->priv;
1988
1989   return priv->header_func;
1990 }
1991
1992 /*
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
1998  *
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.
2005  *
2006  * Since: 3.0
2007  */
2008 void
2009 _gtk_tree_menu_set_header_func (GtkTreeMenu          *menu,
2010                                 GtkTreeMenuHeaderFunc func,
2011                                 gpointer              data,
2012                                 GDestroyNotify        destroy)
2013 {
2014   GtkTreeMenuPrivate *priv;
2015
2016   g_return_if_fail (GTK_IS_TREE_MENU (menu));
2017
2018   priv = menu->priv;
2019
2020   if (priv->header_destroy)
2021     priv->header_destroy (priv->header_data);
2022
2023   priv->header_func    = func;
2024   priv->header_data    = data;
2025   priv->header_destroy = destroy;
2026
2027   rebuild_menu (menu);
2028 }