[gtk+/combo-refactor: 15/23] Added GtkTreeMenuHeaderFunc to decide if a submenu gets a leaf header.



commit 8772c1e888fa6f6caacb2913b4b9de4c246a4439
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Sat Nov 20 16:32:24 2010 +0900

    Added GtkTreeMenuHeaderFunc to decide if a submenu gets a leaf header.
    
    GtkComboBox needs treemenus to allow selection of all leafs including
    rows which may have children, this allows the combobox or combobox user
    to decide which row that has children can also be selectable as a header
    leaf of the submenu. Test case testtreemenu updated to reflect this.

 gtk/gtktreemenu.c    |  181 ++++++++++++++++++++++++++++++++++++++++++--------
 gtk/gtktreemenu.h    |    9 +++
 tests/testtreemenu.c |   72 ++++++++++++++++++++-
 3 files changed, 233 insertions(+), 29 deletions(-)
---
diff --git a/gtk/gtktreemenu.c b/gtk/gtktreemenu.c
index e80ed45..d6586b2 100644
--- a/gtk/gtktreemenu.c
+++ b/gtk/gtktreemenu.c
@@ -24,6 +24,7 @@
 #include "config.h"
 #include "gtkintl.h"
 #include "gtktreemenu.h"
+#include "gtkmarshalers.h"
 #include "gtkmenuitem.h"
 #include "gtkseparatormenuitem.h"
 #include "gtkcellareabox.h"
@@ -91,10 +92,14 @@ static GtkWidget *gtk_tree_menu_create_item                   (GtkTreeMenu
 							       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);
+static void      item_activated_cb                            (GtkMenuItem          *item,
+							       GtkTreeMenu          *menu);
+static void      submenu_activated_cb                         (GtkTreeMenu          *submenu,
+							       const gchar          *path,
+							       GtkTreeMenu          *menu);
 
 struct _GtkTreeMenuPrivate
 {
@@ -116,6 +121,11 @@ struct _GtkTreeMenuPrivate
   GtkTreeViewRowSeparatorFunc row_separator_func;
   gpointer                    row_separator_data;
   GDestroyNotify              row_separator_destroy;
+
+  /* Submenu headers */
+  GtkTreeMenuHeaderFunc header_func;
+  gpointer              header_data;
+  GDestroyNotify        header_destroy;
 };
 
 enum {
@@ -125,6 +135,13 @@ enum {
   PROP_CELL_AREA
 };
 
+enum {
+  SIGNAL_MENU_ACTIVATE,
+  N_SIGNALS
+};
+
+static guint tree_menu_signals[N_SIGNALS] = { 0 };
+
 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));
@@ -158,6 +175,15 @@ gtk_tree_menu_class_init (GtkTreeMenuClass *class)
   widget_class->get_preferred_height           = gtk_tree_menu_get_preferred_height;
   widget_class->size_allocate                  = gtk_tree_menu_size_allocate;
 
+  tree_menu_signals[SIGNAL_MENU_ACTIVATE] =
+    g_signal_new (I_("menu-activate"),
+		  G_OBJECT_CLASS_TYPE (object_class),
+		  G_SIGNAL_RUN_FIRST,
+		  0, /* No class closure here */
+		  NULL, NULL,
+		  _gtk_marshal_VOID__STRING,
+		  G_TYPE_NONE, 1, G_TYPE_STRING);
+
   g_object_class_install_property (object_class,
                                    PROP_MODEL,
                                    g_param_spec_object ("model",
@@ -249,6 +275,17 @@ gtk_tree_menu_dispose (GObject *object)
 static void
 gtk_tree_menu_finalize (GObject *object)
 {
+  GtkTreeMenu        *menu;
+  GtkTreeMenuPrivate *priv;
+
+  menu = GTK_TREE_MENU (object);
+  priv = menu->priv;
+
+  gtk_tree_menu_set_row_separator_func (menu, NULL, NULL, NULL);
+  gtk_tree_menu_set_header_func (menu, NULL, NULL, NULL);
+
+  if (priv->root) 
+    gtk_tree_row_reference_free (priv->root);
 
   G_OBJECT_CLASS (gtk_tree_menu_parent_class)->finalize (object);
 }
@@ -639,44 +676,24 @@ context_size_changed_cb (GtkCellAreaContext  *context,
       !strcmp (pspec->name, "natural-width") ||
       !strcmp (pspec->name, "minimum-height") ||
       !strcmp (pspec->name, "natural-height"))
-    queue_resize_all (menu);
+    gtk_widget_queue_resize (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);
+    g_object_unref (priv->area);
 
   priv->area = area;
 
   if (priv->area)
-    g_object_ref_sink (area);
+    g_object_ref_sink (priv->area);
 }
 
-
 static GtkWidget *
 gtk_tree_menu_create_item (GtkTreeMenu *menu,
 			   GtkTreeIter *iter)
@@ -700,6 +717,8 @@ gtk_tree_menu_create_item (GtkTreeMenu *menu,
   gtk_widget_show (view);
   gtk_container_add (GTK_CONTAINER (item), view);
 
+  g_signal_connect (item, "activate", G_CALLBACK (item_activated_cb), menu);
+
   return item;
 }
 
@@ -722,8 +741,23 @@ gtk_tree_menu_populate (GtkTreeMenu *menu)
   if (path)
     {
       if (gtk_tree_model_get_iter (priv->model, &parent, path))
-	valid = gtk_tree_model_iter_children (priv->model, &iter, &parent);
+	{
+	  valid = gtk_tree_model_iter_children (priv->model, &iter, &parent);
 
+	  if (priv->header_func && 
+	      priv->header_func (priv->model, &parent, priv->header_data))
+	    {
+	      /* Add a submenu header for rows which desire one, used for
+	       * combo boxes to allow all rows to be activatable/selectable 
+	       */
+	      menu_item = gtk_tree_menu_create_item (menu, &parent);
+	      gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+	      
+	      menu_item = gtk_separator_menu_item_new ();
+	      gtk_widget_show (menu_item);
+	      gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+	    }
+	}
       gtk_tree_path_free (path);
     }
   else
@@ -753,11 +787,26 @@ gtk_tree_menu_populate (GtkTreeMenu *menu)
 	      GtkWidget           *submenu;
 	      
 	      row_path = gtk_tree_model_get_path (priv->model, &iter);
-	      submenu  = gtk_tree_menu_new_full (priv->area, priv->model, row_path);
+	      submenu  = gtk_tree_menu_new_with_area (priv->area);
+
+	      gtk_tree_menu_set_row_separator_func (GTK_TREE_MENU (submenu), 
+						    priv->row_separator_func,
+						    priv->row_separator_data,
+						    priv->row_separator_destroy);
+	      gtk_tree_menu_set_header_func (GTK_TREE_MENU (submenu), 
+					     priv->header_func,
+					     priv->header_data,
+					     priv->header_destroy);
+
+	      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);
+
+	      g_signal_connect (submenu, "menu-activate", 
+				G_CALLBACK (submenu_activated_cb), menu);
 	    }
 	}
 
@@ -767,6 +816,36 @@ gtk_tree_menu_populate (GtkTreeMenu *menu)
     }
 }
 
+static void
+item_activated_cb (GtkMenuItem          *item,
+		   GtkTreeMenu          *menu)
+{
+  GtkCellView *view;
+  GtkTreePath *path;
+  gchar       *path_str;
+
+  /* Only activate leafs, not parents */
+  if (!gtk_menu_item_get_submenu (item))
+    {
+      view     = GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)));
+      path     = gtk_cell_view_get_displayed_row (view);
+      path_str = gtk_tree_path_to_string (path);
+      
+      g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path_str);
+      
+      g_free (path_str);
+      gtk_tree_path_free (path);
+    }
+}
+
+static void
+submenu_activated_cb (GtkTreeMenu          *submenu,
+		      const gchar          *path,
+		      GtkTreeMenu          *menu)
+{
+  g_signal_emit (menu, tree_menu_signals[SIGNAL_MENU_ACTIVATE], 0, path);
+}
+
 /****************************************************************
  *                            API                               *
  ****************************************************************/
@@ -865,8 +944,6 @@ gtk_tree_menu_set_root (GtkTreeMenu         *menu,
   /* Populate for the new root */
   if (priv->model)
     gtk_tree_menu_populate (menu);
-
-  gtk_widget_queue_resize (GTK_WIDGET (menu));
 }
 
 GtkTreePath *
@@ -902,6 +979,14 @@ gtk_tree_menu_set_row_separator_func (GtkTreeMenu          *menu,
   priv->row_separator_func    = func;
   priv->row_separator_data    = data;
   priv->row_separator_destroy = destroy;
+
+  /* Destroy all the menu items */
+  gtk_container_foreach (GTK_CONTAINER (menu), 
+			 (GtkCallback) gtk_widget_destroy, NULL);
+  
+  /* Populate again */
+  if (priv->model)
+    gtk_tree_menu_populate (menu);
 }
 
 GtkTreeViewRowSeparatorFunc
@@ -915,3 +1000,43 @@ gtk_tree_menu_get_row_separator_func (GtkTreeMenu *menu)
 
   return priv->row_separator_func;
 }
+
+void
+gtk_tree_menu_set_header_func (GtkTreeMenu          *menu,
+			       GtkTreeMenuHeaderFunc func,
+			       gpointer              data,
+			       GDestroyNotify        destroy)
+{
+  GtkTreeMenuPrivate *priv;
+
+  g_return_if_fail (GTK_IS_TREE_MENU (menu));
+
+  priv = menu->priv;
+
+  if (priv->header_destroy)
+    priv->header_destroy (priv->header_data);
+
+  priv->header_func    = func;
+  priv->header_data    = data;
+  priv->header_destroy = destroy;
+
+  /* Destroy all the menu items */
+  gtk_container_foreach (GTK_CONTAINER (menu), 
+			 (GtkCallback) gtk_widget_destroy, NULL);
+  
+  /* Populate again */
+  if (priv->model)
+    gtk_tree_menu_populate (menu);
+}
+
+GtkTreeMenuHeaderFunc
+gtk_tree_menu_get_header_func (GtkTreeMenu *menu)
+{
+  GtkTreeMenuPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_TREE_MENU (menu), NULL);
+
+  priv = menu->priv;
+
+  return priv->header_func;
+}
diff --git a/gtk/gtktreemenu.h b/gtk/gtktreemenu.h
index 9a05678..539613f 100644
--- a/gtk/gtktreemenu.h
+++ b/gtk/gtktreemenu.h
@@ -46,6 +46,9 @@ typedef struct _GtkTreeMenu              GtkTreeMenu;
 typedef struct _GtkTreeMenuClass         GtkTreeMenuClass;
 typedef struct _GtkTreeMenuPrivate       GtkTreeMenuPrivate;
 
+typedef gboolean (*GtkTreeMenuHeaderFunc) (GtkTreeModel      *model,
+					   GtkTreeIter       *iter,
+					   gpointer           data);
 
 struct _GtkTreeMenu
 {
@@ -89,6 +92,12 @@ void                        gtk_tree_menu_set_row_separator_func   (GtkTreeMenu
 								    GDestroyNotify        destroy);
 GtkTreeViewRowSeparatorFunc gtk_tree_menu_get_row_separator_func   (GtkTreeMenu          *menu);
 
+void                  gtk_tree_menu_set_header_func                (GtkTreeMenu          *menu,
+								    GtkTreeMenuHeaderFunc func,
+								    gpointer              data,
+								    GDestroyNotify        destroy);
+GtkTreeMenuHeaderFunc gtk_tree_menu_get_header_func                (GtkTreeMenu          *menu);
+
 G_END_DECLS
 
 #endif /* __GTK_TREE_MENU_H__ */
diff --git a/tests/testtreemenu.c b/tests/testtreemenu.c
index d099f0d..e514f47 100644
--- a/tests/testtreemenu.c
+++ b/tests/testtreemenu.c
@@ -16,7 +16,7 @@ static GtkCellRenderer *cell_1 = NULL, *cell_2 = NULL, *cell_3 = NULL;
 static GtkTreeModel *
 simple_tree_model (void)
 {
-  GtkTreeIter   iter, parent;
+  GtkTreeIter   iter, parent, child;
   GtkTreeStore *store = 
     gtk_tree_store_new (N_SIMPLE_COLUMNS,
 			G_TYPE_STRING,  /* name text */
@@ -84,6 +84,27 @@ simple_tree_model (void)
 		      SIMPLE_COLUMN_DESCRIPTION, "Eager",
 		      -1);
 
+  gtk_tree_store_append (store, &child, &iter);
+  gtk_tree_store_set (store, &child, 
+		      SIMPLE_COLUMN_NAME, "Jump",
+		      SIMPLE_COLUMN_ICON, "gtk-yes",
+		      SIMPLE_COLUMN_DESCRIPTION, "Very High",
+		      -1);
+
+  gtk_tree_store_append (store, &child, &iter);
+  gtk_tree_store_set (store, &child, 
+		      SIMPLE_COLUMN_NAME, "Pounce",
+		      SIMPLE_COLUMN_ICON, "gtk-no",
+		      SIMPLE_COLUMN_DESCRIPTION, "On Pooh",
+		      -1);
+
+  gtk_tree_store_append (store, &child, &iter);
+  gtk_tree_store_set (store, &child, 
+		      SIMPLE_COLUMN_NAME, "Bounce",
+		      SIMPLE_COLUMN_ICON, "gtk-cancel",
+		      SIMPLE_COLUMN_DESCRIPTION, "Around",
+		      -1);
+
   gtk_tree_store_append (store, &iter, &parent);
   gtk_tree_store_set (store, &iter, 
 		      SIMPLE_COLUMN_NAME, "Owl",
@@ -213,6 +234,44 @@ expand_cell_3_toggled (GtkToggleButton  *toggle,
 }
 
 static void
+menu_activated_cb (GtkTreeMenu *menu,
+		   const gchar *path,
+		   gpointer     unused)
+{
+  GtkTreeModel *model = gtk_tree_menu_get_model (menu);
+  GtkTreeIter   iter;
+  gchar        *row_name;
+
+  if (!gtk_tree_model_get_iter_from_string (model, &iter, path))
+    return;
+
+  gtk_tree_model_get (model, &iter, SIMPLE_COLUMN_NAME, &row_name, -1);
+
+  g_print ("Item activated: %s\n", row_name);
+
+  g_free (row_name);
+}
+
+gboolean 
+enable_submenu_headers (GtkTreeModel      *model,
+			GtkTreeIter       *iter,
+			gpointer           data)
+{
+  return TRUE;
+}
+
+
+static void
+submenu_headers_toggled (GtkToggleButton  *toggle,
+			 GtkTreeMenu      *menu)
+{
+  if (gtk_toggle_button_get_active (toggle))
+    gtk_tree_menu_set_header_func (menu, enable_submenu_headers, NULL, NULL);
+  else
+    gtk_tree_menu_set_header_func (menu, NULL, NULL, NULL);
+}
+
+static void
 tree_menu (void)
 {
   GtkWidget *window, *widget;
@@ -224,6 +283,8 @@ tree_menu (void)
 
   menu = simple_tree_menu ();
 
+  g_signal_connect (menu, "menu-activate", G_CALLBACK (menu_activated_cb), NULL);
+
   vbox  = gtk_vbox_new (FALSE, 4);
   gtk_widget_show (vbox);
 
@@ -278,6 +339,15 @@ tree_menu (void)
   g_signal_connect (G_OBJECT (widget), "toggled",
                     G_CALLBACK (expand_cell_3_toggled), menu);
 
+  widget = gtk_check_button_new_with_label ("Submenu Headers");
+  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 (submenu_headers_toggled), menu);
+
+
   gtk_container_add (GTK_CONTAINER (window), vbox);
 
   gtk_widget_show (window);



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