[gtk+/combo-refactor: 13/42] Added GtkTreeMenuHeaderFunc to decide if a submenu gets a leaf header.
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/combo-refactor: 13/42] Added GtkTreeMenuHeaderFunc to decide if a submenu gets a leaf header.
- Date: Thu, 2 Dec 2010 03:42:42 +0000 (UTC)
commit a9d25fab9dbf958abfb8462a98d2d59f0bc9f1eb
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]