The Table menu patch; another try
- From: Kristian Rietveld <kris gtk org>
- To: gtk-devel-list gnome org
- Subject: The Table menu patch; another try
- Date: Sat, 16 Aug 2003 23:20:12 +0200
Hey,
Attached is a new revision of the table menu patch, which hopefully
fixes most (if not all) issues/comments raised. I tested it with
Matthias' patch to testgtk and my own silly testprogram, and it seems to
work fine. I hope I didn't screw up as badly as the two previous times
...
The only issue I can still think of is LTR/RTL. I didn't look at it yet
since the other code in gtkmenu.c doesn't seem to support it either.
thanks,
-Kris
Index: gtkmenu.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenu.c,v
retrieving revision 1.134
diff -u -p -r1.134 gtkmenu.c
--- gtkmenu.c 13 Jul 2003 02:42:40 -0000 1.134
+++ gtkmenu.c 16 Aug 2003 20:22:03 -0000
@@ -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,25 @@ struct _GtkMenuPrivate
gboolean have_position;
gint x;
gint y;
+
+ /* info used for the table */
+ guint rows;
+ guint columns;
+
+ guint *height;
+
+ gboolean in_attach;
};
+typedef struct
+{
+ guint left_attach;
+ guint right_attach;
+ guint top_attach;
+ guint bottom_attach;
+}
+AttachInfo;
+
enum {
MOVE_SCROLL,
LAST_SIGNAL
@@ -86,16 +105,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,8 +184,13 @@ static void gtk_menu_style_set
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);
+static void gtk_menu_resize (GtkMenu *menu,
+ guint rows,
+ guint columns);
static void gtk_menu_stop_navigating_submenu (GtkMenu *menu);
static gboolean gtk_menu_stop_navigating_submenu_cb (gpointer user_data);
@@ -179,8 +222,18 @@ static void _gtk_menu_refresh_accel_path
static GtkMenuShellClass *parent_class = NULL;
static const gchar *attach_data_key = "gtk-menu-attach-data";
+#define CHILD_INFO_KEY "gtk-menu-child-info"
+
static guint menu_signals[LAST_SIGNAL] = { 0 };
+static void
+gtk_menu_free_private (gpointer data)
+{
+ GtkMenuPrivate *priv = (GtkMenuPrivate *)data;
+
+ g_free (priv->height);
+}
+
GtkMenuPrivate *
gtk_menu_get_private (GtkMenu *menu)
{
@@ -198,7 +251,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 +300,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 +376,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;
- container_class->remove = gtk_menu_remove;
-
- 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_LEFT_ATTACH,
+ g_param_spec_uint ("left_attach",
+ _("Left Attach"),
+ _("Left attach position of the menu item"),
+ 0, UINT_MAX, 0,
+ G_PARAM_READWRITE));
+
+ gtk_container_class_install_child_property (container_class,
+ CHILD_PROP_RIGHT_ATTACH,
+ g_param_spec_uint ("right_attach",
+ _("Right Attach"),
+ _("Right attach position of the menu item"),
+ 0, UINT_MAX, 0,
+ G_PARAM_READWRITE));
+
+ gtk_container_class_install_child_property (container_class,
+ CHILD_PROP_TOP_ATTACH,
+ g_param_spec_uint ("top_attach",
+ _("Top Attach"),
+ _("Top attach position of the menu item"),
+ 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"),
+ _("Bottom attach position of the menu item"),
+ 0, UINT_MAX, 0,
+ G_PARAM_READWRITE));
binding_set = gtk_binding_set_by_class (class);
gtk_binding_entry_add_signal (binding_set,
@@ -469,6 +558,95 @@ gtk_menu_get_property (GObject *obje
}
}
+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 = g_object_get_data (G_OBJECT (child), ATTACH_INFO_KEY);
+
+ if (!ai)
+ {
+ ai = g_new0 (AttachInfo, 1);
+ g_object_set_data (G_OBJECT (child), ATTACH_INFO_KEY, ai);
+ }
+
+ g_assert (ai != NULL);
+
+ 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);
+
+ if (ai->right_attach >= priv->columns)
+ gtk_menu_resize (menu, priv->rows?priv->rows:1, 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);
+
+ if (ai->bottom_attach >= priv->rows)
+ gtk_menu_resize (menu, ai->bottom_attach,
+ priv->columns?priv->columns:1);
+ break;
+
+ default:
+ GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
+ return;
+ }
+
+ gtk_widget_queue_draw (GTK_WIDGET (menu));
+}
+
+static void
+gtk_menu_get_child_property (GtkContainer *container,
+ GtkWidget *child,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ AttachInfo *ai = g_object_get_data (G_OBJECT (child), ATTACH_INFO_KEY);
+
+ if (!ai)
+ {
+ ai = g_new0 (AttachInfo, 1);
+ g_object_set_data (G_OBJECT (child), ATTACH_INFO_KEY, ai);
+ }
+
+ g_assert (ai != NULL);
+
+ 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 gboolean
gtk_menu_window_event (GtkWidget *window,
GdkEvent *event,
@@ -768,10 +946,53 @@ gtk_menu_real_insert (GtkMenuShell *
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 (priv->in_attach)
+ return;
+
+ 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
@@ -1020,7 +1241,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));
}
@@ -1534,6 +1754,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 );
@@ -1674,6 +1895,7 @@ static void
gtk_menu_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
+ gint i;
GtkMenu *menu;
GtkMenuShell *menu_shell;
GtkWidget *child;
@@ -1682,12 +1904,14 @@ gtk_menu_size_request (GtkWidget *w
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;
@@ -1695,35 +1919,73 @@ gtk_menu_size_request (GtkWidget *w
max_toggle_size = 0;
max_accel_width = 0;
+ g_free (priv->height);
+ priv->height = g_new0 (guint, priv->rows);
+
children = menu_shell->children;
while (children)
{
+ 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)
- */
-
- GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
- gtk_widget_size_request (child, &child_requisition);
-
- requisition->width = MAX (requisition->width, child_requisition.width);
- requisition->height += child_requisition.height;
+ gtk_container_child_get (GTK_CONTAINER (menu), child,
+ "left_attach", &l,
+ "right_attach", &r,
+ "top_attach", &t,
+ "bottom_attach", &b,
+ NULL);
+
+ /* 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)
+ */
- 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 (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);
+
+ /* handle all different span types */
+ if (l == (r - 1))
+ {
+ /* spans a single column */
+ requisition->width = MAX (requisition->width,
+ child_requisition.width);
+ }
+ else
+ {
+ gint part = child_requisition.width / (r - l);
+ requisition->width = MAX (requisition->width, part);
+ }
+
+
+ if (t == (b - 1))
+ {
+ /* spans a single row */
+ priv->height[t] = MAX (priv->height[t], child_requisition.height);
+ }
+ else
+ {
+ gint part = child_requisition.height / (b - t);
+ priv->height[t] = MAX (priv->height[t], part);
+ }
}
+ for (i = 0; i < priv->rows; i++)
+ requisition->height += priv->height[i];
+
requisition->width += max_toggle_size + max_accel_width;
+ requisition->width *= priv->columns;
requisition->width += (GTK_CONTAINER (menu)->border_width +
widget->style->xthickness) * 2;
@@ -1749,6 +2011,8 @@ gtk_menu_size_allocate (GtkWidget *w
GtkMenuShell *menu_shell;
GtkWidget *child;
GtkAllocation child_allocation;
+ GtkRequisition child_requisition;
+ GtkMenuPrivate *priv;
GList *children;
gint x, y;
gint width, height;
@@ -1759,8 +2023,10 @@ gtk_menu_size_allocate (GtkWidget *w
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,
@@ -1772,6 +2038,9 @@ gtk_menu_size_allocate (GtkWidget *w
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);
@@ -1799,41 +2068,60 @@ gtk_menu_size_allocate (GtkWidget *w
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)
{
child = children->data;
children = children->next;
-
+
if (GTK_WIDGET_VISIBLE (child))
{
- GtkRequisition child_requisition;
- gtk_widget_get_child_requisition (child, &child_requisition);
-
- child_allocation.height = child_requisition.height;
+ gint i;
+ guint l, r, t, b;
+
+ gtk_container_child_get (GTK_CONTAINER (menu), child,
+ "left_attach", &l,
+ "right_attach", &r,
+ "top_attach", &t,
+ "bottom_attach", &b,
+ NULL);
+
+ child_allocation.width = (r - l) * base_width;
+ child_allocation.height = 0;
+ child_allocation.x = l * child_allocation.width;
+ child_allocation.y = 0;
+
+ for (i = 0; i < b; i++)
+ {
+ if (i < t)
+ child_allocation.y += priv->height[i];
+ else
+ child_allocation.height += priv->height[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;
+ for (i = 0; i < priv->rows; i++)
+ height += priv->height[i];
+
+ width = priv->columns * child_allocation.width;
+
+ gdk_window_resize (menu->bin_window, width, height);
+ }
if (menu->tearoff_active)
{
@@ -2937,40 +3225,35 @@ 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;
-
- if (GTK_WIDGET_VISIBLE (child))
- {
- gtk_widget_size_request (child, &child_requisition);
- child_height = child_requisition.height;
- }
- else
- child_height = 0;
-
- if (child == menu_item)
- {
- if (is_last_child)
- *is_last_child = (children == NULL);
- if (offset)
- *offset = child_offset;
- if (height)
- *height = child_height;
-
- return TRUE;
- }
-
- child_offset += child_height;
- }
+ gtk_container_child_get (GTK_CONTAINER (menu), menu_item,
+ "top_attach", &item_top_attach,
+ "bottom_attach", &item_bottom_attach,
+ NULL);
- return FALSE;
+ /* this is not going to work if we didn't request size yet ... */
+ if (!priv->height)
+ return FALSE;
+
+ /* 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->height[i];
+
+ if (is_last_child)
+ *is_last_child = (item_bottom_attach == priv->rows);
+ if (offset)
+ *offset = child_offset;
+ if (height)
+ *height = priv->height[item_top_attach];
+
+ return TRUE;
}
static void
@@ -2978,6 +3261,7 @@ gtk_menu_scroll_item_visible (GtkMenuShe
GtkWidget *menu_item)
{
GtkMenu *menu;
+ GtkMenuPrivate *priv;
gint child_offset, child_height;
gint width, height;
gint y;
@@ -2985,6 +3269,7 @@ gtk_menu_scroll_item_visible (GtkMenuShe
gboolean last_child = 0;
menu = GTK_MENU (menu_shell);
+ priv = gtk_menu_get_private (menu);
/* We need to check if the selected item fully visible.
* If not we need to scroll the menu so that it becomes fully
@@ -3164,6 +3449,86 @@ gtk_menu_set_screen (GtkMenu *menu,
}
}
+static void
+gtk_menu_resize (GtkMenu *menu,
+ guint rows,
+ guint columns)
+{
+ GtkMenuPrivate *priv;
+ GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
+
+ g_return_if_fail (rows > 0 && rows < 65536);
+ g_return_if_fail (columns > 0 && columns < 65536);
+
+ rows = MAX (rows, 1);
+ columns = MAX (columns, 1);
+
+ priv = gtk_menu_get_private (menu);
+
+ if (rows != priv->rows || columns != priv->columns)
+ {
+ GList *list;
+
+ for (list = menu_shell->children; list; list = list->next)
+ {
+ guint b, r;
+
+ gtk_container_child_get (GTK_CONTAINER (menu), list->data,
+ "bottom_attach", &b,
+ "right_attach", &r,
+ NULL);
+
+ rows = MAX (rows, b);
+ columns = MAX (columns, r);
+ }
+
+ if (rows != priv->rows)
+ priv->rows = rows;
+
+ if (columns != priv->columns)
+ priv->columns = columns;
+ }
+}
+
+void
+gtk_menu_attach (GtkMenu *menu,
+ GtkWidget *child,
+ guint left_attach,
+ guint right_attach,
+ guint top_attach,
+ guint bottom_attach)
+{
+ GList *i;
+ GtkMenuPrivate *priv;
+
+ g_return_if_fail (GTK_IS_MENU (menu));
+ g_return_if_fail (GTK_IS_MENU_ITEM (child));
+
+ priv = gtk_menu_get_private (menu);
+ if (priv->in_attach)
+ return;
+
+ g_return_if_fail (left_attach < right_attach);
+ g_return_if_fail (top_attach < bottom_attach);
+
+ for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
+ if (i->data == child)
+ break;
+
+ priv->in_attach = TRUE;
+
+ if (!i)
+ 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);
+
+ priv->in_attach = FALSE;
+}
static gint
gtk_menu_get_popup_delay (GtkMenuShell *menu_shell)
@@ -3175,6 +3540,148 @@ gtk_menu_get_popup_delay (GtkMenuShell *
NULL);
return popup_delay;
+}
+
+static GtkWidget *
+find_super_child (GtkMenuShell *menu_shell,
+ int left,
+ int right,
+ int top,
+ int bottom)
+{
+ GList *i;
+
+ /* find a superchild which includes the child given by
+ * left, right, top, bottom.
+ */
+
+ for (i = menu_shell->children; i; i = i->next)
+ {
+ guint l, r, t, b;
+
+ if (!_gtk_menu_item_is_selectable (i->data))
+ continue;
+
+ gtk_container_child_get (GTK_CONTAINER (menu_shell), i->data,
+ "left_attach", &l,
+ "right_attach", &r,
+ "top_attach", &t,
+ "bottom_attach", &b,
+ NULL);
+
+ if (l <= left && right <= r
+ && t <= top && bottom <= b)
+ return GTK_WIDGET (i->data);
+ }
+
+ return NULL;
+}
+
+static void
+gtk_menu_move_current (GtkMenuShell *menu_shell,
+ GtkMenuDirectionType direction)
+{
+ GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (menu_shell));
+ gboolean handled = FALSE;
+
+ /* use special table menu key bindings */
+ if (menu_shell->active_menu_item && priv->columns > 1)
+ {
+ GtkWidget *match = NULL;
+ guint l, r, t, b;
+ int i;
+
+ gtk_container_child_get (GTK_CONTAINER (menu_shell),
+ menu_shell->active_menu_item,
+ "left_attach", &l,
+ "right_attach", &r,
+ "top_attach", &t,
+ "bottom_attach", &b,
+ NULL);
+
+ if (direction == GTK_MENU_DIR_NEXT)
+ {
+ for (i = b; i < priv->rows; i++)
+ {
+ match = find_super_child (menu_shell, l, l + 1, i, i + 1);
+ if (match)
+ break;
+ }
+
+ if (!match)
+ {
+ /* wrap around */
+ for (i = 0; i < t; i++)
+ {
+ match = find_super_child (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_super_child (menu_shell, l, l + 1, i - 1, i);
+ if (match)
+ break;
+ }
+
+ if (!match)
+ {
+ /* wrap around */
+ for (i = priv->rows; i > b; i--)
+ {
+ match = find_super_child (menu_shell, l, l + 1, i - 1, i);
+ if (match)
+ break;
+ }
+ }
+ }
+ else if (direction == GTK_MENU_DIR_PARENT)
+ {
+ /* we go one left if possible */
+ if (l > 0)
+ match = find_super_child (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 (direction == GTK_MENU_DIR_CHILD)
+ {
+ /* we go one right if possible */
+ if (r < priv->columns)
+ match = find_super_child (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);
+ handled = TRUE;
+ }
+ }
+
+ if (handled)
+ return;
+
+ GTK_MENU_SHELL_CLASS (parent_class)->move_current (menu_shell, direction);
}
static gint
Index: gtkmenu.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenu.h,v
retrieving revision 1.34
diff -u -p -r1.34 gtkmenu.h
--- gtkmenu.h 8 Nov 2002 19:41:42 -0000 1.34
+++ gtkmenu.h 16 Aug 2003 20:22:03 -0000
@@ -188,6 +188,15 @@ void gtk_menu_reorder_child
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))
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]