Commit 9ffaae50 authored by Tristan Van Berkom's avatar Tristan Van Berkom

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.
parent 6d8dfd55
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "config.h" #include "config.h"
#include "gtkintl.h" #include "gtkintl.h"
#include "gtktreemenu.h" #include "gtktreemenu.h"
#include "gtkmarshalers.h"
#include "gtkmenuitem.h" #include "gtkmenuitem.h"
#include "gtkseparatormenuitem.h" #include "gtkseparatormenuitem.h"
#include "gtkcellareabox.h" #include "gtkcellareabox.h"
...@@ -91,10 +92,14 @@ static GtkWidget *gtk_tree_menu_create_item (GtkTreeMenu ...@@ -91,10 +92,14 @@ static GtkWidget *gtk_tree_menu_create_item (GtkTreeMenu
GtkTreeIter *iter); GtkTreeIter *iter);
static void gtk_tree_menu_set_area (GtkTreeMenu *menu, static void gtk_tree_menu_set_area (GtkTreeMenu *menu,
GtkCellArea *area); GtkCellArea *area);
static void queue_resize_all (GtkWidget *menu);
static void context_size_changed_cb (GtkCellAreaContext *context, static void context_size_changed_cb (GtkCellAreaContext *context,
GParamSpec *pspec, GParamSpec *pspec,
GtkWidget *menu); 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 struct _GtkTreeMenuPrivate
{ {
...@@ -116,6 +121,11 @@ struct _GtkTreeMenuPrivate ...@@ -116,6 +121,11 @@ struct _GtkTreeMenuPrivate
GtkTreeViewRowSeparatorFunc row_separator_func; GtkTreeViewRowSeparatorFunc row_separator_func;
gpointer row_separator_data; gpointer row_separator_data;
GDestroyNotify row_separator_destroy; GDestroyNotify row_separator_destroy;
/* Submenu headers */
GtkTreeMenuHeaderFunc header_func;
gpointer header_data;
GDestroyNotify header_destroy;
}; };
enum { enum {
...@@ -125,6 +135,13 @@ enum { ...@@ -125,6 +135,13 @@ enum {
PROP_CELL_AREA 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_DEFINE_TYPE_WITH_CODE (GtkTreeMenu, gtk_tree_menu, GTK_TYPE_MENU,
G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT, G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
gtk_tree_menu_cell_layout_init)); gtk_tree_menu_cell_layout_init));
...@@ -158,6 +175,15 @@ gtk_tree_menu_class_init (GtkTreeMenuClass *class) ...@@ -158,6 +175,15 @@ gtk_tree_menu_class_init (GtkTreeMenuClass *class)
widget_class->get_preferred_height = gtk_tree_menu_get_preferred_height; widget_class->get_preferred_height = gtk_tree_menu_get_preferred_height;
widget_class->size_allocate = gtk_tree_menu_size_allocate; 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, g_object_class_install_property (object_class,
PROP_MODEL, PROP_MODEL,
g_param_spec_object ("model", g_param_spec_object ("model",
...@@ -249,6 +275,17 @@ gtk_tree_menu_dispose (GObject *object) ...@@ -249,6 +275,17 @@ gtk_tree_menu_dispose (GObject *object)
static void static void
gtk_tree_menu_finalize (GObject *object) 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); G_OBJECT_CLASS (gtk_tree_menu_parent_class)->finalize (object);
} }
...@@ -639,28 +676,9 @@ context_size_changed_cb (GtkCellAreaContext *context, ...@@ -639,28 +676,9 @@ context_size_changed_cb (GtkCellAreaContext *context,
!strcmp (pspec->name, "natural-width") || !strcmp (pspec->name, "natural-width") ||
!strcmp (pspec->name, "minimum-height") || !strcmp (pspec->name, "minimum-height") ||
!strcmp (pspec->name, "natural-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 static void
gtk_tree_menu_set_area (GtkTreeMenu *menu, gtk_tree_menu_set_area (GtkTreeMenu *menu,
GtkCellArea *area) GtkCellArea *area)
...@@ -668,15 +686,14 @@ gtk_tree_menu_set_area (GtkTreeMenu *menu, ...@@ -668,15 +686,14 @@ gtk_tree_menu_set_area (GtkTreeMenu *menu,
GtkTreeMenuPrivate *priv = menu->priv; GtkTreeMenuPrivate *priv = menu->priv;
if (priv->area) if (priv->area)
g_object_unref (area); g_object_unref (priv->area);
priv->area = area; priv->area = area;
if (priv->area) if (priv->area)
g_object_ref_sink (area); g_object_ref_sink (priv->area);
} }
static GtkWidget * static GtkWidget *
gtk_tree_menu_create_item (GtkTreeMenu *menu, gtk_tree_menu_create_item (GtkTreeMenu *menu,
GtkTreeIter *iter) GtkTreeIter *iter)
...@@ -700,6 +717,8 @@ gtk_tree_menu_create_item (GtkTreeMenu *menu, ...@@ -700,6 +717,8 @@ gtk_tree_menu_create_item (GtkTreeMenu *menu,
gtk_widget_show (view); gtk_widget_show (view);
gtk_container_add (GTK_CONTAINER (item), view); gtk_container_add (GTK_CONTAINER (item), view);
g_signal_connect (item, "activate", G_CALLBACK (item_activated_cb), menu);
return item; return item;
} }
...@@ -722,8 +741,23 @@ gtk_tree_menu_populate (GtkTreeMenu *menu) ...@@ -722,8 +741,23 @@ gtk_tree_menu_populate (GtkTreeMenu *menu)
if (path) if (path)
{ {
if (gtk_tree_model_get_iter (priv->model, &parent, 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); gtk_tree_path_free (path);
} }
else else
...@@ -753,11 +787,26 @@ gtk_tree_menu_populate (GtkTreeMenu *menu) ...@@ -753,11 +787,26 @@ gtk_tree_menu_populate (GtkTreeMenu *menu)
GtkWidget *submenu; GtkWidget *submenu;
row_path = gtk_tree_model_get_path (priv->model, &iter); 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_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), submenu);
gtk_tree_path_free (row_path); 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) ...@@ -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 * * API *
****************************************************************/ ****************************************************************/
...@@ -865,8 +944,6 @@ gtk_tree_menu_set_root (GtkTreeMenu *menu, ...@@ -865,8 +944,6 @@ gtk_tree_menu_set_root (GtkTreeMenu *menu,
/* Populate for the new root */ /* Populate for the new root */
if (priv->model) if (priv->model)
gtk_tree_menu_populate (menu); gtk_tree_menu_populate (menu);
gtk_widget_queue_resize (GTK_WIDGET (menu));
} }
GtkTreePath * GtkTreePath *
...@@ -902,6 +979,14 @@ gtk_tree_menu_set_row_separator_func (GtkTreeMenu *menu, ...@@ -902,6 +979,14 @@ gtk_tree_menu_set_row_separator_func (GtkTreeMenu *menu,
priv->row_separator_func = func; priv->row_separator_func = func;
priv->row_separator_data = data; priv->row_separator_data = data;
priv->row_separator_destroy = destroy; 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 GtkTreeViewRowSeparatorFunc
...@@ -915,3 +1000,43 @@ gtk_tree_menu_get_row_separator_func (GtkTreeMenu *menu) ...@@ -915,3 +1000,43 @@ gtk_tree_menu_get_row_separator_func (GtkTreeMenu *menu)
return priv->row_separator_func; 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;
}
...@@ -46,6 +46,9 @@ typedef struct _GtkTreeMenu GtkTreeMenu; ...@@ -46,6 +46,9 @@ typedef struct _GtkTreeMenu GtkTreeMenu;
typedef struct _GtkTreeMenuClass GtkTreeMenuClass; typedef struct _GtkTreeMenuClass GtkTreeMenuClass;
typedef struct _GtkTreeMenuPrivate GtkTreeMenuPrivate; typedef struct _GtkTreeMenuPrivate GtkTreeMenuPrivate;
typedef gboolean (*GtkTreeMenuHeaderFunc) (GtkTreeModel *model,
GtkTreeIter *iter,
gpointer data);
struct _GtkTreeMenu struct _GtkTreeMenu
{ {
...@@ -89,6 +92,12 @@ void gtk_tree_menu_set_row_separator_func (GtkTreeMenu ...@@ -89,6 +92,12 @@ void gtk_tree_menu_set_row_separator_func (GtkTreeMenu
GDestroyNotify destroy); GDestroyNotify destroy);
GtkTreeViewRowSeparatorFunc gtk_tree_menu_get_row_separator_func (GtkTreeMenu *menu); 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 G_END_DECLS
#endif /* __GTK_TREE_MENU_H__ */ #endif /* __GTK_TREE_MENU_H__ */
...@@ -16,7 +16,7 @@ static GtkCellRenderer *cell_1 = NULL, *cell_2 = NULL, *cell_3 = NULL; ...@@ -16,7 +16,7 @@ static GtkCellRenderer *cell_1 = NULL, *cell_2 = NULL, *cell_3 = NULL;
static GtkTreeModel * static GtkTreeModel *
simple_tree_model (void) simple_tree_model (void)
{ {
GtkTreeIter iter, parent; GtkTreeIter iter, parent, child;
GtkTreeStore *store = GtkTreeStore *store =
gtk_tree_store_new (N_SIMPLE_COLUMNS, gtk_tree_store_new (N_SIMPLE_COLUMNS,
G_TYPE_STRING, /* name text */ G_TYPE_STRING, /* name text */
...@@ -84,6 +84,27 @@ simple_tree_model (void) ...@@ -84,6 +84,27 @@ simple_tree_model (void)
SIMPLE_COLUMN_DESCRIPTION, "Eager", SIMPLE_COLUMN_DESCRIPTION, "Eager",
-1); -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_append (store, &iter, &parent);
gtk_tree_store_set (store, &iter, gtk_tree_store_set (store, &iter,
SIMPLE_COLUMN_NAME, "Owl", SIMPLE_COLUMN_NAME, "Owl",
...@@ -212,6 +233,44 @@ expand_cell_3_toggled (GtkToggleButton *toggle, ...@@ -212,6 +233,44 @@ expand_cell_3_toggled (GtkToggleButton *toggle,
gtk_cell_area_cell_set (area, cell_3, "expand", expand, NULL); gtk_cell_area_cell_set (area, cell_3, "expand", expand, NULL);
} }
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 static void
tree_menu (void) tree_menu (void)
{ {
...@@ -224,6 +283,8 @@ tree_menu (void) ...@@ -224,6 +283,8 @@ tree_menu (void)
menu = simple_tree_menu (); menu = simple_tree_menu ();
g_signal_connect (menu, "menu-activate", G_CALLBACK (menu_activated_cb), NULL);
vbox = gtk_vbox_new (FALSE, 4); vbox = gtk_vbox_new (FALSE, 4);
gtk_widget_show (vbox); gtk_widget_show (vbox);
...@@ -278,6 +339,15 @@ tree_menu (void) ...@@ -278,6 +339,15 @@ tree_menu (void)
g_signal_connect (G_OBJECT (widget), "toggled", g_signal_connect (G_OBJECT (widget), "toggled",
G_CALLBACK (expand_cell_3_toggled), menu); 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_container_add (GTK_CONTAINER (window), vbox);
gtk_widget_show (window); gtk_widget_show (window);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment