[gtk+/combo-refactor: 6/42] Adding GtkTreeMenu class.



commit ed936f4cac1fc263dd05e73065bc259092d8f4ab
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Thu Nov 18 17:33:23 2010 +0900

    Adding GtkTreeMenu class.
    
    Added GtkTreeMenu class to automatically render
    a GtkTreeModel into a GtkMenu hierarchy (will be
    used by GtkComboBox for its dropdown menus). Included
    an accompanying testcase tests/testtreemenu

 gtk/Makefile.am      |    2 +
 gtk/gtk.h            |    1 +
 gtk/gtktreemenu.c    |  909 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/gtktreemenu.h    |   91 +++++
 tests/Makefile.am    |    8 +-
 tests/testtreemenu.c |  245 ++++++++++++++
 6 files changed, 1255 insertions(+), 1 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index b3d6e61..a99419b 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -320,6 +320,7 @@ gtk_public_h_sources =          \
 	gtktoolshell.h		\
 	gtktooltip.h		\
 	gtktreednd.h		\
+	gtktreemenu.h		\
 	gtktreemodel.h		\
 	gtktreemodelfilter.h	\
 	gtktreemodelsort.h	\
@@ -628,6 +629,7 @@ gtk_base_c_sources =            \
 	gtktooltip.c		\
 	gtktreedatalist.c	\
 	gtktreednd.c		\
+	gtktreemenu.c		\
 	gtktreemodel.c		\
 	gtktreemodelfilter.c	\
 	gtktreemodelsort.c	\
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 38889ae..66a0778 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -201,6 +201,7 @@
 #include <gtk/gtktooltip.h>
 #include <gtk/gtktestutils.h>
 #include <gtk/gtktreednd.h>
+#include <gtk/gtktreemenu.h>
 #include <gtk/gtktreemodel.h>
 #include <gtk/gtktreemodelfilter.h>
 #include <gtk/gtktreemodelsort.h>
diff --git a/gtk/gtktreemenu.c b/gtk/gtktreemenu.c
new file mode 100644
index 0000000..cff4541
--- /dev/null
+++ b/gtk/gtktreemenu.c
@@ -0,0 +1,909 @@
+/* gtktreemenu.c
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ *      Tristan Van Berkom <tristanvb openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "gtkintl.h"
+#include "gtktreemenu.h"
+#include "gtkmenuitem.h"
+#include "gtkseparatormenuitem.h"
+#include "gtkcellareabox.h"
+#include "gtkcellareacontext.h"
+#include "gtkcelllayout.h"
+#include "gtkcellview.h"
+#include "gtkprivate.h"
+
+
+/* GObjectClass */
+static GObject  *gtk_tree_menu_constructor                    (GType                  type,
+							       guint                  n_construct_properties,
+							       GObjectConstructParam *construct_properties);
+static void      gtk_tree_menu_dispose                        (GObject            *object);
+static void      gtk_tree_menu_finalize                       (GObject            *object);
+static void      gtk_tree_menu_set_property                   (GObject            *object,
+							       guint               prop_id,
+							       const GValue       *value,
+							       GParamSpec         *pspec);
+static void      gtk_tree_menu_get_property                   (GObject            *object,
+							       guint               prop_id,
+							       GValue             *value,
+							       GParamSpec         *pspec);
+
+/* GtkWidgetClass */
+static void      gtk_tree_menu_get_preferred_width            (GtkWidget           *widget,
+							       gint                *minimum_size,
+							       gint                *natural_size);
+static void      gtk_tree_menu_get_preferred_height           (GtkWidget           *widget,
+							       gint                *minimum_size,
+							       gint                *natural_size);
+static void      gtk_tree_menu_size_allocate                  (GtkWidget           *widget,
+							       GtkAllocation       *allocation);
+
+/* GtkCellLayoutIface */
+static void      gtk_tree_menu_cell_layout_init               (GtkCellLayoutIface  *iface);
+static void      gtk_tree_menu_cell_layout_pack_start         (GtkCellLayout       *layout,
+							       GtkCellRenderer     *cell,
+							       gboolean             expand);
+static void      gtk_tree_menu_cell_layout_pack_end           (GtkCellLayout        *layout,
+							       GtkCellRenderer      *cell,
+							       gboolean              expand);
+static GList    *gtk_tree_menu_cell_layout_get_cells          (GtkCellLayout        *layout);
+static void      gtk_tree_menu_cell_layout_clear              (GtkCellLayout        *layout);
+static void      gtk_tree_menu_cell_layout_add_attribute      (GtkCellLayout        *layout,
+							       GtkCellRenderer      *cell,
+							       const gchar          *attribute,
+							       gint                  column);
+static void      gtk_tree_menu_cell_layout_set_cell_data_func (GtkCellLayout        *layout,
+							       GtkCellRenderer      *cell,
+							       GtkCellLayoutDataFunc func,
+							       gpointer              func_data,
+							       GDestroyNotify        destroy);
+static void      gtk_tree_menu_cell_layout_clear_attributes   (GtkCellLayout        *layout,
+							       GtkCellRenderer      *cell);
+static void      gtk_tree_menu_cell_layout_reorder            (GtkCellLayout        *layout,
+							       GtkCellRenderer      *cell,
+							       gint                  position);
+static GtkCellArea *gtk_tree_menu_cell_layout_get_area        (GtkCellLayout        *layout);
+
+
+/* TreeModel/DrawingArea callbacks and building menus/submenus */
+static void      gtk_tree_menu_populate                       (GtkTreeMenu          *menu);
+static GtkWidget *gtk_tree_menu_create_item                   (GtkTreeMenu          *menu,
+							       GtkTreeIter          *iter);
+static void      gtk_tree_menu_set_area                       (GtkTreeMenu          *menu,
+							       GtkCellArea          *area);
+static void      queue_resize_all                             (GtkWidget            *menu);
+static void      context_size_changed_cb                      (GtkCellAreaContext   *context,
+							       GParamSpec           *pspec,
+							       GtkWidget            *menu);
+
+struct _GtkTreeMenuPrivate
+{
+  /* TreeModel and parent for this menu */
+  GtkTreeModel        *model;
+  GtkTreeRowReference *root;
+
+  /* CellArea and context for this menu */
+  GtkCellArea         *area;
+  GtkCellAreaContext  *context;
+
+  gint                 last_alloc_width;
+  gint                 last_alloc_height;
+  
+  /* Signals */
+  gulong               size_changed_id;
+
+  /* Row separators */
+  GtkTreeViewRowSeparatorFunc row_separator_func;
+  gpointer                    row_separator_data;
+  GDestroyNotify              row_separator_destroy;
+};
+
+enum {
+  PROP_0,
+  PROP_MODEL,
+  PROP_ROOT,
+  PROP_CELL_AREA
+};
+
+G_DEFINE_TYPE_WITH_CODE (GtkTreeMenu, gtk_tree_menu, GTK_TYPE_MENU,
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
+						gtk_tree_menu_cell_layout_init));
+
+static void
+gtk_tree_menu_init (GtkTreeMenu *menu)
+{
+  GtkTreeMenuPrivate *priv;
+
+  menu->priv = G_TYPE_INSTANCE_GET_PRIVATE (menu,
+					    GTK_TYPE_TREE_MENU,
+					    GtkTreeMenuPrivate);
+  priv = menu->priv;
+}
+
+static void 
+gtk_tree_menu_class_init (GtkTreeMenuClass *class)
+{
+  GObjectClass   *object_class = G_OBJECT_CLASS (class);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+  object_class->constructor  = gtk_tree_menu_constructor;
+  object_class->dispose      = gtk_tree_menu_dispose;
+  object_class->finalize     = gtk_tree_menu_finalize;
+  object_class->set_property = gtk_tree_menu_set_property;
+  object_class->get_property = gtk_tree_menu_get_property;
+
+  widget_class->get_preferred_width            = gtk_tree_menu_get_preferred_width;
+  widget_class->get_preferred_height           = gtk_tree_menu_get_preferred_height;
+  widget_class->size_allocate                  = gtk_tree_menu_size_allocate;
+
+  g_object_class_install_property (object_class,
+                                   PROP_MODEL,
+                                   g_param_spec_object ("model",
+                                                        P_("TreeMenu model"),
+                                                        P_("The model for the tree menu"),
+                                                        GTK_TYPE_TREE_MODEL,
+                                                        GTK_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class,
+                                   PROP_ROOT,
+                                   g_param_spec_boxed ("root",
+						       P_("TreeMenu root row"),
+						       P_("The TreeMenu will display children of the "
+							  "specified root"),
+						       GTK_TYPE_TREE_ROW_REFERENCE,
+						       GTK_PARAM_READWRITE));
+
+   g_object_class_install_property (object_class,
+                                    PROP_CELL_AREA,
+                                    g_param_spec_object ("cell-area",
+							 P_("Cell Area"),
+							 P_("The GtkCellArea used to layout cells"),
+							 GTK_TYPE_CELL_AREA,
+							 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+
+   g_type_class_add_private (object_class, sizeof (GtkTreeMenuPrivate));
+}
+
+/****************************************************************
+ *                         GObjectClass                         *
+ ****************************************************************/
+static GObject  *
+gtk_tree_menu_constructor (GType                  type,
+			   guint                  n_construct_properties,
+			   GObjectConstructParam *construct_properties)
+{
+  GObject            *object;
+  GtkTreeMenu        *menu;
+  GtkTreeMenuPrivate *priv;
+
+  object = G_OBJECT_CLASS (gtk_tree_menu_parent_class)->constructor
+    (type, n_construct_properties, construct_properties);
+
+  menu = GTK_TREE_MENU (object);
+  priv = menu->priv;
+
+  if (!priv->area)
+    {
+      GtkCellArea *area = gtk_cell_area_box_new ();
+
+      gtk_tree_menu_set_area (menu, area);
+    }
+
+  priv->context = gtk_cell_area_create_context (priv->area);
+  priv->size_changed_id = 
+    g_signal_connect (priv->context, "notify",
+		      G_CALLBACK (context_size_changed_cb), menu);
+
+
+  return object;
+}
+
+static void
+gtk_tree_menu_dispose (GObject *object)
+{
+  GtkTreeMenu        *menu;
+  GtkTreeMenuPrivate *priv;
+
+  menu = GTK_TREE_MENU (object);
+  priv = menu->priv;
+
+  gtk_tree_menu_set_model (menu, NULL);
+  gtk_tree_menu_set_area (menu, NULL);
+
+  if (priv->context)
+    {
+      /* Disconnect signals */
+      g_signal_handler_disconnect (priv->context, priv->size_changed_id);
+
+      g_object_unref (priv->context);
+      priv->context = NULL;
+      priv->size_changed_id = 0;
+    }
+
+  G_OBJECT_CLASS (gtk_tree_menu_parent_class)->dispose (object);
+}
+
+static void
+gtk_tree_menu_finalize (GObject *object)
+{
+
+  G_OBJECT_CLASS (gtk_tree_menu_parent_class)->finalize (object);
+}
+
+static void
+gtk_tree_menu_set_property (GObject            *object,
+			    guint               prop_id,
+			    const GValue       *value,
+			    GParamSpec         *pspec)
+{
+  GtkTreeMenu *menu = GTK_TREE_MENU (object);
+
+  switch (prop_id)
+    {
+    case PROP_MODEL:
+      gtk_tree_menu_set_model (menu, g_value_get_object (value));
+      break;
+
+    case PROP_ROOT:
+      gtk_tree_menu_set_root (menu, g_value_get_boxed (value));
+      break;
+
+    case PROP_CELL_AREA:
+      /* Construct-only, can only be assigned once */
+      gtk_tree_menu_set_area (menu, (GtkCellArea *)g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_tree_menu_get_property (GObject            *object,
+			    guint               prop_id,
+			    GValue             *value,
+			    GParamSpec         *pspec)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (object);
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  switch (prop_id)
+    {
+      case PROP_MODEL:
+        g_value_set_object (value, priv->model);
+        break;
+
+      case PROP_ROOT:
+        g_value_set_boxed (value, priv->root);
+        break;
+
+      case PROP_CELL_AREA:
+	g_value_set_object (value, priv->area);
+	break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+/****************************************************************
+ *                         GtkWidgetClass                       *
+ ****************************************************************/
+
+/* We tell all the menu items to reserve space for the submenu
+ * indicator if there is at least one submenu, this way we ensure
+ * that every internal cell area gets allocated the
+ * same width (and requested height for the same appropriate width).
+ */
+static void
+sync_reserve_submenu_size (GtkTreeMenu *menu)
+{
+  GList              *children, *l;
+  gboolean            has_submenu = FALSE;
+
+  children = gtk_container_get_children (GTK_CONTAINER (menu));
+  for (l = children; l; l = l->next)
+    {
+      GtkMenuItem *item = l->data;
+
+      if (gtk_menu_item_get_submenu (item) != NULL)
+	{
+	  has_submenu = TRUE;
+	  break;
+	}
+    }
+
+  for (l = children; l; l = l->next)
+    {
+      GtkMenuItem *item = l->data;
+
+      gtk_menu_item_set_reserve_indicator (item, has_submenu);
+    }
+
+  g_list_free (children);
+}
+
+static void
+gtk_tree_menu_get_preferred_width (GtkWidget           *widget,
+				   gint                *minimum_size,
+				   gint                *natural_size)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
+  GtkTreeMenuPrivate *priv = menu->priv;
+  GtkTreePath        *path = NULL;
+  GtkTreeIter         iter;
+  gboolean            valid = FALSE;
+
+  g_signal_handler_block (priv->context, priv->size_changed_id);
+
+  /* Before chaining up to the parent class and requesting the 
+   * menu item/cell view sizes, we need to request the size of
+   * each row for this menu and make sure all the cellviews 
+   * request enough space 
+   */
+  gtk_cell_area_context_flush_preferred_width (priv->context);
+
+  sync_reserve_submenu_size (menu);
+
+  if (priv->model)
+    {
+      if (priv->root)
+	path = gtk_tree_row_reference_get_path (priv->root);
+      
+      if (path)
+	{
+	  GtkTreeIter parent;
+
+	  if (gtk_tree_model_get_iter (priv->model, &parent, path))
+	    valid = gtk_tree_model_iter_children (priv->model, &iter, &parent);
+	  
+	  gtk_tree_path_free (path);
+	}
+      else
+	valid = gtk_tree_model_iter_children (priv->model, &iter, NULL);
+      
+      while (valid)
+	{
+	  gboolean is_separator = FALSE;
+	  
+	  if (priv->row_separator_func)
+	    is_separator = 
+	      priv->row_separator_func (priv->model, &iter, priv->row_separator_data);
+
+	  if (!is_separator)
+	    {
+	      gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
+	      gtk_cell_area_get_preferred_width (priv->area, priv->context, widget, NULL, NULL);
+	    }
+	  
+	  valid = gtk_tree_model_iter_next (priv->model, &iter);
+	}
+    }
+
+  gtk_cell_area_context_sum_preferred_width (priv->context);
+
+  g_signal_handler_unblock (priv->context, priv->size_changed_id);
+
+  /* Now that we've requested all the row's and updated priv->context properly, we can go ahead
+   * and calculate the sizes by requesting the menu items and thier cell views */
+  GTK_WIDGET_CLASS (gtk_tree_menu_parent_class)->get_preferred_width (widget, minimum_size, natural_size);
+}
+
+static void
+gtk_tree_menu_get_preferred_height (GtkWidget           *widget,
+				    gint                *minimum_size,
+				    gint                *natural_size)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
+  GtkTreeMenuPrivate *priv = menu->priv;
+  GtkTreePath        *path = NULL;
+  GtkTreeIter         iter;
+  gboolean            valid = FALSE;
+
+  g_signal_handler_block (priv->context, priv->size_changed_id);
+
+  /* Before chaining up to the parent class and requesting the 
+   * menu item/cell view sizes, we need to request the size of
+   * each row for this menu and make sure all the cellviews 
+   * request enough space 
+   */
+  gtk_cell_area_context_flush_preferred_height (priv->context);
+
+  sync_reserve_submenu_size (menu);
+
+  if (priv->model)
+    {
+      if (priv->root)
+	path = gtk_tree_row_reference_get_path (priv->root);
+      
+      if (path)
+	{
+	  GtkTreeIter parent;
+
+	  if (gtk_tree_model_get_iter (priv->model, &parent, path))
+	    valid = gtk_tree_model_iter_children (priv->model, &iter, &parent);
+	  
+	  gtk_tree_path_free (path);
+	}
+      else
+	valid = gtk_tree_model_iter_children (priv->model, &iter, NULL);
+      
+      while (valid)
+	{
+	  gboolean is_separator = FALSE;
+	  
+	  if (priv->row_separator_func)
+	    is_separator = 
+	      priv->row_separator_func (priv->model, &iter, priv->row_separator_data);
+
+	  if (!is_separator)
+	    {
+	      gtk_cell_area_apply_attributes (priv->area, priv->model, &iter, FALSE, FALSE);
+	      gtk_cell_area_get_preferred_height (priv->area, priv->context, widget, NULL, NULL);
+	    }
+	  
+	  valid = gtk_tree_model_iter_next (priv->model, &iter);
+	}
+    }
+
+  gtk_cell_area_context_sum_preferred_height (priv->context);
+
+  g_signal_handler_unblock (priv->context, priv->size_changed_id);
+
+  /* Now that we've requested all the row's and updated priv->context properly, we can go ahead
+   * and calculate the sizes by requesting the menu items and thier cell views */
+  GTK_WIDGET_CLASS (gtk_tree_menu_parent_class)->get_preferred_height (widget, minimum_size, natural_size);
+}
+
+static void
+gtk_tree_menu_size_allocate (GtkWidget           *widget,
+			     GtkAllocation       *allocation)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (widget);
+  GtkTreeMenuPrivate *priv = menu->priv;
+  gint                new_width, new_height;
+
+  /* flush the context allocation */
+  gtk_cell_area_context_flush_allocation (priv->context);
+
+  /* Leave it to the first cell area to allocate the size of priv->context, since
+   * we configure the menu to allocate all children the same width this should work fine */
+  GTK_WIDGET_CLASS (gtk_tree_menu_parent_class)->size_allocate (widget, allocation);
+
+  /* In alot of cases the menu gets allocated while the children dont need
+   * any reallocation, in this case we need to restore the context allocation */
+  gtk_cell_area_context_get_allocation (priv->context, &new_width, &new_height);
+
+  if (new_width <= 0 && new_height <= 0)
+    {
+      gtk_cell_area_context_allocate_width (priv->context, priv->last_alloc_width);
+      gtk_cell_area_context_allocate_height (priv->context, priv->last_alloc_height);
+    }
+
+  /* Save the allocation for the next round */
+  gtk_cell_area_context_get_allocation (priv->context, 
+					&priv->last_alloc_width, 
+					&priv->last_alloc_height);
+}
+
+/****************************************************************
+ *                      GtkCellLayoutIface                      *
+ ****************************************************************/
+/* Just forward all the GtkCellLayoutIface methods to the 
+ * underlying GtkCellArea
+ */
+static void
+gtk_tree_menu_cell_layout_init (GtkCellLayoutIface  *iface)
+{
+  iface->pack_start         = gtk_tree_menu_cell_layout_pack_start;
+  iface->pack_end           = gtk_tree_menu_cell_layout_pack_end;
+  iface->get_cells          = gtk_tree_menu_cell_layout_get_cells;
+  iface->clear              = gtk_tree_menu_cell_layout_clear;
+  iface->add_attribute      = gtk_tree_menu_cell_layout_add_attribute;
+  iface->set_cell_data_func = gtk_tree_menu_cell_layout_set_cell_data_func;
+  iface->clear_attributes   = gtk_tree_menu_cell_layout_clear_attributes;
+  iface->reorder            = gtk_tree_menu_cell_layout_reorder;
+  iface->get_area           = gtk_tree_menu_cell_layout_get_area;
+}
+
+static void
+gtk_tree_menu_cell_layout_pack_start (GtkCellLayout       *layout,
+				      GtkCellRenderer     *cell,
+				      gboolean             expand)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->area), cell, expand);
+}
+
+static void
+gtk_tree_menu_cell_layout_pack_end (GtkCellLayout        *layout,
+				    GtkCellRenderer      *cell,
+				    gboolean              expand)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->area), cell, expand);
+}
+
+static GList *
+gtk_tree_menu_cell_layout_get_cells (GtkCellLayout *layout)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  return gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->area));
+}
+
+static void
+gtk_tree_menu_cell_layout_clear (GtkCellLayout *layout)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  gtk_cell_layout_clear (GTK_CELL_LAYOUT (priv->area));
+}
+
+static void
+gtk_tree_menu_cell_layout_add_attribute (GtkCellLayout        *layout,
+					 GtkCellRenderer      *cell,
+					 const gchar          *attribute,
+					 gint                  column)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->area), cell, attribute, column);
+}
+
+static void
+gtk_tree_menu_cell_layout_set_cell_data_func (GtkCellLayout        *layout,
+					      GtkCellRenderer      *cell,
+					      GtkCellLayoutDataFunc func,
+					      gpointer              func_data,
+					      GDestroyNotify        destroy)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->area), cell, func, func_data, destroy);
+}
+
+static void
+gtk_tree_menu_cell_layout_clear_attributes (GtkCellLayout        *layout,
+					    GtkCellRenderer      *cell)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->area), cell);
+}
+
+static void
+gtk_tree_menu_cell_layout_reorder (GtkCellLayout        *layout,
+				   GtkCellRenderer      *cell,
+				   gint                  position)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->area), cell, position);
+}
+
+static GtkCellArea *
+gtk_tree_menu_cell_layout_get_area (GtkCellLayout *layout)
+{
+  GtkTreeMenu        *menu = GTK_TREE_MENU (layout);
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  return priv->area;
+}
+
+
+/****************************************************************
+ *             TreeModel callbacks/populating menus             *
+ ****************************************************************/
+static void
+context_size_changed_cb (GtkCellAreaContext  *context,
+			 GParamSpec          *pspec,
+			 GtkWidget           *menu)
+{
+  if (!strcmp (pspec->name, "minimum-width") ||
+      !strcmp (pspec->name, "natural-width") ||
+      !strcmp (pspec->name, "minimum-height") ||
+      !strcmp (pspec->name, "natural-height"))
+    queue_resize_all (menu);
+}
+
+static void
+queue_resize_all (GtkWidget *menu)
+{
+  GList *children, *l;
+
+  children = gtk_container_get_children (GTK_CONTAINER (menu));
+  for (l = children; l; l = l->next)
+    {
+      GtkWidget *widget = l->data;
+
+      gtk_widget_queue_resize (widget);
+    }
+
+  g_list_free (children);
+
+  gtk_widget_queue_resize (menu);
+}
+
+
+static void
+gtk_tree_menu_set_area (GtkTreeMenu *menu,
+			GtkCellArea *area)
+{
+  GtkTreeMenuPrivate *priv = menu->priv;
+
+  if (priv->area)
+    g_object_unref (area);
+
+  priv->area = area;
+
+  if (priv->area)
+    g_object_ref_sink (area);
+}
+
+
+static GtkWidget *
+gtk_tree_menu_create_item (GtkTreeMenu *menu,
+			   GtkTreeIter *iter)
+{
+  GtkTreeMenuPrivate *priv = menu->priv;
+  GtkWidget          *item, *view;
+  GtkTreePath        *path;
+
+  view = gtk_cell_view_new_with_context (priv->area, priv->context);
+  item = gtk_menu_item_new ();
+  gtk_widget_show (view);
+  gtk_widget_show (item);
+
+  path = gtk_tree_model_get_path (priv->model, iter);
+
+  gtk_cell_view_set_model (GTK_CELL_VIEW (view), priv->model);
+  gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (view), path);
+
+  gtk_tree_path_free (path);
+
+  gtk_widget_show (view);
+  gtk_container_add (GTK_CONTAINER (item), view);
+
+  return item;
+}
+
+static void
+gtk_tree_menu_populate (GtkTreeMenu *menu)
+{
+  GtkTreeMenuPrivate *priv = menu->priv;
+  GtkTreePath        *path = NULL;
+  GtkTreeIter         parent;
+  GtkTreeIter         iter;
+  gboolean            valid = FALSE;
+  GtkWidget          *menu_item;
+
+  if (!priv->model)
+    return;
+
+  if (priv->root)
+    path = gtk_tree_row_reference_get_path (priv->root);
+
+  if (path)
+    {
+      if (gtk_tree_model_get_iter (priv->model, &parent, path))
+	valid = gtk_tree_model_iter_children (priv->model, &iter, &parent);
+
+      gtk_tree_path_free (path);
+    }
+  else
+    valid = gtk_tree_model_iter_children (priv->model, &iter, NULL);
+
+  /* Create a menu item for every row at the current depth, add a GtkTreeMenu
+   * submenu for iters/items that have children */
+  while (valid)
+    {
+      gboolean is_separator = FALSE;
+
+      if (priv->row_separator_func)
+	is_separator = 
+	  priv->row_separator_func (priv->model, &iter, 
+				    priv->row_separator_data);
+
+      if (is_separator)
+	menu_item = gtk_separator_menu_item_new ();
+      else
+	{
+	  menu_item = gtk_tree_menu_create_item (menu, &iter);
+
+	  /* Add a GtkTreeMenu submenu to render the children of this row */
+	  if (gtk_tree_model_iter_has_child (priv->model, &iter))
+	    {
+	      GtkTreePath         *row_path;
+	      GtkWidget           *submenu;
+	      
+	      row_path = gtk_tree_model_get_path (priv->model, &iter);
+	      submenu  = gtk_tree_menu_new_with_area (priv->area);
+
+	      gtk_tree_menu_set_model (GTK_TREE_MENU (submenu), priv->model);
+	      gtk_tree_menu_set_root  (GTK_TREE_MENU (submenu), row_path);
+
+	      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), submenu);
+
+	      gtk_tree_path_free (row_path);
+	    }
+	}
+
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+
+      valid = gtk_tree_model_iter_next (priv->model, &iter);
+    }
+}
+
+/****************************************************************
+ *                            API                               *
+ ****************************************************************/
+GtkWidget *
+gtk_tree_menu_new (void)
+{
+  return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU, NULL);
+}
+
+GtkWidget *
+gtk_tree_menu_new_with_area (GtkCellArea    *area)
+{
+  return (GtkWidget *)g_object_new (GTK_TYPE_TREE_MENU, 
+				    "area", area, 
+				    NULL);
+}
+
+void
+gtk_tree_menu_set_model (GtkTreeMenu  *menu,
+			 GtkTreeModel *model)
+{
+  GtkTreeMenuPrivate *priv;
+
+  g_return_if_fail (GTK_IS_TREE_MENU (menu));
+  g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
+
+  priv = menu->priv;
+
+  if (priv->model != model)
+    {
+      if (priv->model)
+	{
+	  /* Disconnect signals */
+
+	  g_object_unref (priv->model);
+	}
+
+      priv->model = model;
+
+      if (priv->model)
+	{
+	  /* Connect signals */
+
+	  g_object_ref (priv->model);
+	}
+
+      /* Changing the model in any way invalidates the currently set root, 
+       * so we implicitly reset it to NULL here */
+      gtk_tree_menu_set_root (menu, NULL);
+
+      g_object_notify (G_OBJECT (menu), "model");
+    }
+}
+
+GtkTreeModel *
+gtk_tree_menu_get_model (GtkTreeMenu *menu)
+{
+  GtkTreeMenuPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
+
+  priv = menu->priv;
+
+  return priv->model;
+}
+
+void
+gtk_tree_menu_set_root (GtkTreeMenu         *menu,
+			GtkTreePath         *path)
+{
+  GtkTreeMenuPrivate *priv;
+
+  g_return_if_fail (GTK_IS_TREE_MENU (menu));
+  g_return_if_fail (path == NULL || menu->priv->model != NULL);
+
+  priv = menu->priv;
+
+  if (priv->root) 
+    gtk_tree_row_reference_free (priv->root);
+
+  if (path)
+    priv->root = gtk_tree_row_reference_new (priv->model, path);
+  else
+    priv->root = NULL;
+
+  /* Destroy all the menu items for the previous root */
+  gtk_container_foreach (GTK_CONTAINER (menu), 
+			 (GtkCallback) gtk_widget_destroy, NULL);
+  
+  /* Populate for the new root */
+  gtk_tree_menu_populate (menu);
+}
+
+GtkTreePath *
+gtk_tree_menu_get_root (GtkTreeMenu *menu)
+{
+  GtkTreeMenuPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
+
+  priv = menu->priv;
+
+  if (priv->root)
+    return gtk_tree_row_reference_get_path (priv->root);
+
+  return NULL;
+}
+
+void
+gtk_tree_menu_set_row_separator_func (GtkTreeMenu          *menu,
+				      GtkTreeViewRowSeparatorFunc func,
+				      gpointer              data,
+				      GDestroyNotify        destroy)
+{
+  GtkTreeMenuPrivate *priv;
+
+  g_return_if_fail (GTK_IS_TREE_MENU (menu));
+
+  priv = menu->priv;
+
+  if (priv->row_separator_destroy)
+    priv->row_separator_destroy (priv->row_separator_data);
+
+  priv->row_separator_func    = func;
+  priv->row_separator_data    = data;
+  priv->row_separator_destroy = destroy;
+}
+
+GtkTreeViewRowSeparatorFunc
+gtk_tree_menu_get_row_separator_func (GtkTreeMenu *menu)
+{
+  GtkTreeMenuPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
+
+  priv = menu->priv;
+
+  return priv->row_separator_func;
+}
diff --git a/gtk/gtktreemenu.h b/gtk/gtktreemenu.h
new file mode 100644
index 0000000..994e293
--- /dev/null
+++ b/gtk/gtktreemenu.h
@@ -0,0 +1,91 @@
+/* gtktreemenu.h
+ *
+ * Copyright (C) 2010 Openismus GmbH
+ *
+ * Authors:
+ *      Tristan Van Berkom <tristanvb openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#ifndef __GTK_TREE_MENU_H__
+#define __GTK_TREE_MENU_H__
+
+#include <gtk/gtkmenu.h>
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkcellarea.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_TREE_MENU		  (gtk_tree_menu_get_type ())
+#define GTK_TREE_MENU(obj)		  (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TREE_MENU, GtkTreeMenu))
+#define GTK_TREE_MENU_CLASS(klass)	  (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TREE_MENU, GtkTreeMenuClass))
+#define GTK_IS_TREE_MENU(obj)	  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TREE_MENU))
+#define GTK_IS_TREE_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TREE_MENU))
+#define GTK_TREE_MENU_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TREE_MENU, GtkTreeMenuClass))
+
+typedef struct _GtkTreeMenu              GtkTreeMenu;
+typedef struct _GtkTreeMenuClass         GtkTreeMenuClass;
+typedef struct _GtkTreeMenuPrivate       GtkTreeMenuPrivate;
+
+
+struct _GtkTreeMenu
+{
+  GtkMenu parent_instance;
+
+  GtkTreeMenuPrivate *priv;
+};
+
+struct _GtkTreeMenuClass
+{
+  GtkMenuClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_gtk_reserved1) (void);
+  void (*_gtk_reserved2) (void);
+  void (*_gtk_reserved3) (void);
+  void (*_gtk_reserved4) (void);
+  void (*_gtk_reserved5) (void);
+  void (*_gtk_reserved6) (void);
+  void (*_gtk_reserved7) (void);
+  void (*_gtk_reserved8) (void);
+};
+
+GType                 gtk_tree_menu_get_type                       (void) G_GNUC_CONST;
+
+GtkWidget            *gtk_tree_menu_new                            (void);
+GtkWidget            *gtk_tree_menu_new_with_area                  (GtkCellArea         *area);
+void                  gtk_tree_menu_set_model                      (GtkTreeMenu         *menu,
+								    GtkTreeModel        *model);
+GtkTreeModel         *gtk_tree_menu_get_model                      (GtkTreeMenu         *menu);
+void                  gtk_tree_menu_set_root                       (GtkTreeMenu         *menu,
+								    GtkTreePath         *path);
+GtkTreePath          *gtk_tree_menu_get_root                       (GtkTreeMenu         *menu);
+
+void                        gtk_tree_menu_set_row_separator_func   (GtkTreeMenu          *menu,
+								    GtkTreeViewRowSeparatorFunc func,
+								    gpointer              data,
+								    GDestroyNotify        destroy);
+GtkTreeViewRowSeparatorFunc gtk_tree_menu_get_row_separator_func   (GtkTreeMenu          *menu);
+
+G_END_DECLS
+
+#endif /* __GTK_TREE_MENU_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2832853..4353882 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -98,8 +98,9 @@ noinst_PROGRAMS =  $(TEST_PROGS)	\
 	testexpander			\
 	testvolumebutton		\
 	testscrolledwindow		\
+	testswitch			\
 	testcellarea			\
-	testswitch
+	testtreemenu
 
 if USE_X11
 noinst_PROGRAMS += testerrors
@@ -188,6 +189,7 @@ testtooltips_DEPENDENCIES = $(TEST_DEPS)
 testvolumebutton_DEPENDENCIES = $(TEST_DEPS)
 testscrolledwindow_DEPENDENCIES = $(TEST_DEPS)
 testcellarea_DEPENDENCIES = $(TEST_DEPS)
+testtreemenu_DEPENDENCIES = $(TEST_DEPS)
 testwindows_DEPENDENCIES = $(TEST_DEPS)
 testexpand_DEPENDENCIES = $(TEST_DEPS)
 testexpander_DEPENDENCIES = $(TEST_DEPS)
@@ -266,6 +268,7 @@ testtooltips_LDADD = $(LDADDS)
 testvolumebutton_LDADD = $(LDADDS)
 testscrolledwindow_LDADD = $(LDADDS)
 testcellarea_LDADD = $(LDADDS)
+testtreemenu_LDADD = $(LDADDS)
 testwindows_LDADD = $(LDADDS)
 testexpand_LDADD = $(LDADDS)
 testexpander_LDADD = $(LDADDS)
@@ -384,6 +387,9 @@ testcellarea_SOURCES =		\
 	cellareascaffold.c	\
 	cellareascaffold.h
 
+testtreemenu_SOURCES =		\
+	testtreemenu.c
+
 testoffscreen_SOURCES = 	\
 	gtkoffscreenbox.c	\
 	gtkoffscreenbox.h	\
diff --git a/tests/testtreemenu.c b/tests/testtreemenu.c
new file mode 100644
index 0000000..84c8564
--- /dev/null
+++ b/tests/testtreemenu.c
@@ -0,0 +1,245 @@
+#include <gtk/gtk.h>
+#include "cellareascaffold.h"
+
+/*******************************************************
+ *                      Simple Test                    *
+ *******************************************************/
+enum {
+  SIMPLE_COLUMN_NAME,
+  SIMPLE_COLUMN_ICON,
+  SIMPLE_COLUMN_DESCRIPTION,
+  N_SIMPLE_COLUMNS
+};
+
+static GtkCellRenderer *cell_1 = NULL, *cell_2 = NULL, *cell_3 = NULL;
+
+static GtkTreeModel *
+simple_tree_model (void)
+{
+  GtkTreeIter   iter;
+  GtkTreeStore *store = 
+    gtk_tree_store_new (N_SIMPLE_COLUMNS,
+			G_TYPE_STRING,  /* name text */
+			G_TYPE_STRING,  /* icon name */
+			G_TYPE_STRING); /* description text */
+
+  gtk_tree_store_append (store, &iter, NULL);
+  gtk_tree_store_set (store, &iter, 
+		      SIMPLE_COLUMN_NAME, "Alice in wonderland",
+		      SIMPLE_COLUMN_ICON, "gtk-execute",
+		      SIMPLE_COLUMN_DESCRIPTION, 
+		      "Twas brillig, and the slithy toves "
+		      "did gyre and gimble in the wabe",
+		      -1);
+
+  gtk_tree_store_append (store, &iter, NULL);
+  gtk_tree_store_set (store, &iter, 
+		      SIMPLE_COLUMN_NAME, "Marry Poppins",
+		      SIMPLE_COLUMN_ICON, "gtk-yes",
+		      SIMPLE_COLUMN_DESCRIPTION, "Supercalifragilisticexpialidocious",
+		      -1);
+
+  gtk_tree_store_append (store, &iter, NULL);
+  gtk_tree_store_set (store, &iter, 
+		      SIMPLE_COLUMN_NAME, "George Bush",
+		      SIMPLE_COLUMN_ICON, "gtk-dialog-warning",
+		      SIMPLE_COLUMN_DESCRIPTION, "It's a very good question, very direct, "
+		      "and I'm not going to answer it",
+		      -1);
+
+  gtk_tree_store_append (store, &iter, NULL);
+  gtk_tree_store_set (store, &iter, 
+		      SIMPLE_COLUMN_NAME, "Whinnie the pooh",
+		      SIMPLE_COLUMN_ICON, "gtk-stop",
+		      SIMPLE_COLUMN_DESCRIPTION, "The most wonderful thing about tiggers, "
+		      "is tiggers are wonderful things",
+		      -1);
+
+  gtk_tree_store_append (store, &iter, NULL);
+  gtk_tree_store_set (store, &iter, 
+		      SIMPLE_COLUMN_NAME, "Aleister Crowley",
+		      SIMPLE_COLUMN_ICON, "gtk-about",
+		      SIMPLE_COLUMN_DESCRIPTION, 
+		      "Thou shalt do what thou wilt shall be the whole of the law",
+		      -1);
+
+  gtk_tree_store_append (store, &iter, NULL);
+  gtk_tree_store_set (store, &iter, 
+		      SIMPLE_COLUMN_NAME, "Mark Twain",
+		      SIMPLE_COLUMN_ICON, "gtk-quit",
+		      SIMPLE_COLUMN_DESCRIPTION, 
+		      "Giving up smoking is the easiest thing in the world. "
+		      "I know because I've done it thousands of times.",
+		      -1);
+
+
+  return (GtkTreeModel *)store;
+}
+
+static GtkWidget *
+simple_tree_menu (void)
+{
+  GtkTreeModel *model;
+  GtkWidget *menu;
+  GtkCellArea *area;
+  GtkCellRenderer *renderer;
+
+  model = simple_tree_model ();
+
+  menu = gtk_tree_menu_new ();
+  gtk_tree_menu_set_model (GTK_TREE_MENU (menu), model);
+
+  area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (menu));
+
+  cell_1 = renderer = gtk_cell_renderer_text_new ();
+  gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, FALSE, FALSE);
+  gtk_cell_area_attribute_connect (area, renderer, "text", SIMPLE_COLUMN_NAME);
+
+  cell_2 = renderer = gtk_cell_renderer_pixbuf_new ();
+  g_object_set (G_OBJECT (renderer), "xalign", 0.0F, NULL);
+  gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, TRUE, FALSE);
+  gtk_cell_area_attribute_connect (area, renderer, "stock-id", SIMPLE_COLUMN_ICON);
+
+  cell_3 = renderer = gtk_cell_renderer_text_new ();
+  g_object_set (G_OBJECT (renderer), 
+		"wrap-mode", PANGO_WRAP_WORD,
+		"wrap-width", 215,
+		NULL);
+  gtk_cell_area_box_pack_start (GTK_CELL_AREA_BOX (area), renderer, FALSE, TRUE);
+  gtk_cell_area_attribute_connect (area, renderer, "text", SIMPLE_COLUMN_DESCRIPTION);
+
+  return menu;
+}
+
+static void
+align_cell_2_toggled (GtkToggleButton  *toggle,
+		      GtkTreeMenu    *menu)
+{
+  GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (menu));
+  gboolean     align = gtk_toggle_button_get_active (toggle);
+
+  gtk_cell_area_cell_set (area, cell_2, "align", align, NULL);
+}
+
+static void
+align_cell_3_toggled (GtkToggleButton  *toggle,
+		      GtkTreeMenu    *menu)
+{
+  GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (menu));
+  gboolean     align = gtk_toggle_button_get_active (toggle);
+
+  gtk_cell_area_cell_set (area, cell_3, "align", align, NULL);
+}
+
+static void
+expand_cell_1_toggled (GtkToggleButton  *toggle,
+		       GtkTreeMenu      *menu)
+{
+  GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (menu));
+  gboolean     expand = gtk_toggle_button_get_active (toggle);
+
+  gtk_cell_area_cell_set (area, cell_1, "expand", expand, NULL);
+}
+
+static void
+expand_cell_2_toggled (GtkToggleButton  *toggle,
+		       GtkTreeMenu      *menu)
+{
+  GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (menu));
+  gboolean     expand = gtk_toggle_button_get_active (toggle);
+
+  gtk_cell_area_cell_set (area, cell_2, "expand", expand, NULL);
+}
+
+static void
+expand_cell_3_toggled (GtkToggleButton  *toggle,
+		       GtkTreeMenu      *menu)
+{
+  GtkCellArea *area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (menu));
+  gboolean     expand = gtk_toggle_button_get_active (toggle);
+
+  gtk_cell_area_cell_set (area, cell_3, "expand", expand, NULL);
+}
+
+static void
+tree_menu (void)
+{
+  GtkWidget *window, *widget;
+  GtkWidget *menu, *menubar, *vbox, *menuitem;
+
+  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+  gtk_window_set_title (GTK_WINDOW (window), "GtkTreeMenu");
+
+  menu = simple_tree_menu ();
+
+  vbox  = gtk_vbox_new (FALSE, 4);
+  gtk_widget_show (vbox);
+
+  menubar = gtk_menu_bar_new ();
+  menuitem = gtk_menu_item_new_with_label ("Tree");
+  gtk_widget_show (menu);
+  gtk_widget_show (menubar);
+  gtk_widget_show (menuitem);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menubar), menuitem);
+  gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
+
+  gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
+
+  /* Now add some controls */
+  widget = gtk_check_button_new_with_label ("Align 2nd Cell");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+  
+  g_signal_connect (G_OBJECT (widget), "toggled",
+                    G_CALLBACK (align_cell_2_toggled), menu);
+
+  widget = gtk_check_button_new_with_label ("Align 3rd Cell");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+  
+  g_signal_connect (G_OBJECT (widget), "toggled",
+                    G_CALLBACK (align_cell_3_toggled), menu);
+
+  widget = gtk_check_button_new_with_label ("Expand 1st Cell");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+  
+  g_signal_connect (G_OBJECT (widget), "toggled",
+                    G_CALLBACK (expand_cell_1_toggled), menu);
+
+  widget = gtk_check_button_new_with_label ("Expand 2nd Cell");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+  
+  g_signal_connect (G_OBJECT (widget), "toggled",
+                    G_CALLBACK (expand_cell_2_toggled), menu);
+
+  widget = gtk_check_button_new_with_label ("Expand 3rd Cell");
+  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+  gtk_widget_show (widget);
+  gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
+  
+  g_signal_connect (G_OBJECT (widget), "toggled",
+                    G_CALLBACK (expand_cell_3_toggled), menu);
+
+  gtk_container_add (GTK_CONTAINER (window), vbox);
+
+  gtk_widget_show (window);
+}
+
+int
+main (int argc, char *argv[])
+{
+  gtk_init (NULL, NULL);
+
+  tree_menu ();
+
+  gtk_main ();
+
+  return 0;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]