#136672, reported by Christian Persch; fixes based on a patch by Soeren

Sun Mar 14 19:26:48 2004  Owen Taylor  <otaylor@redhat.com>

        #136672, reported by Christian Persch; fixes based
        on a patch by Soeren Sandmann.

        * gtk/gtkmenu.c: Change the handling of mixed gridded
        and non-gridded menu items; the old method was causing
        major performance problems even with the "avoid relayout
        on destruction" hack put in recently.

        Now we first lay out the gridded items and then put the
        non-gridded items into empty rows / after the gridded items.
        Layout is done in a central menu_shell_ensure_layout(). Also
        avoid emitting property notifications when we initially
        insert or attach items.

        * gtk/gtkmenushell.[ch] gtk/gtkmenu.c: Stop setting the
        unused menu_shell->menu_flag, and document it as unused
        in the header.

        * tests/testactions.c: Add a test of creating/removing
        lots of items.

        * test/testgtk.c: Add some more cruft to the menu test.
This commit is contained in:
Owen Taylor 2004-03-15 02:03:59 +00:00 committed by Owen Taylor
parent f2e252a2c2
commit 3895bea4df
10 changed files with 544 additions and 223 deletions

View File

@ -1,3 +1,28 @@
Sun Mar 14 19:26:48 2004 Owen Taylor <otaylor@redhat.com>
#136672, reported by Christian Persch; fixes based
on a patch by Soeren Sandmann.
* gtk/gtkmenu.c: Change the handling of mixed gridded
and non-gridded menu items; the old method was causing
major performance problems even with the "avoid relayout
on destruction" hack put in recently.
Now we first lay out the gridded items and then put the
non-gridded items into empty rows / after the gridded items.
Layout is done in a central menu_shell_ensure_layout(). Also
avoid emitting property notifications when we initially
insert or attach items.
* gtk/gtkmenushell.[ch] gtk/gtkmenu.c: Stop setting the
unused menu_shell->menu_flag, and document it as unused
in the header.
* tests/testactions.c: Add a test of creating/removing
lots of items.
* test/testgtk.c: Add some more cruft to the menu test.
Mon Mar 15 02:36:07 2004 Matthias Clasen <maclas@gmx.de>
* gtk/gtkentrycompletion.c (gtk_entry_completion_class_init): Add

View File

@ -1,3 +1,28 @@
Sun Mar 14 19:26:48 2004 Owen Taylor <otaylor@redhat.com>
#136672, reported by Christian Persch; fixes based
on a patch by Soeren Sandmann.
* gtk/gtkmenu.c: Change the handling of mixed gridded
and non-gridded menu items; the old method was causing
major performance problems even with the "avoid relayout
on destruction" hack put in recently.
Now we first lay out the gridded items and then put the
non-gridded items into empty rows / after the gridded items.
Layout is done in a central menu_shell_ensure_layout(). Also
avoid emitting property notifications when we initially
insert or attach items.
* gtk/gtkmenushell.[ch] gtk/gtkmenu.c: Stop setting the
unused menu_shell->menu_flag, and document it as unused
in the header.
* tests/testactions.c: Add a test of creating/removing
lots of items.
* test/testgtk.c: Add some more cruft to the menu test.
Mon Mar 15 02:36:07 2004 Matthias Clasen <maclas@gmx.de>
* gtk/gtkentrycompletion.c (gtk_entry_completion_class_init): Add

View File

@ -1,3 +1,28 @@
Sun Mar 14 19:26:48 2004 Owen Taylor <otaylor@redhat.com>
#136672, reported by Christian Persch; fixes based
on a patch by Soeren Sandmann.
* gtk/gtkmenu.c: Change the handling of mixed gridded
and non-gridded menu items; the old method was causing
major performance problems even with the "avoid relayout
on destruction" hack put in recently.
Now we first lay out the gridded items and then put the
non-gridded items into empty rows / after the gridded items.
Layout is done in a central menu_shell_ensure_layout(). Also
avoid emitting property notifications when we initially
insert or attach items.
* gtk/gtkmenushell.[ch] gtk/gtkmenu.c: Stop setting the
unused menu_shell->menu_flag, and document it as unused
in the header.
* tests/testactions.c: Add a test of creating/removing
lots of items.
* test/testgtk.c: Add some more cruft to the menu test.
Mon Mar 15 02:36:07 2004 Matthias Clasen <maclas@gmx.de>
* gtk/gtkentrycompletion.c (gtk_entry_completion_class_init): Add

View File

@ -1,3 +1,28 @@
Sun Mar 14 19:26:48 2004 Owen Taylor <otaylor@redhat.com>
#136672, reported by Christian Persch; fixes based
on a patch by Soeren Sandmann.
* gtk/gtkmenu.c: Change the handling of mixed gridded
and non-gridded menu items; the old method was causing
major performance problems even with the "avoid relayout
on destruction" hack put in recently.
Now we first lay out the gridded items and then put the
non-gridded items into empty rows / after the gridded items.
Layout is done in a central menu_shell_ensure_layout(). Also
avoid emitting property notifications when we initially
insert or attach items.
* gtk/gtkmenushell.[ch] gtk/gtkmenu.c: Stop setting the
unused menu_shell->menu_flag, and document it as unused
in the header.
* tests/testactions.c: Add a test of creating/removing
lots of items.
* test/testgtk.c: Add some more cruft to the menu test.
Mon Mar 15 02:36:07 2004 Matthias Clasen <maclas@gmx.de>
* gtk/gtkentrycompletion.c (gtk_entry_completion_class_init): Add

View File

@ -1,3 +1,28 @@
Sun Mar 14 19:26:48 2004 Owen Taylor <otaylor@redhat.com>
#136672, reported by Christian Persch; fixes based
on a patch by Soeren Sandmann.
* gtk/gtkmenu.c: Change the handling of mixed gridded
and non-gridded menu items; the old method was causing
major performance problems even with the "avoid relayout
on destruction" hack put in recently.
Now we first lay out the gridded items and then put the
non-gridded items into empty rows / after the gridded items.
Layout is done in a central menu_shell_ensure_layout(). Also
avoid emitting property notifications when we initially
insert or attach items.
* gtk/gtkmenushell.[ch] gtk/gtkmenu.c: Stop setting the
unused menu_shell->menu_flag, and document it as unused
in the header.
* tests/testactions.c: Add a test of creating/removing
lots of items.
* test/testgtk.c: Add some more cruft to the menu test.
Mon Mar 15 02:36:07 2004 Matthias Clasen <maclas@gmx.de>
* gtk/gtkentrycompletion.c (gtk_entry_completion_class_init): Add

View File

@ -46,7 +46,6 @@
#define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_GET_CLASS (w)
#define MENU_NEEDS_RESIZE(m) GTK_MENU_SHELL (m)->menu_flag
#define DEFAULT_POPUP_DELAY 225
#define DEFAULT_POPDOWN_DELAY 1000
@ -80,25 +79,28 @@ struct _GtkMenuPrivate
gint y;
/* info used for the table */
guint rows;
guint columns;
guint *heights;
gint heights_length;
gint monitor_num;
gboolean destroying;
/* Cached layout information */
gboolean have_layout;
gint n_rows;
gint n_columns;
};
typedef struct
{
guint left_attach;
guint right_attach;
guint top_attach;
guint bottom_attach;
}
AttachInfo;
gint left_attach;
gint right_attach;
gint top_attach;
gint bottom_attach;
gint effective_left_attach;
gint effective_right_attach;
gint effective_top_attach;
gint effective_bottom_attach;
} AttachInfo;
enum {
MOVE_SCROLL,
@ -110,8 +112,7 @@ enum {
PROP_TEAROFF_TITLE
};
enum
{
enum {
CHILD_PROP_0,
CHILD_PROP_LEFT_ATTACH,
CHILD_PROP_RIGHT_ATTACH,
@ -175,9 +176,6 @@ static void gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell,
GtkWidget *menu_item);
static void gtk_menu_select_item (GtkMenuShell *menu_shell,
GtkWidget *menu_item);
static void gtk_menu_do_insert (GtkMenuShell *menu_shell,
GtkWidget *child,
gint position);
static void gtk_menu_real_insert (GtkMenuShell *menu_shell,
GtkWidget *child,
gint position);
@ -291,6 +289,171 @@ gtk_menu_get_type (void)
return menu_type;
}
static void
menu_queue_resize (GtkMenu *menu)
{
GtkMenuPrivate *priv = gtk_menu_get_private (menu);
priv->have_layout = FALSE;
gtk_widget_queue_resize (GTK_WIDGET (menu));
}
static AttachInfo *
get_attach_info (GtkWidget *child)
{
GObject *object = G_OBJECT (child);
AttachInfo *ai = g_object_get_data (object, ATTACH_INFO_KEY);
if (!ai)
{
ai = g_new0 (AttachInfo, 1);
g_object_set_data_full (object, ATTACH_INFO_KEY, ai, g_free);
}
return ai;
}
static gboolean
is_grid_attached (AttachInfo *ai)
{
return (ai->left_attach >= 0 &&
ai->right_attach >= 0 &&
ai->top_attach >= 0 &&
ai->bottom_attach >= 0);
}
static void
menu_ensure_layout (GtkMenu *menu)
{
GtkMenuPrivate *priv = gtk_menu_get_private (menu);
if (!priv->have_layout)
{
GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
GList *l;
gchar *row_occupied;
gint current_row;
gint max_right_attach;
gint max_bottom_attach;
/* Find extents of gridded portion
*/
max_right_attach = 1;
max_bottom_attach = 0;
for (l = menu_shell->children; l; l = l->next)
{
GtkWidget *child = l->data;
AttachInfo *ai = get_attach_info (child);
if (is_grid_attached (ai))
{
max_bottom_attach = MAX (max_bottom_attach, ai->bottom_attach);
max_right_attach = MAX (max_right_attach, ai->right_attach);
}
}
/* Find empty rows
*/
row_occupied = g_malloc0 (max_bottom_attach);
for (l = menu_shell->children; l; l = l->next)
{
GtkWidget *child = l->data;
AttachInfo *ai = get_attach_info (child);
if (is_grid_attached (ai))
{
gint i;
for (i = ai->top_attach; i < ai->bottom_attach; i++)
row_occupied[i] = TRUE;
}
}
/* Lay non-grid-items out in those rows
*/
current_row = 0;
for (l = menu_shell->children; l; l = l->next)
{
GtkWidget *child = l->data;
AttachInfo *ai = get_attach_info (child);
if (!is_grid_attached (ai))
{
while (current_row < max_bottom_attach && row_occupied[current_row])
current_row++;
ai->effective_left_attach = 0;
ai->effective_right_attach = max_right_attach;
ai->effective_top_attach = current_row;
ai->effective_bottom_attach = current_row + 1;
current_row++;
}
else
{
ai->effective_left_attach = ai->left_attach;
ai->effective_right_attach = ai->right_attach;
ai->effective_top_attach = ai->top_attach;
ai->effective_bottom_attach = ai->bottom_attach;
}
}
g_free (row_occupied);
priv->n_rows = MAX (current_row, max_bottom_attach);
priv->n_columns = max_right_attach;
priv->have_layout = TRUE;
}
}
static gint
gtk_menu_get_n_columns (GtkMenu *menu)
{
GtkMenuPrivate *priv = gtk_menu_get_private (menu);
menu_ensure_layout (menu);
return priv->n_columns;
}
static gint
gtk_menu_get_n_rows (GtkMenu *menu)
{
GtkMenuPrivate *priv = gtk_menu_get_private (menu);
menu_ensure_layout (menu);
return priv->n_rows;
}
static void
get_effective_child_attach (GtkWidget *child,
int *l,
int *r,
int *t,
int *b)
{
GtkMenu *menu = GTK_MENU (child->parent);
AttachInfo *ai;
menu_ensure_layout (menu);
ai = get_attach_info (child);
if (l)
*l = ai->effective_left_attach;
if (r)
*r = ai->effective_right_attach;
if (t)
*t = ai->effective_top_attach;
if (b)
*b = ai->effective_bottom_attach;
}
static void
gtk_menu_class_init (GtkMenuClass *class)
{
@ -387,34 +550,34 @@ gtk_menu_class_init (GtkMenuClass *class)
gtk_container_class_install_child_property (container_class,
CHILD_PROP_LEFT_ATTACH,
g_param_spec_uint ("left_attach",
g_param_spec_int ("left_attach",
P_("Left Attach"),
P_("The column number to attach the left side of the child to"),
0, UINT_MAX, 0,
-1, INT_MAX, -1,
G_PARAM_READWRITE));
gtk_container_class_install_child_property (container_class,
CHILD_PROP_RIGHT_ATTACH,
g_param_spec_uint ("right_attach",
g_param_spec_int ("right_attach",
P_("Right Attach"),
P_("The column number to attach the right side of the child to"),
0, UINT_MAX, 0,
-1, INT_MAX, -1,
G_PARAM_READWRITE));
gtk_container_class_install_child_property (container_class,
CHILD_PROP_TOP_ATTACH,
g_param_spec_uint ("top_attach",
g_param_spec_int ("top_attach",
P_("Top Attach"),
P_("The row number to attach the top of the child to"),
0, UINT_MAX, 0,
-1, INT_MAX, -1,
G_PARAM_READWRITE));
gtk_container_class_install_child_property (container_class,
CHILD_PROP_BOTTOM_ATTACH,
g_param_spec_uint ("bottom_attach",
g_param_spec_int ("bottom_attach",
P_("Bottom Attach"),
P_("The row number to attach the bottom of the child to"),
0, UINT_MAX, 0,
-1, INT_MAX, -1,
G_PARAM_READWRITE));
binding_set = gtk_binding_set_by_class (class);
@ -566,20 +729,6 @@ 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,
@ -588,33 +737,29 @@ gtk_menu_set_child_property (GtkContainer *container,
GParamSpec *pspec)
{
GtkMenu *menu = GTK_MENU (container);
GtkMenuPrivate *priv;
AttachInfo *ai = get_attach_info (G_OBJECT (child));
priv = gtk_menu_get_private (menu);
AttachInfo *ai = get_attach_info (child);
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);
priv->columns = MAX (priv->columns, ai->right_attach);
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);
priv->rows = MAX (priv->rows, ai->bottom_attach);
break;
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
return;
case CHILD_PROP_LEFT_ATTACH:
ai->left_attach = g_value_get_int (value);
break;
case CHILD_PROP_RIGHT_ATTACH:
ai->right_attach = g_value_get_int (value);
break;
case CHILD_PROP_TOP_ATTACH:
ai->top_attach = g_value_get_int (value);
break;
case CHILD_PROP_BOTTOM_ATTACH:
ai->bottom_attach = g_value_get_int (value);
break;
default:
GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
return;
}
gtk_widget_queue_resize (GTK_WIDGET (menu));
menu_queue_resize (menu);
}
static void
@ -624,44 +769,29 @@ gtk_menu_get_child_property (GtkContainer *container,
GValue *value,
GParamSpec *pspec)
{
AttachInfo *ai = get_attach_info (G_OBJECT (child));
AttachInfo *ai = get_attach_info (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;
case CHILD_PROP_LEFT_ATTACH:
g_value_set_int (value, ai->left_attach);
break;
case CHILD_PROP_RIGHT_ATTACH:
g_value_set_int (value, ai->right_attach);
break;
case CHILD_PROP_TOP_ATTACH:
g_value_set_int (value, ai->top_attach);
break;
case CHILD_PROP_BOTTOM_ATTACH:
g_value_set_int (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,
@ -758,10 +888,8 @@ gtk_menu_init (GtkMenu *menu)
menu->lower_arrow_visible = FALSE;
menu->upper_arrow_prelight = FALSE;
menu->lower_arrow_prelight = FALSE;
MENU_NEEDS_RESIZE (menu) = TRUE;
priv->columns = 1;
priv->have_layout = FALSE;
}
static void
@ -809,8 +937,6 @@ gtk_menu_destroy (GtkObject *object)
priv = gtk_menu_get_private (menu);
priv->destroying = TRUE;
GTK_OBJECT_CLASS (parent_class)->destroy (object);
}
@ -946,45 +1072,6 @@ gtk_menu_detach (GtkMenu *menu)
}
static void
gtk_menu_do_remove (GtkMenuShell *menu_shell,
GtkWidget *child)
{
AttachInfo *ai;
GList *children;
GtkMenuPrivate *priv;
gint delta;
gboolean single_column;
ai = get_attach_info (G_OBJECT (child));
priv = gtk_menu_get_private (GTK_MENU (menu_shell));
delta = ai->bottom_attach - ai->top_attach;
single_column = priv->columns == 1;
/* Recalculate these, assuming the child has already been removed.
* Note that an empty menu is assumed to have one column
*/
priv->rows = 0;
priv->columns = 1;
for (children = menu_shell->children; children; children = children->next)
{
AttachInfo *child_ai = get_attach_info (children->data);
/* Do not move items in table menus */
if (single_column && child_ai->bottom_attach > ai->bottom_attach)
gtk_container_child_set (GTK_CONTAINER (menu_shell), children->data,
"top_attach", child_ai->top_attach - delta,
"bottom_attach", child_ai->bottom_attach - delta,
NULL);
else
{
priv->columns = MAX (priv->columns, child_ai->right_attach);
priv->rows = MAX (priv->rows, child_ai->bottom_attach);
}
}
}
static void
gtk_menu_remove (GtkContainer *container,
GtkWidget *widget)
{
@ -1006,11 +1093,10 @@ gtk_menu_remove (GtkContainer *container,
}
GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
if (!priv->destroying)
gtk_menu_do_remove (GTK_MENU_SHELL (container), widget);
g_object_set_data (G_OBJECT (widget), ATTACH_INFO_KEY, NULL);
}
menu_queue_resize (menu);
}
GtkWidget*
gtk_menu_new (void)
@ -1019,58 +1105,24 @@ gtk_menu_new (void)
}
static void
gtk_menu_do_insert (GtkMenuShell *menu_shell,
GtkWidget *child,
gint position)
gtk_menu_real_insert (GtkMenuShell *menu_shell,
GtkWidget *child,
gint position)
{
GList *children;
GtkMenuPrivate *priv;
GtkMenu *menu = GTK_MENU (menu_shell);
AttachInfo *ai = get_attach_info (child);
priv = gtk_menu_get_private (GTK_MENU (menu_shell));
ai->left_attach = -1;
ai->right_attach = -1;
ai->top_attach = -1;
ai->bottom_attach = -1;
if (position < 0 || position >= priv->rows)
{
/* attach after the last row */
gtk_menu_attach (GTK_MENU (menu_shell), child,
0, priv->columns,
priv->rows, priv->rows + 1);
return;
}
/* We need to make space for this new item; move all items with
* top >= position one down.
* Note that this does not prevent overlaps when the menu contains
* vertically spanning items.
*/
for (children = menu_shell->children; children; children = children->next)
{
AttachInfo *child_ai = get_attach_info (children->data);
if (child_ai->top_attach >= position)
gtk_container_child_set (GTK_CONTAINER (menu_shell), children->data,
"top_attach", child_ai->top_attach + 1,
"bottom_attach", child_ai->bottom_attach + 1,
NULL);
}
/* attach the new item */
gtk_menu_attach (GTK_MENU (menu_shell), child,
0, priv->columns,
position, position + 1);
}
static void
gtk_menu_real_insert (GtkMenuShell *menu_shell,
GtkWidget *child,
gint position)
{
if (GTK_WIDGET_REALIZED (menu_shell))
gtk_widget_set_parent_window (child, GTK_MENU (menu_shell)->bin_window);
gtk_widget_set_parent_window (child, menu->bin_window);
GTK_MENU_SHELL_CLASS (parent_class)->insert (menu_shell, child, position);
gtk_menu_do_insert (menu_shell, child, position);
menu_queue_resize (menu);
}
static void
@ -1851,10 +1903,8 @@ gtk_menu_reorder_child (GtkMenu *menu,
{
menu_shell->children = g_list_remove (menu_shell->children, child);
menu_shell->children = g_list_insert (menu_shell->children, child, position);
gtk_menu_do_insert (menu_shell, child, position);
if (GTK_WIDGET_VISIBLE (menu_shell))
gtk_widget_queue_resize (GTK_WIDGET (menu_shell));
menu_queue_resize (menu);
}
}
@ -2065,8 +2115,8 @@ gtk_menu_size_request (GtkWidget *widget,
max_accel_width = 0;
g_free (priv->heights);
priv->heights = g_new0 (guint, priv->rows);
priv->heights_length = priv->rows;
priv->heights = g_new0 (guint, gtk_menu_get_n_rows (menu));
priv->heights_length = gtk_menu_get_n_rows (menu);
children = menu_shell->children;
while (children)
@ -2081,7 +2131,7 @@ gtk_menu_size_request (GtkWidget *widget,
if (! GTK_WIDGET_VISIBLE (child))
continue;
get_child_attach (child, &l, &r, &t, &b);
get_effective_child_attach (child, &l, &r, &t, &b);
/* It's important to size_request the child
* before doing the toggle size request, in
@ -2104,11 +2154,11 @@ gtk_menu_size_request (GtkWidget *widget,
priv->heights[t] = MAX (priv->heights[t], part);
}
for (i = 0; i < priv->rows; i++)
for (i = 0; i < gtk_menu_get_n_rows (menu); i++)
requisition->height += priv->heights[i];
requisition->width += max_toggle_size + max_accel_width;
requisition->width *= priv->columns;
requisition->width *= gtk_menu_get_n_columns (menu);
requisition->width += (GTK_CONTAINER (menu)->border_width +
widget->style->xthickness) * 2;
@ -2191,7 +2241,7 @@ gtk_menu_size_allocate (GtkWidget *widget,
if (menu_shell->children)
{
gint base_width = width / priv->columns;
gint base_width = width / gtk_menu_get_n_columns (menu);
children = menu_shell->children;
while (children)
@ -2204,13 +2254,13 @@ gtk_menu_size_allocate (GtkWidget *widget,
gint i;
guint l, r, t, b;
get_child_attach (child, &l, &r, &t, &b);
get_effective_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;
tmp = gtk_menu_get_n_columns (menu) - l;
l = gtk_menu_get_n_columns (menu) - r;
r = tmp;
}
@ -2242,10 +2292,10 @@ gtk_menu_size_allocate (GtkWidget *widget,
gint width, height;
height = 0;
for (i = 0; i < priv->rows; i++)
for (i = 0; i < gtk_menu_get_n_rows (menu); i++)
height += priv->heights[i];
width = priv->columns * base_width;
width = gtk_menu_get_n_columns (menu) * base_width;
gdk_window_resize (menu->bin_window, width, height);
}
@ -3517,15 +3567,13 @@ compute_child_offset (GtkMenu *menu,
gint child_offset = 0;
gint i;
gtk_container_child_get (GTK_CONTAINER (menu), menu_item,
"top_attach", &item_top_attach,
"bottom_attach", &item_bottom_attach,
NULL);
get_effective_child_attach (menu_item, NULL, NULL,
&item_top_attach, &item_bottom_attach);
/* 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)
if (!priv->heights || priv->heights_length < gtk_menu_get_n_rows (menu))
return FALSE;
/* when we have a row with only invisible children, it's height will
@ -3535,7 +3583,7 @@ compute_child_offset (GtkMenu *menu,
child_offset += priv->heights[i];
if (is_last_child)
*is_last_child = (item_bottom_attach == priv->rows);
*is_last_child = (item_bottom_attach == gtk_menu_get_n_rows (menu));
if (offset)
*offset = child_offset;
if (height)
@ -3758,6 +3806,8 @@ gtk_menu_attach (GtkMenu *menu,
guint top_attach,
guint bottom_attach)
{
GtkMenuShell *menu_shell;
g_return_if_fail (GTK_IS_MENU (menu));
g_return_if_fail (GTK_IS_MENU_ITEM (child));
g_return_if_fail (child->parent == NULL ||
@ -3765,20 +3815,32 @@ gtk_menu_attach (GtkMenu *menu,
g_return_if_fail (left_attach < right_attach);
g_return_if_fail (top_attach < bottom_attach);
menu_shell = GTK_MENU_SHELL (menu);
if (!child->parent)
{
GTK_MENU_SHELL (menu)->children =
g_list_append (GTK_MENU_SHELL (menu)->children, child);
AttachInfo *ai = get_attach_info (child);
ai->left_attach = left_attach;
ai->right_attach = right_attach;
ai->top_attach = top_attach;
ai->bottom_attach = bottom_attach;
menu_shell->children = g_list_append (menu_shell->children, child);
gtk_widget_set_parent (child, GTK_WIDGET (menu));
}
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);
menu_queue_resize (menu);
}
else
{
gtk_container_child_set (GTK_CONTAINER (child->parent), child,
"left_attach", left_attach,
"right_attach", right_attach,
"top_attach", top_attach,
"bottom_attach", bottom_attach,
NULL);
}
}
static gint
@ -3813,7 +3875,7 @@ find_child_containing (GtkMenuShell *menu_shell,
if (!_gtk_menu_item_is_selectable (list->data))
continue;
get_child_attach (list->data, &l, &r, &t, &b);
get_effective_child_attach (list->data, &l, &r, &t, &b);
if (l <= left && right <= r
&& t <= top && bottom <= b)
@ -3827,21 +3889,21 @@ static void
gtk_menu_move_current (GtkMenuShell *menu_shell,
GtkMenuDirectionType direction)
{
GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (menu_shell));
GtkMenu *menu = GTK_MENU (menu_shell);
/* use special table menu key bindings */
if (menu_shell->active_menu_item && priv->columns > 1)
if (menu_shell->active_menu_item && gtk_menu_get_n_columns (menu) > 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);
get_effective_child_attach (menu_shell->active_menu_item, &l, &r, &t, &b);
if (direction == GTK_MENU_DIR_NEXT)
{
for (i = b; i < priv->rows; i++)
for (i = b; i < gtk_menu_get_n_rows (menu); i++)
{
match = find_child_containing (menu_shell, l, l + 1, i, i + 1);
if (match)
@ -3872,7 +3934,7 @@ gtk_menu_move_current (GtkMenuShell *menu_shell,
if (!match)
{
/* wrap around */
for (i = priv->rows; i > b; i--)
for (i = gtk_menu_get_n_rows (menu); i > b; i--)
{
match = find_child_containing (menu_shell,
l, l + 1, i - 1, i);
@ -3901,7 +3963,7 @@ gtk_menu_move_current (GtkMenuShell *menu_shell,
|| (rtl && direction == GTK_MENU_DIR_PARENT))
{
/* we go one right if possible */
if (r < priv->columns)
if (r < gtk_menu_get_n_columns (menu))
match = find_child_containing (menu_shell, r, r + 1, t, t + 1);
if (!match)

View File

@ -325,7 +325,6 @@ gtk_menu_shell_init (GtkMenuShell *menu_shell)
menu_shell->have_grab = FALSE;
menu_shell->have_xgrab = FALSE;
menu_shell->button = 0;
menu_shell->menu_flag = 0;
menu_shell->activate_time = 0;
}

View File

@ -63,7 +63,7 @@ struct _GtkMenuShell
guint have_grab : 1;
guint have_xgrab : 1;
guint ignore_leave : 1; /* unused */
guint menu_flag : 1;
guint menu_flag : 1; /* unused */
guint ignore_enter : 1;
};

View File

@ -94,6 +94,7 @@ toolbar_size_large (GtkAction *action)
static GtkActionEntry entries[] = {
{ "Menu1Action", NULL, "Menu _1" },
{ "Menu2Action", NULL, "Menu _2" },
{ "Menu3Action", NULL, "_Dynamic Menu" },
{ "cut", GTK_STOCK_CUT, "C_ut", "<control>X",
"Cut the selected text to the clipboard", G_CALLBACK (activate_action) },
@ -187,6 +188,7 @@ static const gchar *ui_info =
" <menuitem action=\"toolbar-small-icons\" />\n"
" <menuitem action=\"toolbar-large-icons\" />\n"
" </menu>\n"
" <menu name=\"DynamicMenu\" action=\"Menu3Action\" />\n"
" </menubar>\n"
" <toolbar name=\"toolbar\">\n"
" <toolitem name=\"cut\" action=\"cut\" />\n"
@ -209,7 +211,7 @@ add_widget (GtkUIManager *merge,
GtkContainer *container)
{
gtk_container_add (container, widget);
gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
gtk_widget_show (widget);
if (GTK_IS_TOOLBAR (widget))
@ -219,12 +221,91 @@ add_widget (GtkUIManager *merge,
}
}
static guint ui_id = 0;
static GtkActionGroup *dag = NULL;
static void
ensure_update (GtkUIManager *manager)
{
GTimer *timer;
double seconds;
gulong microsecs;
timer = g_timer_new ();
g_timer_start (timer);
gtk_ui_manager_ensure_update (manager);
g_timer_stop (timer);
seconds = g_timer_elapsed (timer, &microsecs);
g_timer_destroy (timer);
g_print ("Time: %fs\n", seconds);
}
static void
add_cb (GtkWidget *button,
GtkUIManager *manager)
{
GtkWidget *spinbutton;
GtkAction *action;
int i, num;
char *name, *label;
if (ui_id != 0 || dag != NULL)
return;
spinbutton = g_object_get_data (G_OBJECT (button), "spinbutton");
num = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spinbutton));
dag = gtk_action_group_new ("DynamicActions");
gtk_ui_manager_insert_action_group (manager, dag, 0);
ui_id = gtk_ui_manager_new_merge_id (manager);
for (i = 0; i < num; i++)
{
name = g_strdup_printf ("DynAction%u", i);
label = g_strdup_printf ("Dynamic Item %d", i);
action = g_object_new (GTK_TYPE_ACTION,
"name", name,
"label", label,
NULL);
gtk_action_group_add_action (dag, action);
g_object_unref (action);
gtk_ui_manager_add_ui (manager, ui_id, "/menubar/DynamicMenu",
name, name,
GTK_UI_MANAGER_MENUITEM, FALSE);
}
ensure_update (manager);
}
static void
remove_cb (GtkWidget *button,
GtkUIManager *manager)
{
if (ui_id == 0 || dag == NULL)
return;
gtk_ui_manager_remove_ui (manager, ui_id);
ensure_update (manager);
ui_id = 0;
gtk_ui_manager_remove_action_group (manager, dag);
g_object_unref (dag);
dag = NULL;
}
static void
create_window (GtkActionGroup *action_group)
{
GtkUIManager *merge;
GtkWidget *window;
GtkWidget *box;
GtkWidget *hbox, *spinbutton, *button;
GError *error = NULL;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
@ -249,6 +330,27 @@ create_window (GtkActionGroup *action_group)
g_error_free (error);
}
hbox = gtk_hbox_new (FALSE, 0);
gtk_box_pack_end (GTK_BOX (box), hbox, FALSE, FALSE, 0);
gtk_widget_show (hbox);
spinbutton = gtk_spin_button_new_with_range (100, 10000, 100);
gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
gtk_widget_show (spinbutton);
button = gtk_button_new_with_label ("Add");
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_object_set_data (G_OBJECT (button), "spinbutton", spinbutton);
g_signal_connect (button, "clicked", G_CALLBACK (add_cb), merge);
button = gtk_button_new_with_label ("Remove");
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
g_signal_connect (button, "clicked", G_CALLBACK (remove_cb), merge);
gtk_widget_show (window);
}

View File

@ -3551,6 +3551,23 @@ create_table_menu (GdkScreen *screen, gint cols, gint rows, gboolean tearoff)
menuitem = gtk_check_menu_item_new_with_label ("Check");
gtk_menu_attach (GTK_MENU (submenu), menuitem, 1, 2, 5, 6);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label ("1. Inserted normally (8)");
gtk_widget_show (menuitem);
gtk_menu_shell_insert (GTK_MENU_SHELL (submenu), menuitem, 8);
menuitem = gtk_menu_item_new_with_label ("2. Inserted normally (2)");
gtk_widget_show (menuitem);
gtk_menu_shell_insert (GTK_MENU_SHELL (submenu), menuitem, 2);
menuitem = gtk_menu_item_new_with_label ("3. Inserted normally (0)");
gtk_widget_show (menuitem);
gtk_menu_shell_insert (GTK_MENU_SHELL (submenu), menuitem, 0);
menuitem = gtk_menu_item_new_with_label ("4. Inserted normally (-1)");
gtk_widget_show (menuitem);
gtk_menu_shell_insert (GTK_MENU_SHELL (submenu), menuitem, -1);
/* end of items submenu */
menuitem = gtk_menu_item_new_with_label ("spanning");
@ -3593,6 +3610,9 @@ create_table_menu (GdkScreen *screen, gint cols, gint rows, gboolean tearoff)
menuitem = gtk_menu_item_new_with_label ("Empty");
gtk_menu_attach (GTK_MENU (submenu), menuitem, 0, 1, 0, 1);
submenu = gtk_menu_new ();
gtk_menu_set_screen (GTK_MENU (submenu), screen);
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label ("right");
@ -3617,6 +3637,19 @@ create_table_menu (GdkScreen *screen, gint cols, gint rows, gboolean tearoff)
gtk_widget_show (menuitem);
}
menuitem = gtk_menu_item_new_with_label ("1. Inserted normally (8)");
gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, 8);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label ("2. Inserted normally (2)");
gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, 2);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label ("3. Inserted normally (0)");
gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, 0);
gtk_widget_show (menuitem);
menuitem = gtk_menu_item_new_with_label ("4. Inserted normally (-1)");
gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, -1);
gtk_widget_show (menuitem);
return menu;
}