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