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