]> Pileus Git - ~andy/gtk/blob - gtk/gtktreemenu.c
Getting closer to updating the treemenu view properly from the model signals
[~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
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         {
680           g_print ("gtk_tree_menu_path_in_menu: Found in root menu !\n");
681           in_menu = TRUE;
682         }
683     }
684   /* If we are a submenu, compare the parent path */
685   else if (priv->root && gtk_tree_path_get_depth (path) > 1)
686     {
687       GtkTreePath *root_path   = gtk_tree_row_reference_get_path (priv->root);
688       GtkTreePath *parent_path = gtk_tree_path_copy (path);
689
690       gtk_tree_path_up (parent_path);
691
692       if (gtk_tree_path_compare (root_path, parent_path) == 0)
693         {
694           in_menu = TRUE;
695           g_print ("gtk_tree_menu_path_in_menu: Found in this menu !\n");
696         }
697
698       if (!in_menu && priv->menu_with_header && 
699           gtk_tree_path_compare (root_path, path) == 0)
700         {
701           g_print ("gtk_tree_menu_path_in_menu: Found as menu header !\n");
702           in_menu   = TRUE;
703           is_header = TRUE;
704         }
705       gtk_tree_path_free (root_path);
706       gtk_tree_path_free (parent_path);
707     }
708
709   if (header_item)
710     *header_item = is_header;
711
712   return in_menu;
713 }
714
715 static GtkWidget *
716 gtk_tree_menu_path_needs_submenu (GtkTreeMenu *menu, 
717                                   GtkTreePath *search)
718 {
719   GtkWidget   *item = NULL;
720   GList       *children, *l;
721   GtkTreePath *parent_path;
722
723   if (gtk_tree_path_get_depth (search) <= 1)
724     return NULL;
725
726   parent_path = gtk_tree_path_copy (search);
727   gtk_tree_path_up (parent_path);
728
729   children    = gtk_container_get_children (GTK_CONTAINER (menu));
730
731   for (l = children; item == NULL && l != NULL; l = l->next)
732     {
733       GtkWidget   *child = l->data;
734       GtkTreePath *path  = NULL;
735
736       /* Separators dont get submenus, if it already has a submenu then let
737        * the submenu handle inserted rows */
738       if (!GTK_IS_SEPARATOR_MENU_ITEM (child) && 
739           !gtk_menu_item_get_submenu (GTK_MENU_ITEM (child)))
740         {
741           GtkWidget *view = gtk_bin_get_child (GTK_BIN (child));
742
743           /* It's always a cellview */
744           if (GTK_IS_CELL_VIEW (view))
745             path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view));
746         }
747
748       if (path)
749         {
750           if (gtk_tree_path_compare (parent_path, path) == 0)
751             item = child;
752
753           gtk_tree_path_free (path);
754         }
755     }
756
757   g_list_free (children);
758   gtk_tree_path_free (parent_path);
759
760   return item;
761 }
762
763 static void
764 row_inserted_cb (GtkTreeModel     *model,
765                  GtkTreePath      *path,
766                  GtkTreeIter      *iter,
767                  GtkTreeMenu      *menu)
768 {
769   GtkTreeMenuPrivate *priv = menu->priv;
770   gint               *indices, index, depth;
771   GtkWidget          *item;
772
773   /* If the iter should be in this menu then go ahead and insert it */
774   if (gtk_tree_menu_path_in_menu (menu, path, NULL))
775     {
776
777       if (priv->wrap_width > 0)
778         rebuild_menu (menu);
779       else
780         {
781           /* Get the index of the path for this depth */
782           indices = gtk_tree_path_get_indices (path);
783           depth   = gtk_tree_path_get_depth (path);
784           index   = indices[depth -1];
785           
786           /* Menus with a header include a menuitem for it's root node
787            * and a separator menu item */
788           if (priv->menu_with_header)
789             index += 2;
790           
791           /* Index after the tearoff item for the root menu if
792            * there is a tearoff item
793            */
794           if (priv->root == NULL && priv->tearoff)
795             index += 1;
796           
797           item = gtk_tree_menu_create_item (menu, iter, FALSE);
798           gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, index);
799           
800           /* Resize everything */
801           gtk_cell_area_context_flush (menu->priv->context);
802         }
803     }
804   else
805     {
806       /* Create submenus for iters if we need to */
807       item = gtk_tree_menu_path_needs_submenu (menu, path);
808       if (item)
809         {
810           GtkTreePath *item_path = gtk_tree_path_copy (path);
811
812           gtk_tree_path_up (item_path);
813           gtk_tree_menu_create_submenu (menu, item, item_path);
814           gtk_tree_path_free (item_path);
815         }
816     }
817 }
818
819 static void
820 row_deleted_cb (GtkTreeModel     *model,
821                 GtkTreePath      *path,
822                 GtkTreeMenu      *menu)
823 {
824   GtkTreeMenuPrivate *priv = menu->priv;
825   GtkWidget          *item;
826
827   /* If it's the header item we leave it to the parent menu 
828    * to remove us from its menu
829    */
830   item = gtk_tree_menu_get_path_item (menu, path);
831
832   g_print ("Deleting item %p\n", item);
833
834   if (item)
835     {
836       if (priv->wrap_width > 0)
837         rebuild_menu (menu);
838       else
839         {
840           /* Get rid of the deleted item */
841           gtk_widget_destroy (item);
842           
843           /* Resize everything */
844           gtk_cell_area_context_flush (menu->priv->context);
845         }
846     }
847 }
848
849 static void
850 row_reordered_cb (GtkTreeModel    *model,
851                   GtkTreePath     *path,
852                   GtkTreeIter     *iter,
853                   gint            *new_order,
854                   GtkTreeMenu     *menu)
855 {
856   GtkTreeMenuPrivate *priv = menu->priv;
857   gboolean            this_menu = FALSE;
858
859   if (path == NULL && priv->root == NULL)
860     this_menu = TRUE;
861   else if (priv->root)
862     {
863       GtkTreePath *root_path =
864         gtk_tree_row_reference_get_path (priv->root);
865
866       if (gtk_tree_path_compare (root_path, path) == 0)
867         this_menu = TRUE;
868
869       gtk_tree_path_free (root_path);
870     }
871
872   if (this_menu)
873     rebuild_menu (menu);
874 }
875
876 static gint 
877 menu_item_position (GtkTreeMenu *menu,
878                     GtkWidget   *item)
879 {
880   GList *children, *l;
881   gint   position;
882
883   children = gtk_container_get_children (GTK_CONTAINER (menu));
884   for (position = 0, l = children; l; position++, l = l->next)
885     {
886       GtkWidget *iitem = l->data;
887
888       if (item == iitem)
889         break;
890     }
891
892   g_list_free (children);
893
894   return position;
895 }
896
897 static void
898 row_changed_cb (GtkTreeModel         *model,
899                 GtkTreePath          *path,
900                 GtkTreeIter          *iter,
901                 GtkTreeMenu          *menu)
902 {
903   GtkTreeMenuPrivate *priv = menu->priv;
904   gboolean            is_separator = FALSE;
905   gboolean            has_header = FALSE;
906   GtkWidget          *item;
907
908   item = gtk_tree_menu_get_path_item (menu, path);
909
910   if (priv->root)
911     {
912       GtkTreePath *root_path =
913         gtk_tree_row_reference_get_path (priv->root);
914       
915       if (root_path && gtk_tree_path_compare (root_path, path) == 0)
916         {
917           if (priv->header_func)
918             has_header = 
919               priv->header_func (priv->model, iter, priv->header_data);
920           
921           if (has_header && !item)
922             {
923               item = gtk_separator_menu_item_new ();
924               gtk_widget_show (item);
925               gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
926
927               item = gtk_tree_menu_create_item (menu, iter, TRUE);
928               gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
929
930               priv->menu_with_header = TRUE;
931             }
932           else if (!has_header && item)
933             {
934               /* Destroy the header item and then the following separator */
935               gtk_widget_destroy (item);
936               gtk_widget_destroy (GTK_MENU_SHELL (menu)->children->data);
937
938               priv->menu_with_header = FALSE;
939             }
940
941           gtk_tree_path_free (root_path);
942         }
943     }
944   
945   if (item)
946     {
947       if (priv->wrap_width > 0)
948         /* Ugly, we need to rebuild the menu here if
949          * the row-span/row-column values change 
950          */
951         rebuild_menu (menu);
952       else
953         {
954           if (priv->row_separator_func)
955             is_separator = 
956               priv->row_separator_func (model, iter,
957                                         priv->row_separator_data);
958
959
960           if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
961             {
962               gint position = menu_item_position (menu, item);
963
964               gtk_widget_destroy (item);
965               item = gtk_tree_menu_create_item (menu, iter, FALSE);
966               gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, position);
967             }
968         }
969     }
970 }
971
972 static void
973 context_size_changed_cb (GtkCellAreaContext  *context,
974                          GParamSpec          *pspec,
975                          GtkWidget           *menu)
976 {
977   if (!strcmp (pspec->name, "minimum-width") ||
978       !strcmp (pspec->name, "natural-width") ||
979       !strcmp (pspec->name, "minimum-height") ||
980       !strcmp (pspec->name, "natural-height"))
981     gtk_widget_queue_resize (menu);
982 }
983
984 static gboolean
985 area_is_sensitive (GtkCellArea *area)
986 {
987   GList    *cells, *list;
988   gboolean  sensitive = FALSE;
989   
990   cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
991
992   for (list = cells; list; list = list->next)
993     {
994       g_object_get (list->data, "sensitive", &sensitive, NULL);
995       
996       if (sensitive)
997         break;
998     }
999   g_list_free (cells);
1000
1001   return sensitive;
1002 }
1003
1004 static void
1005 area_apply_attributes_cb (GtkCellArea          *area,
1006                           GtkTreeModel         *tree_model,
1007                           GtkTreeIter          *iter,
1008                           gboolean              is_expander,
1009                           gboolean              is_expanded,
1010                           GtkTreeMenu          *menu)
1011 {
1012   /* If the menu for this iter has a submenu */
1013   GtkTreeMenuPrivate *priv = menu->priv;
1014   GtkTreePath        *path;
1015   GtkWidget          *item;
1016   gboolean            is_header;
1017   gboolean            sensitive;
1018
1019   path = gtk_tree_model_get_path (tree_model, iter);
1020
1021   if (gtk_tree_menu_path_in_menu (menu, path, &is_header))
1022     {
1023       item = gtk_tree_menu_get_path_item (menu, path);
1024
1025       /* If there is no submenu, go ahead and update item sensitivity,
1026        * items with submenus are always sensitive */
1027       if (item && !gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)))
1028         {
1029           sensitive = area_is_sensitive (priv->area);
1030
1031           gtk_widget_set_sensitive (item, sensitive);
1032
1033           if (is_header)
1034             {
1035               /* For header items we need to set the sensitivity
1036                * of the following separator item 
1037                */
1038               if (GTK_MENU_SHELL (menu)->children && 
1039                   GTK_MENU_SHELL (menu)->children->next)
1040                 {
1041                   GtkWidget *separator = 
1042                     GTK_MENU_SHELL (menu)->children->next->data;
1043
1044                   gtk_widget_set_sensitive (separator, sensitive);
1045                 }
1046             }
1047         }
1048     }
1049
1050   gtk_tree_path_free (path);
1051 }
1052
1053 static void
1054 gtk_tree_menu_set_area (GtkTreeMenu *menu,
1055                         GtkCellArea *area)
1056 {
1057   GtkTreeMenuPrivate *priv = menu->priv;
1058
1059   if (priv->area)
1060     {
1061       g_signal_handler_disconnect (priv->area,
1062                                    priv->apply_attributes_id);
1063       priv->apply_attributes_id = 0;
1064
1065       g_object_unref (priv->area);
1066     }
1067
1068   priv->area = area;
1069
1070   if (priv->area)
1071     {
1072       g_object_ref_sink (priv->area);
1073
1074       priv->apply_attributes_id =
1075         g_signal_connect (priv->area, "apply-attributes",
1076                           G_CALLBACK (area_apply_attributes_cb), menu);
1077     }
1078 }
1079
1080 static gboolean
1081 menu_occupied (GtkTreeMenu *menu,
1082                guint        left_attach,
1083                guint        right_attach,
1084                guint        top_attach,
1085                guint        bottom_attach)
1086 {
1087   GList *i;
1088
1089   for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
1090     {
1091       guint l, r, b, t;
1092
1093       gtk_container_child_get (GTK_CONTAINER (menu), 
1094                                i->data,
1095                                "left-attach", &l,
1096                                "right-attach", &r,
1097                                "bottom-attach", &b,
1098                                "top-attach", &t,
1099                                NULL);
1100
1101       /* look if this item intersects with the given coordinates */
1102       if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
1103         return TRUE;
1104     }
1105
1106   return FALSE;
1107 }
1108
1109 static void
1110 relayout_item (GtkTreeMenu *menu,
1111                GtkWidget   *item,
1112                GtkTreeIter *iter,
1113                GtkWidget   *prev)
1114 {
1115   GtkTreeMenuPrivate *priv = menu->priv;
1116   gint                current_col = 0, current_row = 0;
1117   gint                rows = 1, cols = 1;
1118   
1119   if (priv->col_span_col == -1 &&
1120       priv->row_span_col == -1 &&
1121       prev)
1122     {
1123       gtk_container_child_get (GTK_CONTAINER (menu), prev,
1124                                "right-attach", &current_col,
1125                                "top-attach", &current_row,
1126                                NULL);
1127       if (current_col + cols > priv->wrap_width)
1128         {
1129           current_col = 0;
1130           current_row++;
1131         }
1132     }
1133   else
1134     {
1135       if (priv->col_span_col != -1)
1136         gtk_tree_model_get (priv->model, iter,
1137                             priv->col_span_col, &cols,
1138                             -1);
1139       if (priv->row_span_col != -1)
1140         gtk_tree_model_get (priv->model, iter,
1141                             priv->row_span_col, &rows,
1142                             -1);
1143
1144       while (1)
1145         {
1146           if (current_col + cols > priv->wrap_width)
1147             {
1148               current_col = 0;
1149               current_row++;
1150             }
1151           
1152           if (!menu_occupied (menu, 
1153                               current_col, current_col + cols,
1154                               current_row, current_row + rows))
1155             break;
1156
1157           current_col++;
1158         }
1159     }
1160
1161   /* set attach props */
1162   gtk_menu_attach (GTK_MENU (menu), item,
1163                    current_col, current_col + cols,
1164                    current_row, current_row + rows);
1165 }
1166
1167 static void
1168 gtk_tree_menu_create_submenu (GtkTreeMenu *menu,
1169                               GtkWidget   *item,
1170                               GtkTreePath *path)
1171 {
1172   GtkTreeMenuPrivate *priv = menu->priv;
1173   GtkWidget          *view;
1174   GtkWidget          *submenu;
1175
1176   view = gtk_bin_get_child (GTK_BIN (item));
1177   gtk_cell_view_set_draw_sensitive (GTK_CELL_VIEW (view), TRUE);
1178
1179   submenu = gtk_tree_menu_new_with_area (priv->area);
1180
1181   gtk_tree_menu_set_row_separator_func (GTK_TREE_MENU (submenu), 
1182                                         priv->row_separator_func,
1183                                         priv->row_separator_data,
1184                                         priv->row_separator_destroy);
1185   gtk_tree_menu_set_header_func (GTK_TREE_MENU (submenu), 
1186                                  priv->header_func,
1187                                  priv->header_data,
1188                                  priv->header_destroy);
1189
1190   gtk_tree_menu_set_wrap_width (GTK_TREE_MENU (submenu), priv->wrap_width);
1191   gtk_tree_menu_set_row_span_column (GTK_TREE_MENU (submenu), priv->row_span_col);
1192   gtk_tree_menu_set_column_span_column (GTK_TREE_MENU (submenu), priv->col_span_col);
1193   
1194   gtk_tree_menu_set_model_internal (GTK_TREE_MENU (submenu), priv->model);
1195   gtk_tree_menu_set_root (GTK_TREE_MENU (submenu), path);
1196   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
1197   
1198   g_signal_connect (submenu, "menu-activate", 
1199                     G_CALLBACK (submenu_activated_cb), menu);
1200 }
1201
1202 static GtkWidget *
1203 gtk_tree_menu_create_item (GtkTreeMenu *menu,
1204                            GtkTreeIter *iter,
1205                            gboolean     header_item)
1206 {
1207   GtkTreeMenuPrivate *priv = menu->priv;
1208   GtkWidget          *item, *view;
1209   GtkTreePath        *path;
1210   gboolean            is_separator = FALSE;
1211
1212   path = gtk_tree_model_get_path (priv->model, iter);
1213
1214   if (priv->row_separator_func)
1215     is_separator = 
1216       priv->row_separator_func (priv->model, iter,
1217                                 priv->row_separator_data);
1218
1219   if (is_separator)
1220     {
1221       item = gtk_separator_menu_item_new ();
1222       gtk_widget_show (item);
1223
1224       g_object_set_qdata_full (G_OBJECT (item),
1225                                tree_menu_path_quark,
1226                                gtk_tree_row_reference_new (priv->model, path),
1227                                (GDestroyNotify)gtk_tree_row_reference_free);
1228     }
1229   else
1230     {
1231       view = gtk_cell_view_new_with_context (priv->area, priv->context);
1232       item = gtk_menu_item_new ();
1233       gtk_widget_show (view);
1234       gtk_widget_show (item);
1235       
1236       gtk_cell_view_set_model (GTK_CELL_VIEW (view), priv->model);
1237       gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (view), path);
1238       
1239       gtk_widget_show (view);
1240       gtk_container_add (GTK_CONTAINER (item), view);
1241       
1242       g_signal_connect (item, "activate", G_CALLBACK (item_activated_cb), menu);
1243
1244       /* Add a GtkTreeMenu submenu to render the children of this row */
1245       if (header_item == FALSE &&
1246           gtk_tree_model_iter_has_child (priv->model, iter))
1247         gtk_tree_menu_create_submenu (menu, item, path);
1248     }
1249
1250   gtk_tree_path_free (path);
1251
1252   return item;
1253 }
1254
1255 static inline void 
1256 rebuild_menu (GtkTreeMenu *menu)
1257 {
1258   GtkTreeMenuPrivate *priv = menu->priv;
1259
1260   /* Destroy all the menu items */
1261   gtk_container_foreach (GTK_CONTAINER (menu), 
1262                          (GtkCallback) gtk_widget_destroy, NULL);
1263   
1264   /* Populate */
1265   if (priv->model)
1266     gtk_tree_menu_populate (menu);
1267 }
1268
1269
1270 static void
1271 gtk_tree_menu_populate (GtkTreeMenu *menu)
1272 {
1273   GtkTreeMenuPrivate *priv = menu->priv;
1274   GtkTreePath        *path = NULL;
1275   GtkTreeIter         parent;
1276   GtkTreeIter         iter;
1277   gboolean            valid = FALSE;
1278   GtkWidget          *menu_item, *prev = NULL;
1279
1280   if (!priv->model)
1281     return;
1282
1283   if (priv->root)
1284     path = gtk_tree_row_reference_get_path (priv->root);
1285
1286   if (path)
1287     {
1288       if (gtk_tree_model_get_iter (priv->model, &parent, path))
1289         {
1290           valid = gtk_tree_model_iter_children (priv->model, &iter, &parent);
1291
1292           if (priv->header_func && 
1293               priv->header_func (priv->model, &parent, priv->header_data))
1294             {
1295               /* Add a submenu header for rows which desire one, used for
1296                * combo boxes to allow all rows to be activatable/selectable 
1297                */
1298               menu_item = gtk_tree_menu_create_item (menu, &parent, TRUE);
1299               gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1300               
1301               menu_item = gtk_separator_menu_item_new ();
1302               gtk_widget_show (menu_item);
1303               gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1304
1305               prev = menu_item;
1306               priv->menu_with_header = TRUE;
1307             }
1308         }
1309       gtk_tree_path_free (path);
1310     }
1311   else
1312     {
1313       /* Tearoff menu items only go in the root menu */
1314       if (priv->tearoff)
1315         {
1316           menu_item = gtk_tearoff_menu_item_new ();
1317           gtk_widget_show (menu_item);
1318
1319           if (priv->wrap_width > 0)
1320             gtk_menu_attach (GTK_MENU (menu), menu_item, 0, priv->wrap_width, 0, 1);
1321           else
1322             gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1323
1324           prev = menu_item;
1325         }
1326
1327       valid = gtk_tree_model_iter_children (priv->model, &iter, NULL);
1328     }
1329
1330   /* Create a menu item for every row at the current depth, add a GtkTreeMenu
1331    * submenu for iters/items that have children */
1332   while (valid)
1333     {
1334       menu_item = gtk_tree_menu_create_item (menu, &iter, FALSE);
1335
1336       gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1337
1338       if (priv->wrap_width > 0)
1339         relayout_item (menu, menu_item, &iter, prev);
1340
1341       prev  = menu_item;
1342       valid = gtk_tree_model_iter_next (priv->model, &iter);
1343     }
1344 }
1345
1346 static void
1347 item_activated_cb (GtkMenuItem          *item,
1348                    GtkTreeMenu          *menu)
1349 {
1350   GtkCellView *view;
1351   GtkTreePath *path;
1352   gchar       *path_str;
1353
1354   /* Only activate leafs, not parents */
1355   if (!gtk_menu_item_get_submenu (item))
1356     {
1357       view     = GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)));
1358       path     = gtk_cell_view_get_displayed_row (view);
1359       path_str = gtk_tree_path_to_string (path);
1360       
1361       g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path_str);
1362       
1363       g_free (path_str);
1364       gtk_tree_path_free (path);
1365     }
1366 }
1367
1368 static void
1369 submenu_activated_cb (GtkTreeMenu          *submenu,
1370                       const gchar          *path,
1371                       GtkTreeMenu          *menu)
1372 {
1373   g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path);
1374 }
1375
1376 /* Sets the model without rebuilding the menu, prevents
1377  * infinite recursion while building submenus (we wait
1378  * until the root is set and then build the menu) */
1379 static void
1380 gtk_tree_menu_set_model_internal (GtkTreeMenu  *menu,
1381                                   GtkTreeModel *model)
1382 {
1383   GtkTreeMenuPrivate *priv;
1384
1385   priv = menu->priv;
1386
1387   if (priv->model != model)
1388     {
1389       if (priv->model)
1390         {
1391           /* Disconnect signals */
1392           g_signal_handler_disconnect (priv->model,
1393                                        priv->row_inserted_id);
1394           g_signal_handler_disconnect (priv->model,
1395                                        priv->row_deleted_id);
1396           g_signal_handler_disconnect (priv->model,
1397                                        priv->row_reordered_id);
1398           g_signal_handler_disconnect (priv->model,
1399                                        priv->row_changed_id);
1400           priv->row_inserted_id  = 0;
1401           priv->row_deleted_id   = 0;
1402           priv->row_reordered_id = 0;
1403           priv->row_changed_id = 0;
1404
1405           g_object_unref (priv->model);
1406         }
1407
1408       priv->model = model;
1409
1410       if (priv->model)
1411         {
1412           g_object_ref (priv->model);
1413
1414           /* Connect signals */
1415           priv->row_inserted_id  = g_signal_connect (priv->model, "row-inserted",
1416                                                      G_CALLBACK (row_inserted_cb), menu);
1417           priv->row_deleted_id   = g_signal_connect (priv->model, "row-deleted",
1418                                                      G_CALLBACK (row_deleted_cb), menu);
1419           priv->row_reordered_id = g_signal_connect (priv->model, "rows-reordered",
1420                                                      G_CALLBACK (row_reordered_cb), menu);
1421           priv->row_changed_id   = g_signal_connect (priv->model, "row-changed",
1422                                                      G_CALLBACK (row_changed_cb), menu);
1423         }
1424     }
1425 }
1426
1427 /****************************************************************
1428  *                            API                               *
1429  ****************************************************************/
1430 GtkWidget *
1431 gtk_tree_menu_new (void)
1432 {
1433   return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU, NULL);
1434 }
1435
1436 GtkWidget *
1437 gtk_tree_menu_new_with_area (GtkCellArea    *area)
1438 {
1439   return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU, 
1440                                     "cell-area", area, 
1441                                     NULL);
1442 }
1443
1444 GtkWidget *
1445 gtk_tree_menu_new_full (GtkCellArea         *area,
1446                         GtkTreeModel        *model,
1447                         GtkTreePath         *root)
1448 {
1449   return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU, 
1450                                     "cell-area", area, 
1451                                     "model", model,
1452                                     "root", root,
1453                                     NULL);
1454 }
1455
1456 void
1457 gtk_tree_menu_set_model (GtkTreeMenu  *menu,
1458                          GtkTreeModel *model)
1459 {
1460   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1461   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
1462
1463   gtk_tree_menu_set_model_internal (menu, model);
1464
1465   rebuild_menu (menu);
1466 }
1467
1468 GtkTreeModel *
1469 gtk_tree_menu_get_model (GtkTreeMenu *menu)
1470 {
1471   GtkTreeMenuPrivate *priv;
1472
1473   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1474
1475   priv = menu->priv;
1476
1477   return priv->model;
1478 }
1479
1480 void
1481 gtk_tree_menu_set_root (GtkTreeMenu         *menu,
1482                         GtkTreePath         *path)
1483 {
1484   GtkTreeMenuPrivate *priv;
1485
1486   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1487   g_return_if_fail (menu->priv->model != NULL || path == NULL);
1488
1489   priv = menu->priv;
1490
1491   if (priv->root) 
1492     gtk_tree_row_reference_free (priv->root);
1493
1494   if (path)
1495     priv->root = gtk_tree_row_reference_new (priv->model, path);
1496   else
1497     priv->root = NULL;
1498
1499   rebuild_menu (menu);
1500 }
1501
1502 GtkTreePath *
1503 gtk_tree_menu_get_root (GtkTreeMenu *menu)
1504 {
1505   GtkTreeMenuPrivate *priv;
1506
1507   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1508
1509   priv = menu->priv;
1510
1511   if (priv->root)
1512     return gtk_tree_row_reference_get_path (priv->root);
1513
1514   return NULL;
1515 }
1516
1517 gboolean
1518 gtk_tree_menu_get_tearoff (GtkTreeMenu *menu)
1519 {
1520   GtkTreeMenuPrivate *priv;
1521
1522   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1523
1524   priv = menu->priv;
1525
1526   return priv->tearoff;
1527 }
1528
1529 void
1530 gtk_tree_menu_set_tearoff (GtkTreeMenu *menu,
1531                            gboolean     tearoff)
1532 {
1533   GtkTreeMenuPrivate *priv;
1534
1535   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1536
1537   priv = menu->priv;
1538
1539   if (priv->tearoff != tearoff)
1540     {
1541       priv->tearoff = tearoff;
1542
1543       rebuild_menu (menu);
1544
1545       g_object_notify (G_OBJECT (menu), "tearoff");
1546     }
1547 }
1548
1549 gint
1550 gtk_tree_menu_get_wrap_width (GtkTreeMenu *menu)
1551 {
1552   GtkTreeMenuPrivate *priv;
1553
1554   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1555
1556   priv = menu->priv;
1557
1558   return priv->wrap_width;
1559 }
1560
1561 void
1562 gtk_tree_menu_set_wrap_width (GtkTreeMenu *menu,
1563                               gint         width)
1564 {
1565   GtkTreeMenuPrivate *priv;
1566
1567   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1568   g_return_if_fail (width >= 0);
1569
1570   priv = menu->priv;
1571
1572   if (priv->wrap_width != width)
1573     {
1574       priv->wrap_width = width;
1575
1576       rebuild_menu (menu);
1577
1578       g_object_notify (G_OBJECT (menu), "wrap-width");
1579     }
1580 }
1581
1582 gint
1583 gtk_tree_menu_get_row_span_column (GtkTreeMenu *menu)
1584 {
1585   GtkTreeMenuPrivate *priv;
1586
1587   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1588
1589   priv = menu->priv;
1590
1591   return priv->row_span_col;
1592 }
1593
1594 void
1595 gtk_tree_menu_set_row_span_column (GtkTreeMenu *menu,
1596                                    gint         row_span)
1597 {
1598   GtkTreeMenuPrivate *priv;
1599
1600   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1601
1602   priv = menu->priv;
1603
1604   if (priv->row_span_col != row_span)
1605     {
1606       priv->row_span_col = row_span;
1607
1608       if (priv->wrap_width > 0)
1609         rebuild_menu (menu);
1610
1611       g_object_notify (G_OBJECT (menu), "row-span-column");
1612     }
1613 }
1614
1615 gint
1616 gtk_tree_menu_get_column_span_column (GtkTreeMenu *menu)
1617 {
1618   GtkTreeMenuPrivate *priv;
1619
1620   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), FALSE);
1621
1622   priv = menu->priv;
1623
1624   return priv->col_span_col;
1625 }
1626
1627 void
1628 gtk_tree_menu_set_column_span_column (GtkTreeMenu *menu,
1629                                       gint         column_span)
1630 {
1631   GtkTreeMenuPrivate *priv;
1632
1633   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1634
1635   priv = menu->priv;
1636
1637   if (priv->col_span_col != column_span)
1638     {
1639       priv->col_span_col = column_span;
1640
1641       if (priv->wrap_width > 0)
1642         rebuild_menu (menu);
1643
1644       g_object_notify (G_OBJECT (menu), "column-span-column");
1645     }
1646 }
1647
1648 GtkTreeViewRowSeparatorFunc
1649 gtk_tree_menu_get_row_separator_func (GtkTreeMenu *menu)
1650 {
1651   GtkTreeMenuPrivate *priv;
1652
1653   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1654
1655   priv = menu->priv;
1656
1657   return priv->row_separator_func;
1658 }
1659
1660 void
1661 gtk_tree_menu_set_row_separator_func (GtkTreeMenu          *menu,
1662                                       GtkTreeViewRowSeparatorFunc func,
1663                                       gpointer              data,
1664                                       GDestroyNotify        destroy)
1665 {
1666   GtkTreeMenuPrivate *priv;
1667
1668   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1669
1670   priv = menu->priv;
1671
1672   if (priv->row_separator_destroy)
1673     priv->row_separator_destroy (priv->row_separator_data);
1674
1675   priv->row_separator_func    = func;
1676   priv->row_separator_data    = data;
1677   priv->row_separator_destroy = destroy;
1678
1679   rebuild_menu (menu);
1680 }
1681
1682 GtkTreeMenuHeaderFunc
1683 gtk_tree_menu_get_header_func (GtkTreeMenu *menu)
1684 {
1685   GtkTreeMenuPrivate *priv;
1686
1687   g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
1688
1689   priv = menu->priv;
1690
1691   return priv->header_func;
1692 }
1693
1694 void
1695 gtk_tree_menu_set_header_func (GtkTreeMenu          *menu,
1696                                GtkTreeMenuHeaderFunc func,
1697                                gpointer              data,
1698                                GDestroyNotify        destroy)
1699 {
1700   GtkTreeMenuPrivate *priv;
1701
1702   g_return_if_fail (GTK_IS_TREE_MENU (menu));
1703
1704   priv = menu->priv;
1705
1706   if (priv->header_destroy)
1707     priv->header_destroy (priv->header_data);
1708
1709   priv->header_func    = func;
1710   priv->header_data    = data;
1711   priv->header_destroy = destroy;
1712
1713   rebuild_menu (menu);
1714 }