The table menu patch! Turns GtkMenu into a table, so you can attach menu

Wed Sep 10 22:25:04 2003  Kristian Rietveld  <kris@gtk.org>

	The table menu patch! Turns GtkMenu into a table, so you can attach
	menu items in numerous new ways! Be creative!

	Contains some bug fixes and RTL adaptions from Matthias Clasen.

	* gtk/gtkmenu.c [toplevel]: introduce ATTACH_INFO_KEY, extend
	GtkMenuPrivate, introduce AttachInfo, add child properties enum,
	(gtk_menu_free_private), (gtk_menu_get_private): we have to free
	the heights array in the private struct,
	(gtk_menu_class_init): reorder code a bit, install child properties,
	(get_attach_info), (get_child_attach): new utility functions,
	(gtk_menu_set_child_property), (gtk_menu_get_child_property): introduce
	child properties, for the attach info,
	(gtk_menu_remove): remove AttachInfo from menu item,
	(gtk_menu_real_insert): implemented algorithm to automagically place
	inserted menu items at the correct place in the table,
	(gtk_menu_size_request), (gtk_menu_size_allocate),
	(compute_child_offset): reworked/rewritten to support table menus,
	(gtk_menu_attach): new function,
	(find_child_containing), (gtk_menu_move_current): new functions to
	get table menu keynav right.

	* gtk/gtkmenu.h: add gtk_menu_attach() prototype.
This commit is contained in:
Kristian Rietveld 2003-09-10 20:32:33 +00:00 committed by Kristian Rietveld
parent 87c0d2651c
commit e23ed60782
7 changed files with 682 additions and 100 deletions

View File

@ -1,3 +1,29 @@
Wed Sep 10 22:25:04 2003 Kristian Rietveld <kris@gtk.org>
The table menu patch! Turns GtkMenu into a table, so you can attach
menu items in numerous new ways! Be creative!
Contains some bug fixes and RTL adaptions from Matthias Clasen.
* gtk/gtkmenu.c [toplevel]: introduce ATTACH_INFO_KEY, extend
GtkMenuPrivate, introduce AttachInfo, add child properties enum,
(gtk_menu_free_private), (gtk_menu_get_private): we have to free
the heights array in the private struct,
(gtk_menu_class_init): reorder code a bit, install child properties,
(get_attach_info), (get_child_attach): new utility functions,
(gtk_menu_set_child_property), (gtk_menu_get_child_property): introduce
child properties, for the attach info,
(gtk_menu_remove): remove AttachInfo from menu item,
(gtk_menu_real_insert): implemented algorithm to automagically place
inserted menu items at the correct place in the table,
(gtk_menu_size_request), (gtk_menu_size_allocate),
(compute_child_offset): reworked/rewritten to support table menus,
(gtk_menu_attach): new function,
(find_child_containing), (gtk_menu_move_current): new functions to
get table menu keynav right.
* gtk/gtkmenu.h: add gtk_menu_attach() prototype.
2003-09-10 Matthias Clasen <maclas@gmx.de>
* gtk/gtksocket.c (gtk_socket_filter_func): Only remove

View File

@ -1,3 +1,29 @@
Wed Sep 10 22:25:04 2003 Kristian Rietveld <kris@gtk.org>
The table menu patch! Turns GtkMenu into a table, so you can attach
menu items in numerous new ways! Be creative!
Contains some bug fixes and RTL adaptions from Matthias Clasen.
* gtk/gtkmenu.c [toplevel]: introduce ATTACH_INFO_KEY, extend
GtkMenuPrivate, introduce AttachInfo, add child properties enum,
(gtk_menu_free_private), (gtk_menu_get_private): we have to free
the heights array in the private struct,
(gtk_menu_class_init): reorder code a bit, install child properties,
(get_attach_info), (get_child_attach): new utility functions,
(gtk_menu_set_child_property), (gtk_menu_get_child_property): introduce
child properties, for the attach info,
(gtk_menu_remove): remove AttachInfo from menu item,
(gtk_menu_real_insert): implemented algorithm to automagically place
inserted menu items at the correct place in the table,
(gtk_menu_size_request), (gtk_menu_size_allocate),
(compute_child_offset): reworked/rewritten to support table menus,
(gtk_menu_attach): new function,
(find_child_containing), (gtk_menu_move_current): new functions to
get table menu keynav right.
* gtk/gtkmenu.h: add gtk_menu_attach() prototype.
2003-09-10 Matthias Clasen <maclas@gmx.de>
* gtk/gtksocket.c (gtk_socket_filter_func): Only remove

View File

@ -1,3 +1,29 @@
Wed Sep 10 22:25:04 2003 Kristian Rietveld <kris@gtk.org>
The table menu patch! Turns GtkMenu into a table, so you can attach
menu items in numerous new ways! Be creative!
Contains some bug fixes and RTL adaptions from Matthias Clasen.
* gtk/gtkmenu.c [toplevel]: introduce ATTACH_INFO_KEY, extend
GtkMenuPrivate, introduce AttachInfo, add child properties enum,
(gtk_menu_free_private), (gtk_menu_get_private): we have to free
the heights array in the private struct,
(gtk_menu_class_init): reorder code a bit, install child properties,
(get_attach_info), (get_child_attach): new utility functions,
(gtk_menu_set_child_property), (gtk_menu_get_child_property): introduce
child properties, for the attach info,
(gtk_menu_remove): remove AttachInfo from menu item,
(gtk_menu_real_insert): implemented algorithm to automagically place
inserted menu items at the correct place in the table,
(gtk_menu_size_request), (gtk_menu_size_allocate),
(compute_child_offset): reworked/rewritten to support table menus,
(gtk_menu_attach): new function,
(find_child_containing), (gtk_menu_move_current): new functions to
get table menu keynav right.
* gtk/gtkmenu.h: add gtk_menu_attach() prototype.
2003-09-10 Matthias Clasen <maclas@gmx.de>
* gtk/gtksocket.c (gtk_socket_filter_func): Only remove

View File

@ -1,3 +1,29 @@
Wed Sep 10 22:25:04 2003 Kristian Rietveld <kris@gtk.org>
The table menu patch! Turns GtkMenu into a table, so you can attach
menu items in numerous new ways! Be creative!
Contains some bug fixes and RTL adaptions from Matthias Clasen.
* gtk/gtkmenu.c [toplevel]: introduce ATTACH_INFO_KEY, extend
GtkMenuPrivate, introduce AttachInfo, add child properties enum,
(gtk_menu_free_private), (gtk_menu_get_private): we have to free
the heights array in the private struct,
(gtk_menu_class_init): reorder code a bit, install child properties,
(get_attach_info), (get_child_attach): new utility functions,
(gtk_menu_set_child_property), (gtk_menu_get_child_property): introduce
child properties, for the attach info,
(gtk_menu_remove): remove AttachInfo from menu item,
(gtk_menu_real_insert): implemented algorithm to automagically place
inserted menu items at the correct place in the table,
(gtk_menu_size_request), (gtk_menu_size_allocate),
(compute_child_offset): reworked/rewritten to support table menus,
(gtk_menu_attach): new function,
(find_child_containing), (gtk_menu_move_current): new functions to
get table menu keynav right.
* gtk/gtkmenu.h: add gtk_menu_attach() prototype.
2003-09-10 Matthias Clasen <maclas@gmx.de>
* gtk/gtksocket.c (gtk_socket_filter_func): Only remove

View File

@ -1,3 +1,29 @@
Wed Sep 10 22:25:04 2003 Kristian Rietveld <kris@gtk.org>
The table menu patch! Turns GtkMenu into a table, so you can attach
menu items in numerous new ways! Be creative!
Contains some bug fixes and RTL adaptions from Matthias Clasen.
* gtk/gtkmenu.c [toplevel]: introduce ATTACH_INFO_KEY, extend
GtkMenuPrivate, introduce AttachInfo, add child properties enum,
(gtk_menu_free_private), (gtk_menu_get_private): we have to free
the heights array in the private struct,
(gtk_menu_class_init): reorder code a bit, install child properties,
(get_attach_info), (get_child_attach): new utility functions,
(gtk_menu_set_child_property), (gtk_menu_get_child_property): introduce
child properties, for the attach info,
(gtk_menu_remove): remove AttachInfo from menu item,
(gtk_menu_real_insert): implemented algorithm to automagically place
inserted menu items at the correct place in the table,
(gtk_menu_size_request), (gtk_menu_size_allocate),
(compute_child_offset): reworked/rewritten to support table menus,
(gtk_menu_attach): new function,
(find_child_containing), (gtk_menu_move_current): new functions to
get table menu keynav right.
* gtk/gtkmenu.h: add gtk_menu_attach() prototype.
2003-09-10 Matthias Clasen <maclas@gmx.de>
* gtk/gtksocket.c (gtk_socket_filter_func): Only remove

View File

@ -60,6 +60,8 @@
#define MENU_SCROLL_TIMEOUT1 50
#define MENU_SCROLL_TIMEOUT2 50
#define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
typedef struct _GtkMenuAttachData GtkMenuAttachData;
typedef struct _GtkMenuPrivate GtkMenuPrivate;
@ -74,8 +76,24 @@ struct _GtkMenuPrivate
gboolean have_position;
gint x;
gint y;
/* info used for the table */
guint rows;
guint columns;
guint *heights;
gint heights_length;
};
typedef struct
{
guint left_attach;
guint right_attach;
guint top_attach;
guint bottom_attach;
}
AttachInfo;
enum {
MOVE_SCROLL,
LAST_SIGNAL
@ -86,16 +104,35 @@ enum {
PROP_TEAROFF_TITLE
};
enum
{
CHILD_PROP_0,
CHILD_PROP_LEFT_ATTACH,
CHILD_PROP_RIGHT_ATTACH,
CHILD_PROP_TOP_ATTACH,
CHILD_PROP_BOTTOM_ATTACH
};
static void gtk_menu_class_init (GtkMenuClass *klass);
static void gtk_menu_init (GtkMenu *menu);
static void gtk_menu_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gtk_menu_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void gtk_menu_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gtk_menu_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void gtk_menu_set_child_property(GtkContainer *container,
GtkWidget *child,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gtk_menu_get_child_property(GtkContainer *container,
GtkWidget *child,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gtk_menu_destroy (GtkObject *object);
static void gtk_menu_finalize (GObject *object);
static void gtk_menu_realize (GtkWidget *widget);
@ -146,6 +183,8 @@ static void gtk_menu_style_set (GtkWidget *widget,
static gboolean gtk_menu_focus (GtkWidget *widget,
GtkDirectionType direction);
static gint gtk_menu_get_popup_delay (GtkMenuShell *menu_shell);
static void gtk_menu_move_current (GtkMenuShell *menu_shell,
GtkMenuDirectionType direction);
static void gtk_menu_real_move_scroll (GtkMenu *menu,
GtkScrollType type);
@ -181,6 +220,14 @@ static const gchar *attach_data_key = "gtk-menu-attach-data";
static guint menu_signals[LAST_SIGNAL] = { 0 };
static void
gtk_menu_free_private (gpointer data)
{
GtkMenuPrivate *priv = (GtkMenuPrivate *)data;
g_free (priv->heights);
}
GtkMenuPrivate *
gtk_menu_get_private (GtkMenu *menu)
{
@ -198,7 +245,7 @@ gtk_menu_get_private (GtkMenu *menu)
private->have_position = FALSE;
g_object_set_qdata_full (G_OBJECT (menu), private_quark,
private, g_free);
private, gtk_menu_free_private);
}
return private;
@ -247,6 +294,37 @@ gtk_menu_class_init (GtkMenuClass *class)
gobject_class->set_property = gtk_menu_set_property;
gobject_class->get_property = gtk_menu_get_property;
object_class->destroy = gtk_menu_destroy;
widget_class->realize = gtk_menu_realize;
widget_class->unrealize = gtk_menu_unrealize;
widget_class->size_request = gtk_menu_size_request;
widget_class->size_allocate = gtk_menu_size_allocate;
widget_class->show = gtk_menu_show;
widget_class->expose_event = gtk_menu_expose;
widget_class->key_press_event = gtk_menu_key_press;
widget_class->button_press_event = gtk_menu_button_press;
widget_class->button_release_event = gtk_menu_button_release;
widget_class->motion_notify_event = gtk_menu_motion_notify;
widget_class->show_all = gtk_menu_show_all;
widget_class->hide_all = gtk_menu_hide_all;
widget_class->enter_notify_event = gtk_menu_enter_notify;
widget_class->leave_notify_event = gtk_menu_leave_notify;
widget_class->motion_notify_event = gtk_menu_motion_notify;
widget_class->style_set = gtk_menu_style_set;
widget_class->focus = gtk_menu_focus;
container_class->remove = gtk_menu_remove;
container_class->get_child_property = gtk_menu_get_child_property;
container_class->set_child_property = gtk_menu_set_child_property;
menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
menu_shell_class->deactivate = gtk_menu_deactivate;
menu_shell_class->select_item = gtk_menu_select_item;
menu_shell_class->insert = gtk_menu_real_insert;
menu_shell_class->get_popup_delay = gtk_menu_get_popup_delay;
menu_shell_class->move_current = gtk_menu_move_current;
menu_signals[MOVE_SCROLL] =
_gtk_binding_signal_new ("move_scroll",
G_OBJECT_CLASS_TYPE (object_class),
@ -292,33 +370,38 @@ gtk_menu_class_init (GtkMenuClass *class)
-2,
G_PARAM_READABLE));
object_class->destroy = gtk_menu_destroy;
widget_class->realize = gtk_menu_realize;
widget_class->unrealize = gtk_menu_unrealize;
widget_class->size_request = gtk_menu_size_request;
widget_class->size_allocate = gtk_menu_size_allocate;
widget_class->show = gtk_menu_show;
widget_class->expose_event = gtk_menu_expose;
widget_class->key_press_event = gtk_menu_key_press;
widget_class->button_press_event = gtk_menu_button_press;
widget_class->button_release_event = gtk_menu_button_release;
widget_class->motion_notify_event = gtk_menu_motion_notify;
widget_class->show_all = gtk_menu_show_all;
widget_class->hide_all = gtk_menu_hide_all;
widget_class->enter_notify_event = gtk_menu_enter_notify;
widget_class->leave_notify_event = gtk_menu_leave_notify;
widget_class->motion_notify_event = gtk_menu_motion_notify;
widget_class->style_set = gtk_menu_style_set;
widget_class->focus = gtk_menu_focus;
gtk_container_class_install_child_property (container_class,
CHILD_PROP_LEFT_ATTACH,
g_param_spec_uint ("left_attach",
_("Left Attach"),
_("The column number to attach the left side of the child to"),
0, UINT_MAX, 0,
G_PARAM_READWRITE));
container_class->remove = gtk_menu_remove;
gtk_container_class_install_child_property (container_class,
CHILD_PROP_RIGHT_ATTACH,
g_param_spec_uint ("right_attach",
_("Right Attach"),
_("The column number to attach the right side of the child to"),
0, UINT_MAX, 0,
G_PARAM_READWRITE));
menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
menu_shell_class->deactivate = gtk_menu_deactivate;
menu_shell_class->select_item = gtk_menu_select_item;
menu_shell_class->insert = gtk_menu_real_insert;
menu_shell_class->get_popup_delay = gtk_menu_get_popup_delay;
gtk_container_class_install_child_property (container_class,
CHILD_PROP_TOP_ATTACH,
g_param_spec_uint ("top_attach",
_("Top Attach"),
_("The row number to attach the top of the child to"),
0, UINT_MAX, 0,
G_PARAM_READWRITE));
gtk_container_class_install_child_property (container_class,
CHILD_PROP_BOTTOM_ATTACH,
g_param_spec_uint ("bottom_attach",
_("Bottom Attach"),
_("The row number to attach the bottom of the child to"),
0, UINT_MAX, 0,
G_PARAM_READWRITE));
binding_set = gtk_binding_set_by_class (class);
gtk_binding_entry_add_signal (binding_set,
@ -469,6 +552,104 @@ gtk_menu_get_property (GObject *object,
}
}
static AttachInfo *
get_attach_info (GObject *child)
{
AttachInfo *ai = g_object_get_data (child, ATTACH_INFO_KEY);
if (!ai)
{
ai = g_new0 (AttachInfo, 1);
g_object_set_data_full (child, ATTACH_INFO_KEY, ai, g_free);
}
return ai;
}
static void
gtk_menu_set_child_property (GtkContainer *container,
GtkWidget *child,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GtkMenu *menu = GTK_MENU (container);
GtkMenuPrivate *priv;
AttachInfo *ai = get_attach_info (G_OBJECT (child));
priv = gtk_menu_get_private (menu);
switch (property_id)
{
case CHILD_PROP_LEFT_ATTACH:
ai->left_attach = g_value_get_uint (value);
break;
case CHILD_PROP_RIGHT_ATTACH:
ai->right_attach = g_value_get_uint (value);
break;
case CHILD_PROP_TOP_ATTACH:
ai->top_attach = g_value_get_uint (value);
break;
case CHILD_PROP_BOTTOM_ATTACH:
ai->bottom_attach = g_value_get_uint (value);
break;
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
return;
}
priv->columns = MAX (priv->columns, ai->right_attach);
priv->rows = MAX (priv->rows, ai->bottom_attach);
gtk_widget_queue_resize (GTK_WIDGET (menu));
}
static void
gtk_menu_get_child_property (GtkContainer *container,
GtkWidget *child,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
AttachInfo *ai = get_attach_info (G_OBJECT (child));
switch (property_id)
{
case CHILD_PROP_LEFT_ATTACH:
g_value_set_uint (value, ai->left_attach);
break;
case CHILD_PROP_RIGHT_ATTACH:
g_value_set_uint (value, ai->right_attach);
break;
case CHILD_PROP_TOP_ATTACH:
g_value_set_uint (value, ai->top_attach);
break;
case CHILD_PROP_BOTTOM_ATTACH:
g_value_set_uint (value, ai->bottom_attach);
break;
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
return;
}
}
static void
get_child_attach (GtkWidget *child,
gint *l,
gint *r,
gint *t,
gint *b)
{
gtk_container_child_get (GTK_CONTAINER (child->parent), child,
"left_attach", l,
"right_attach", r,
"top_attach", t,
"bottom_attach", b,
NULL);
}
static gboolean
gtk_menu_window_event (GtkWidget *window,
GdkEvent *event,
@ -748,6 +929,7 @@ gtk_menu_remove (GtkContainer *container,
GtkWidget *widget)
{
GtkMenu *menu;
g_return_if_fail (GTK_IS_MENU (container));
g_return_if_fail (GTK_IS_MENU_ITEM (widget));
@ -761,6 +943,8 @@ gtk_menu_remove (GtkContainer *container,
menu->old_active_menu_item = NULL;
}
g_object_set_data (G_OBJECT (widget), ATTACH_INFO_KEY, NULL);
GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
}
@ -776,10 +960,51 @@ gtk_menu_real_insert (GtkMenuShell *menu_shell,
GtkWidget *child,
gint position)
{
gint i;
GList *children;
GtkMenuPrivate *priv;
if (GTK_WIDGET_REALIZED (menu_shell))
gtk_widget_set_parent_window (child, GTK_MENU (menu_shell)->bin_window);
GTK_MENU_SHELL_CLASS (parent_class)->insert (menu_shell, child, position);
priv = gtk_menu_get_private (GTK_MENU (menu_shell));
if (position < 0)
{
/* attach after the last row */
i = g_list_length (menu_shell->children) - 1;
gtk_menu_attach (GTK_MENU (menu_shell), child,
0, priv->columns ? priv->columns : 1,
i, i + 1);
return;
}
/* we need to make space for this new item; move all items with
* top >= position one down
*/
for (children = menu_shell->children; children; children = children->next)
{
guint top, bottom;
gtk_container_child_get (GTK_CONTAINER (menu_shell), children->data,
"top_attach", &top,
"bottom_attach", &bottom,
NULL);
if (top >= position)
gtk_container_child_set (GTK_CONTAINER (menu_shell), children->data,
"top_attach", top + 1,
"bottom_attach", bottom + 1,
NULL);
}
/* attach the new item */
gtk_menu_attach (GTK_MENU (menu_shell), child,
0, priv->columns ? priv->columns : 1,
position, position + 1);
}
static void
@ -1028,7 +1253,6 @@ gtk_menu_popup (GtkMenu *menu,
if (xgrab_shell == widget)
popup_grab_on_window (widget->window, activate_time); /* Should always succeed */
gtk_grab_add (GTK_WIDGET (menu));
}
@ -1542,6 +1766,7 @@ gtk_menu_realize (GtkWidget *widget)
attributes.colormap = gtk_widget_get_colormap (widget);
attributes.event_mask = gtk_widget_get_events (widget);
attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK |
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK );
@ -1682,6 +1907,7 @@ static void
gtk_menu_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
gint i;
GtkMenu *menu;
GtkMenuShell *menu_shell;
GtkWidget *child;
@ -1690,12 +1916,14 @@ gtk_menu_size_request (GtkWidget *widget,
guint max_accel_width;
guint vertical_padding;
GtkRequisition child_requisition;
GtkMenuPrivate *priv;
g_return_if_fail (GTK_IS_MENU (widget));
g_return_if_fail (requisition != NULL);
menu = GTK_MENU (widget);
menu_shell = GTK_MENU_SHELL (widget);
priv = gtk_menu_get_private (menu);
requisition->width = 0;
requisition->height = 0;
@ -1703,35 +1931,51 @@ gtk_menu_size_request (GtkWidget *widget,
max_toggle_size = 0;
max_accel_width = 0;
g_free (priv->heights);
priv->heights = g_new0 (guint, priv->rows);
priv->heights_length = priv->rows;
children = menu_shell->children;
while (children)
{
gint part;
gint toggle_size;
guint l, r, t, b;
child = children->data;
children = children->next;
if (GTK_WIDGET_VISIBLE (child))
{
gint toggle_size;
if (! GTK_WIDGET_VISIBLE (child))
continue;
/* It's important to size_request the child
* before doing the toggle size request, in
* case the toggle size request depends on the size
* request of a child of the child (e.g. for ImageMenuItem)
*/
get_child_attach (child, &l, &r, &t, &b);
GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
gtk_widget_size_request (child, &child_requisition);
/* It's important to size_request the child
* before doing the toggle size request, in
* case the toggle size request depends on the size
* request of a child of the child (e.g. for ImageMenuItem)
*/
requisition->width = MAX (requisition->width, child_requisition.width);
requisition->height += child_requisition.height;
GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
gtk_widget_size_request (child, &child_requisition);
gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
max_toggle_size = MAX (max_toggle_size, toggle_size);
max_accel_width = MAX (max_accel_width, GTK_MENU_ITEM (child)->accelerator_width);
}
gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size);
max_toggle_size = MAX (max_toggle_size, toggle_size);
max_accel_width = MAX (max_accel_width,
GTK_MENU_ITEM (child)->accelerator_width);
part = child_requisition.width / (r - l);
requisition->width = MAX (requisition->width, part);
part = child_requisition.height / (b - t);
priv->heights[t] = MAX (priv->heights[t], part);
}
for (i = 0; i < priv->rows; i++)
requisition->height += priv->heights[i];
requisition->width += max_toggle_size + max_accel_width;
requisition->width *= priv->columns;
requisition->width += (GTK_CONTAINER (menu)->border_width +
widget->style->xthickness) * 2;
@ -1757,6 +2001,8 @@ gtk_menu_size_allocate (GtkWidget *widget,
GtkMenuShell *menu_shell;
GtkWidget *child;
GtkAllocation child_allocation;
GtkRequisition child_requisition;
GtkMenuPrivate *priv;
GList *children;
gint x, y;
gint width, height;
@ -1767,8 +2013,10 @@ gtk_menu_size_allocate (GtkWidget *widget,
menu = GTK_MENU (widget);
menu_shell = GTK_MENU_SHELL (widget);
priv = gtk_menu_get_private (menu);
widget->allocation = *allocation;
gtk_widget_get_child_requisition (GTK_WIDGET (menu), &child_requisition);
gtk_widget_style_get (GTK_WIDGET (menu),
"vertical-padding", &vertical_padding,
@ -1780,6 +2028,9 @@ gtk_menu_size_allocate (GtkWidget *widget,
width = MAX (1, allocation->width - x * 2);
height = MAX (1, allocation->height - y * 2);
child_requisition.width -= x * 2;
child_requisition.height -= y * 2;
if (menu_shell->active)
gtk_menu_scroll_to (menu, menu->scroll_offset);
@ -1807,9 +2058,7 @@ gtk_menu_size_allocate (GtkWidget *widget,
if (menu_shell->children)
{
child_allocation.x = 0;
child_allocation.y = 0;
child_allocation.width = width;
gint base_width = width / priv->columns;
children = menu_shell->children;
while (children)
@ -1819,29 +2068,53 @@ gtk_menu_size_allocate (GtkWidget *widget,
if (GTK_WIDGET_VISIBLE (child))
{
GtkRequisition child_requisition;
gtk_widget_get_child_requisition (child, &child_requisition);
gint i;
guint l, r, t, b;
child_allocation.height = child_requisition.height;
get_child_attach (child, &l, &r, &t, &b);
if (gtk_widget_get_direction (GTK_WIDGET (menu)) == GTK_TEXT_DIR_RTL)
{
guint tmp;
tmp = priv->columns - l;
l = priv->columns - r;
r = tmp;
}
child_allocation.width = (r - l) * base_width;
child_allocation.height = 0;
child_allocation.x = l * base_width;
child_allocation.y = 0;
for (i = 0; i < b; i++)
{
if (i < t)
child_allocation.y += priv->heights[i];
else
child_allocation.height += priv->heights[i];
}
gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child),
menu->toggle_size);
gtk_widget_size_allocate (child, &child_allocation);
gtk_widget_queue_draw (child);
child_allocation.y += child_allocation.height;
}
}
/* Resize the item window */
if (GTK_WIDGET_REALIZED (widget))
{
gdk_window_resize (menu->bin_window,
child_allocation.width,
child_allocation.y);
}
gint i;
gint width, height;
height = 0;
for (i = 0; i < priv->rows; i++)
height += priv->heights[i];
width = priv->columns * base_width;
gdk_window_resize (menu->bin_window, width, height);
}
if (menu->tearoff_active)
{
@ -3052,40 +3325,37 @@ compute_child_offset (GtkMenu *menu,
gint *height,
gboolean *is_last_child)
{
GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
GList *children;
GtkMenuPrivate *priv = gtk_menu_get_private (menu);
guint item_top_attach;
guint item_bottom_attach;
gint child_offset = 0;
gint i;
for (children = menu_shell->children; children; children = children->next)
{
GtkWidget *child = children->data;
GtkRequisition child_requisition;
gint child_height;
gtk_container_child_get (GTK_CONTAINER (menu), menu_item,
"top_attach", &item_top_attach,
"bottom_attach", &item_bottom_attach,
NULL);
if (GTK_WIDGET_VISIBLE (child))
{
gtk_widget_size_request (child, &child_requisition);
child_height = child_requisition.height;
}
else
child_height = 0;
/* there is a possibility that we get called before _size_request, so
* check the height table for safety.
*/
if (!priv->heights || priv->heights_length < priv->rows)
return FALSE;
if (child == menu_item)
{
if (is_last_child)
*is_last_child = (children == NULL);
if (offset)
*offset = child_offset;
if (height)
*height = child_height;
/* when we have a row with only invisible children, it's height will
* be zero, so there's no need to check WIDGET_VISIBLE here
*/
for (i = 0; i < item_top_attach; i++)
child_offset += priv->heights[i];
return TRUE;
}
if (is_last_child)
*is_last_child = (item_bottom_attach == priv->rows);
if (offset)
*offset = child_offset;
if (height)
*height = priv->heights[item_top_attach];
child_offset += child_height;
}
return FALSE;
return TRUE;
}
static void
@ -3279,6 +3549,47 @@ gtk_menu_set_screen (GtkMenu *menu,
}
}
/**
* gtk_menu_attach:
* @menu: a #GtkMenu.
* @child: a #GtkMenuItem.
* @left_attach: The column number to attach the left side of the item to.
* @right_attach: The column number to attach the right side of the item to.
* @top_attach: The row number to attach the top of the item to.
* @bottom_attach: The row number to attach the bottom of the item to.
*
* Adds a new #GtkMenuItem to a (table) menu. The number of 'cells' that
* an item will occupy is specified by @left_attach, @right_attach,
* @top_attach and @bottom_attach. These each represent the leftmost,
* rightmost, uppermost and lower column and row numbers of the table.
* (Columns and rows are indexed from zero).
*
* Since: 2.4
**/
void
gtk_menu_attach (GtkMenu *menu,
GtkWidget *child,
guint left_attach,
guint right_attach,
guint top_attach,
guint bottom_attach)
{
g_return_if_fail (GTK_IS_MENU (menu));
g_return_if_fail (GTK_IS_MENU_ITEM (child));
g_return_if_fail (left_attach < right_attach);
g_return_if_fail (top_attach < bottom_attach);
if (!child->parent)
gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
gtk_container_child_set (GTK_CONTAINER (menu), child,
"left_attach", left_attach,
"right_attach", right_attach,
"top_attach", top_attach,
"bottom_attach", bottom_attach,
NULL);
}
static gint
gtk_menu_get_popup_delay (GtkMenuShell *menu_shell)
@ -3292,6 +3603,138 @@ gtk_menu_get_popup_delay (GtkMenuShell *menu_shell)
return popup_delay;
}
static GtkWidget *
find_child_containing (GtkMenuShell *menu_shell,
int left,
int right,
int top,
int bottom)
{
GList *list;
/* find a child which includes the area given by
* left, right, top, bottom.
*/
for (list = menu_shell->children; list; list = list->next)
{
guint l, r, t, b;
if (!_gtk_menu_item_is_selectable (list->data))
continue;
get_child_attach (list->data, &l, &r, &t, &b);
if (l <= left && right <= r
&& t <= top && bottom <= b)
return GTK_WIDGET (list->data);
}
return NULL;
}
static void
gtk_menu_move_current (GtkMenuShell *menu_shell,
GtkMenuDirectionType direction)
{
GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (menu_shell));
/* use special table menu key bindings */
if (menu_shell->active_menu_item && priv->columns > 1)
{
int i;
guint l, r, t, b;
gboolean rtl = (gtk_widget_get_direction (GTK_WIDGET (menu_shell)) == GTK_TEXT_DIR_RTL);
GtkWidget *match = NULL;
get_child_attach (menu_shell->active_menu_item, &l, &r, &t, &b);
if (direction == GTK_MENU_DIR_NEXT)
{
for (i = b; i < priv->rows; i++)
{
match = find_child_containing (menu_shell, l, l + 1, i, i + 1);
if (match)
break;
}
if (!match)
{
/* wrap around */
for (i = 0; i < t; i++)
{
match = find_child_containing (menu_shell,
l, l + 1, i, i + 1);
if (match)
break;
}
}
}
else if (direction == GTK_MENU_DIR_PREV)
{
for (i = t; i > 0; i--)
{
match = find_child_containing (menu_shell, l, l + 1, i - 1, i);
if (match)
break;
}
if (!match)
{
/* wrap around */
for (i = priv->rows; i > b; i--)
{
match = find_child_containing (menu_shell,
l, l + 1, i - 1, i);
if (match)
break;
}
}
}
else if ((!rtl && direction == GTK_MENU_DIR_PARENT)
|| (rtl && direction == GTK_MENU_DIR_CHILD))
{
/* we go one left if possible */
if (l > 0)
match = find_child_containing (menu_shell, l - 1, l, t, t + 1);
if (!match)
{
GtkWidget *parent = menu_shell->parent_menu_shell;
if (!parent
|| g_list_length (GTK_MENU_SHELL (parent)->children) <= 1)
match = menu_shell->active_menu_item;
}
}
else if ((!rtl && direction == GTK_MENU_DIR_CHILD)
|| (rtl && direction == GTK_MENU_DIR_PARENT))
{
/* we go one right if possible */
if (r < priv->columns)
match = find_child_containing (menu_shell, r, r + 1, t, t + 1);
if (!match)
{
GtkWidget *parent = menu_shell->parent_menu_shell;
if (! GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu &&
(!parent ||
g_list_length (GTK_MENU_SHELL (parent)->children) <= 1))
match = menu_shell->active_menu_item;
}
}
if (match)
{
gtk_menu_shell_select_item (menu_shell, match);
return;
}
}
GTK_MENU_SHELL_CLASS (parent_class)->move_current (menu_shell, direction);
}
static gint
get_visible_size (GtkMenu *menu)
{

View File

@ -188,6 +188,15 @@ void gtk_menu_reorder_child (GtkMenu *menu,
void gtk_menu_set_screen (GtkMenu *menu,
GdkScreen *screen);
void gtk_menu_attach (GtkMenu *menu,
GtkWidget *child,
guint left_attach,
guint right_attach,
guint top_attach,
guint bottom_attach);
#ifndef GTK_DISABLE_DEPRECATED
#define gtk_menu_append(menu,child) gtk_menu_shell_append ((GtkMenuShell *)(menu),(child))
#define gtk_menu_prepend(menu,child) gtk_menu_shell_prepend ((GtkMenuShell *)(menu),(child))