Table menu patch for GtkMenu
- From: Kristian Rietveld <kris gtk org>
- To: GTK Development list <gtk-devel-list gnome org>
- Subject: Table menu patch for GtkMenu
- Date: 12 Jul 2003 17:22:35 +0200
Hello,
Before I can land the new combo box in gtk+ HEAD, I need to get my happy
fun table menu patch for GtkMenu approved. So I attached this patch. A
good chunk of the code is based on the GtkTable size
allocation/requisition code. I guess it's a pretty sane patch, but maybe
some won't like the proposed API.
thanks,
-Kris
Index: gtkmenu.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenu.c,v
retrieving revision 1.128.2.1
diff -u -p -r1.128.2.1 gtkmenu.c
--- gtkmenu.c 28 Feb 2003 02:03:50 -0000 1.128.2.1
+++ gtkmenu.c 13 Apr 2003 19:58:41 -0000
@@ -63,17 +63,43 @@
typedef struct _GtkMenuAttachData GtkMenuAttachData;
typedef struct _GtkMenuPrivate GtkMenuPrivate;
+typedef struct _ChildInfo ChildInfo;
+typedef struct _ColRow ColRow;
+
struct _GtkMenuAttachData
{
GtkWidget *attach_widget;
GtkMenuDetachFunc detacher;
};
+struct _ChildInfo
+{
+ GtkWidget *widget;
+ guint16 left_attach;
+ guint16 right_attach;
+ guint16 top_attach;
+ guint16 bottom_attach;
+};
+
+struct _ColRow
+{
+ guint16 requisition;
+ guint16 allocation;
+};
+
struct _GtkMenuPrivate
{
gboolean have_position;
gint x;
gint y;
+
+ /* info used for the table */
+ guint rows;
+ guint columns;
+ GList *child_info;
+
+ ColRow *col_info;
+ ColRow *row_info;
};
enum {
@@ -146,6 +172,8 @@ 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);
@@ -179,6 +207,8 @@ 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 };
GtkMenuPrivate *
@@ -292,6 +322,7 @@ gtk_menu_class_init (GtkMenuClass *class
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;
binding_set = gtk_binding_set_by_class (class);
gtk_binding_entry_add_signal (binding_set,
@@ -993,7 +1024,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));
}
@@ -1506,6 +1536,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 );
@@ -1639,6 +1670,176 @@ gtk_menu_unrealize (GtkWidget *widget)
}
static void
+gtk_menu_size_request_init (GtkMenu *menu)
+{
+ gint i;
+ GList *child;
+ GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+
+ for (i = 0; i < priv->rows; i++)
+ priv->row_info[i].requisition = 0;
+
+ for (i = 0; i < priv->columns; i++)
+ priv->col_info[i].requisition = 0;
+
+ for (child = GTK_MENU_SHELL (menu)->children; child; child = child->next)
+ {
+ if (GTK_WIDGET_VISIBLE (child->data))
+ gtk_widget_size_request (child->data, NULL);
+ }
+}
+
+static void
+gtk_menu_size_request_pass1 (GtkMenu *menu)
+{
+ GList *i;
+ GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+ gint width, height;
+
+ /* check for childs which span a single column/row */
+
+ for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
+ {
+ GtkWidget *child = GTK_WIDGET (i->data);
+ ChildInfo *ci;
+ GtkRequisition child_requisition;
+
+ if (!GTK_WIDGET_VISIBLE (child))
+ continue;
+
+ gtk_widget_get_child_requisition (child, &child_requisition);
+
+ ci = g_object_get_data (G_OBJECT (child), CHILD_INFO_KEY);
+
+ /* child spans a single column */
+ if (ci->left_attach == (ci->right_attach - 1))
+ {
+ width = child_requisition.width;
+ priv->col_info[ci->left_attach].requisition = MAX (priv->col_info[ci->left_attach].requisition, width);
+ }
+
+ /* child spans a single row */
+ if (ci->top_attach == (ci->bottom_attach - 1))
+ {
+ height = child_requisition.height;
+ priv->row_info[ci->top_attach].requisition = MAX (priv->row_info[ci->top_attach].requisition, height);
+ }
+ }
+}
+
+static void
+gtk_menu_size_request_pass2 (GtkMenu *menu)
+{
+ gint max_height = 0, max_width = 0;
+ gint row, col;
+ GtkMenuPrivate *priv;
+
+ priv = gtk_menu_get_private (menu);
+
+ /* all items are homogeneous by default */
+ for (col = 0; col < priv->columns; col++)
+ max_width = MAX (max_width, priv->col_info[col].requisition);
+ for (row = 0; row < priv->rows; row++)
+ max_height = MAX (max_height, priv->row_info[row].requisition);
+
+ for (col = 0; col < priv->columns; col++)
+ priv->col_info[col].requisition = max_width;
+ for (row = 0; row < priv->rows; row++)
+ priv->row_info[row].requisition = max_height;
+}
+
+static void
+gtk_menu_size_request_pass3 (GtkMenu *menu)
+{
+ GList *i;
+ GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+ gint width, height;
+ gint row, col;
+ gint extra;
+
+ /* handle childs which span multiple columns */
+
+ for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
+ {
+ GtkWidget *child = GTK_WIDGET (i->data);
+ ChildInfo *ci;
+
+ if (!GTK_WIDGET_VISIBLE (child))
+ continue;
+
+ ci = g_object_get_data (G_OBJECT (child), CHILD_INFO_KEY);
+
+ /* child spans multiple columns */
+ if (ci->left_attach != (ci->right_attach - 1))
+ {
+ GtkRequisition child_requisition;
+
+ gtk_widget_get_child_requisition (child, &child_requisition);
+
+ /* check and see if there is already enough space for the child */
+ width = 0;
+ for (col = ci->left_attach; col < ci->right_attach; col++)
+ width += priv->col_info[col].requisition;
+
+ /* if we need to request more space for this child to fill its
+ * requisition, then divide up the needed space amongst the
+ * columns it spans.
+ */
+ if (width < child_requisition.width)
+ {
+ gint n_expand;
+
+ width = child_requisition.width - width;
+
+ n_expand = ci->right_attach - ci->left_attach;
+
+ for (col = ci->left_attach; col < ci->right_attach; col++)
+ {
+ extra = width / n_expand;
+ priv->col_info[col].requisition += extra;
+ width -= extra;
+ n_expand--;
+ }
+ }
+ }
+
+ /* child spans multiple rows */
+ if (ci->top_attach != (ci->bottom_attach - 1))
+ {
+ GtkRequisition child_requisition;
+
+ gtk_widget_get_child_requisition (child, &child_requisition);
+
+ /* check and see if there is already enough space for the child */
+ height = 0;
+ for (row = ci->top_attach; row < ci->bottom_attach; row++)
+ height += priv->row_info[row].requisition;
+
+ /* if we need to request more space for this child to fill its
+ * requistion, then devide up the needed space amongst the
+ * rows it spans.
+ */
+ if (height < child_requisition.height)
+ {
+ gint n_expand;
+
+ height = child_requisition.height - height;
+
+ n_expand = ci->bottom_attach - ci->top_attach;
+
+ for (row = ci->top_attach; row < ci->bottom_attach; row++)
+ {
+ extra = height / n_expand;
+ priv->row_info[row].requisition += extra;
+ height -= extra;
+ n_expand--;
+ }
+ }
+ }
+ }
+}
+
+static void
gtk_menu_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
@@ -1649,12 +1850,14 @@ gtk_menu_size_request (GtkWidget *w
guint max_toggle_size;
guint max_accel_width;
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;
@@ -1662,41 +1865,72 @@ gtk_menu_size_request (GtkWidget *w
max_toggle_size = 0;
max_accel_width = 0;
- children = menu_shell->children;
- while (children)
+ /* if gtk_menu_resize hasn't been called rows and columns are still zero,
+ * and gtk_menu_attach hasn't been called yet. So we use the old size
+ * requisition algorithm.
+ */
+ if (!priv->rows && !priv->columns)
{
- child = children->data;
- children = children->next;
+ children = menu_shell->children;
+ while (children)
+ {
+ child = children->data;
+ children = children->next;
- if (GTK_WIDGET_VISIBLE (child))
- {
- gint toggle_size;
+ if (GTK_WIDGET_VISIBLE (child))
+ {
+ gint toggle_size;
- /* 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)
- */
+ /* 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);
+ 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;
+ requisition->width = MAX (requisition->width, child_requisition.width);
+ requisition->height += child_requisition.height;
- 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);
- }
- }
-
- requisition->width += max_toggle_size + max_accel_width;
- requisition->width += (GTK_CONTAINER (menu)->border_width +
- widget->style->xthickness) * 2;
- requisition->height += (GTK_CONTAINER (menu)->border_width +
- widget->style->ythickness) * 2;
+ 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);
+ }
+ }
+
+ requisition->width += max_toggle_size + max_accel_width;
+ requisition->width += (GTK_CONTAINER (menu)->border_width +
+ widget->style->xthickness) * 2;
+ requisition->height += (GTK_CONTAINER (menu)->border_width +
+ widget->style->ythickness) * 2;
- menu->toggle_size = max_toggle_size;
+ menu->toggle_size = max_toggle_size;
+ }
+ else /* else we use the table based requisition algorithm */
+ {
+ gint row, col;
+
+ requisition->width = 0;
+ requisition->height = 0;
+
+ gtk_menu_size_request_init (menu);
+ gtk_menu_size_request_pass1 (menu);
+ gtk_menu_size_request_pass2 (menu);
+ gtk_menu_size_request_pass3 (menu);
+ gtk_menu_size_request_pass2 (menu);
+
+ for (col = 0; col < priv->columns; col++)
+ requisition->width += priv->col_info[col].requisition;
+
+ for (row = 0; row < priv->rows; row++)
+ requisition->height += priv->row_info[row].requisition;
+
+ requisition->width += (GTK_CONTAINER (menu)->border_width +
+ widget->style->xthickness) * 2;
+ requisition->height += (GTK_CONTAINER (menu)->border_width +
+ widget->style->ythickness) * 2;
+ }
/* Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap).
*/
@@ -1705,6 +1939,93 @@ gtk_menu_size_request (GtkWidget *w
}
static void
+gtk_menu_size_allocation_init (GtkMenu *menu)
+{
+ GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+ gint row, col;
+
+ for (col = 0; col < priv->columns; col++)
+ priv->col_info[col].allocation = priv->col_info[col].requisition;
+
+ for (row = 0; row < priv->rows; row++)
+ priv->row_info[row].allocation = priv->row_info[row].requisition;
+}
+
+static void
+gtk_menu_size_allocation_pass1 (GtkMenu *menu,
+ gint real_width)
+{
+ gint width, col, extra;
+ GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+
+ /* if the width is bigger than requested, then divide it over the
+ * columns. Note that the height stays the same as the requested width.
+ *
+ * is that a FIXME?
+ */
+
+ width = real_width;
+ for (col = 0; col + 1 < priv->columns; col++)
+ {
+ extra = width / (priv->columns - col);
+ priv->col_info[col].allocation = MAX (1, extra);
+ width -= extra;
+ }
+}
+
+static gint
+gtk_menu_size_allocation_pass2 (GtkMenu *menu)
+{
+ GList *i;
+ GtkAllocation allocation;
+ GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+ gint height = 0;
+
+ allocation.x = 0;
+ allocation.y = 0;
+
+ for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
+ {
+ gint row, col;
+ gint x = 0, y = 0;
+
+ ChildInfo *ci;
+ GtkWidget *child = GTK_WIDGET (i->data);
+ GtkRequisition child_requisition;
+
+ if (!GTK_WIDGET_VISIBLE (i->data))
+ continue;
+
+ ci = g_object_get_data (G_OBJECT (child), CHILD_INFO_KEY);
+
+ gtk_widget_get_child_requisition (child, &child_requisition);
+
+ for (col = 0; col < ci->left_attach; col++)
+ x += priv->col_info[col].allocation;
+
+ for (row = 0; row < ci->top_attach; row++)
+ y += priv->row_info[row].allocation;
+
+ allocation.width = (child_requisition.width / priv->col_info[col].allocation + 1) * priv->col_info[col].allocation;
+ if (!(child_requisition.width % priv->col_info[col].allocation))
+ allocation.width -= priv->col_info[col].allocation;
+ allocation.x = x;
+
+ allocation.height = child_requisition.height;
+ allocation.y = y;
+
+ height += child_requisition.height;
+
+ /* FIXME: RTL? */
+
+ gtk_widget_size_allocate (child, &allocation);
+ gtk_widget_queue_draw (child);
+ }
+
+ return height;
+}
+
+static void
gtk_menu_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
@@ -1757,31 +2078,45 @@ gtk_menu_size_allocate (GtkWidget *w
if (menu_shell->children)
{
+ GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+
child_allocation.x = 0;
child_allocation.y = 0;
child_allocation.width = width;
- children = menu_shell->children;
- while (children)
- {
- child = children->data;
- children = children->next;
-
- if (GTK_WIDGET_VISIBLE (child))
+ if (!priv->rows && !priv->columns)
+ {
+ /* use the default algorithms */
+ children = menu_shell->children;
+ while (children)
{
- GtkRequisition child_requisition;
- gtk_widget_get_child_requisition (child, &child_requisition);
+ 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;
+ child_allocation.height = child_requisition.height;
- 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);
+ 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;
+ child_allocation.y += child_allocation.height;
+ }
}
}
+ else
+ {
+ /* use the table algorithm */
+
+ gtk_menu_size_allocation_init (menu);
+ gtk_menu_size_allocation_pass1 (menu, width);
+ child_allocation.y = gtk_menu_size_allocation_pass2 (menu);
+ }
/* Resize the item window */
if (GTK_WIDGET_REALIZED (widget))
@@ -1972,6 +2307,92 @@ gtk_menu_button_release (GtkWidget
return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
}
+static GtkWidget *
+find_child (GList *childs,
+ int row,
+ int col)
+{
+ GList *i;
+
+ for (i = childs; i; i = i->next)
+ {
+ ChildInfo *ci = g_object_get_data (G_OBJECT (i->data), CHILD_INFO_KEY);
+
+ if (ci->top_attach == row && ci->left_attach == col)
+ return GTK_WIDGET (i->data);
+ }
+
+ return NULL;
+}
+
+static void
+gtk_menu_move_selected_left_right (GtkMenu *menu,
+ gint distance)
+{
+ GtkMenuPrivate *priv;
+ GtkMenuShell *menu_shell = GTK_MENU_SHELL (menu);
+ gboolean had_selection;
+
+ int i;
+ GtkWidget *match = NULL;
+ ChildInfo *ci = NULL;
+
+ had_selection = menu_shell->active_menu_item != NULL;
+
+ if (menu_shell->active_menu_item)
+ {
+ ci = g_object_get_data (G_OBJECT (menu_shell->active_menu_item), CHILD_INFO_KEY);
+ }
+
+ priv = gtk_menu_get_private (menu);
+
+ /* FIXME: this is just totally broken */
+
+ if (distance == -1)
+ {
+ for (i = ci->left_attach - 1; i >= 0; i--)
+ {
+ match = find_child (menu_shell->children, ci->top_attach, i);
+ if (match)
+ break;
+ }
+
+ if (!match)
+ {
+ /* wrap around */
+ for (i = priv->columns; i >= ci->right_attach; i--)
+ {
+ match = find_child (menu_shell->children, ci->top_attach, i);
+ if (match)
+ break;
+ }
+ }
+ }
+ else if (distance == 1)
+ {
+ for (i = ci->right_attach; i < priv->columns; i++)
+ {
+ match = find_child (menu_shell->children, ci->top_attach, i);
+ if (match)
+ break;
+ }
+
+ if (!match)
+ {
+ /* wrap around */
+ for (i = 0; i < ci->right_attach; i++)
+ {
+ match = find_child (menu_shell->children, ci->top_attach, i);
+ if (match)
+ break;
+ }
+ }
+ }
+
+ if (match)
+ gtk_menu_shell_select_item (GTK_MENU_SHELL (menu), match);
+}
+
static gboolean
gtk_menu_key_press (GtkWidget *widget,
GdkEventKey *event)
@@ -1984,15 +2405,29 @@ gtk_menu_key_press (GtkWidget *widget,
guint accel_key, accel_mods;
GdkModifierType consumed_modifiers;
GdkDisplay *display;
+ GtkMenuPrivate *priv;
g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
menu_shell = GTK_MENU_SHELL (widget);
menu = GTK_MENU (widget);
+
+ priv = gtk_menu_get_private (GTK_MENU (widget));
gtk_menu_stop_navigating_submenu (menu);
+ if (priv->rows && priv->columns)
+ {
+ if (event->keyval == GDK_Left || event->keyval == GDK_Right)
+ {
+ gtk_menu_move_selected_left_right (menu,
+ event->keyval == GDK_Left?-1:1);
+
+ return TRUE;
+ }
+ }
+
if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
return TRUE;
@@ -2910,6 +3345,7 @@ gtk_menu_scroll_item_visible (GtkMenuShe
GtkWidget *menu_item)
{
GtkMenu *menu;
+ GtkMenuPrivate *priv;
gint child_offset, child_height;
gint width, height;
gint y;
@@ -2917,6 +3353,11 @@ gtk_menu_scroll_item_visible (GtkMenuShe
gboolean last_child = 0;
menu = GTK_MENU (menu_shell);
+ priv = gtk_menu_get_private (menu);
+
+ /* disable scrolling stuff for table menus */
+ if (priv->rows && priv->columns)
+ return;
/* We need to check if the selected item fully visible.
* If not we need to scroll the menu so that it becomes fully
@@ -3090,6 +3531,195 @@ gtk_menu_set_screen (GtkMenu *menu,
}
}
+static void
+gtk_menu_resize (GtkMenu *menu,
+ guint rows,
+ guint columns)
+{
+ GtkMenuPrivate *priv;
+
+ 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 = priv->child_info; list; list = list->next)
+ {
+ ChildInfo *ci = list->data;
+
+ rows = MAX (rows, ci->bottom_attach);
+ columns = MAX (columns, ci->right_attach);
+ }
+
+ if (rows != priv->rows)
+ {
+ guint i;
+
+ i = priv->rows;
+ priv->rows = rows;
+ priv->row_info = g_realloc (priv->row_info, priv->rows * sizeof (ColRow));
+
+ for (; i < priv->rows; i++)
+ {
+ priv->row_info[i].requisition = 0;
+ priv->row_info[i].allocation = 0;
+ }
+
+ /* FIXME: notify? */
+ }
+
+ if (columns != priv->columns)
+ {
+ guint i;
+
+ i = priv->columns;
+ priv->columns = columns;
+ priv->col_info = g_realloc (priv->col_info, priv->columns * sizeof (ColRow));
+
+ for (; i < priv->columns; i++)
+ {
+ priv->col_info[i].requisition = 0;
+ priv->col_info[i].allocation = 0;
+ }
+
+ /* FIXME: notify? */
+ }
+ }
+}
+
+void
+gtk_menu_attach (GtkMenu *menu,
+ GtkWidget *child,
+ guint left_attach,
+ guint right_attach,
+ guint top_attach,
+ guint bottom_attach)
+{
+ GtkMenuPrivate *priv;
+ ChildInfo *ci;
+
+ g_return_if_fail (GTK_IS_MENU (menu));
+ g_return_if_fail (GTK_IS_MENU_ITEM (child));
+ g_return_if_fail (child->parent == NULL);
+
+ g_return_if_fail (left_attach < right_attach);
+ g_return_if_fail (top_attach < bottom_attach);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
+
+ priv = gtk_menu_get_private (menu);
+
+ if (right_attach >= priv->columns)
+ gtk_menu_resize (menu, priv->rows?priv->rows:1, right_attach);
+
+ if (bottom_attach >= priv->rows)
+ gtk_menu_resize (menu, bottom_attach, priv->columns?priv->columns:1);
+
+ ci = g_new (ChildInfo, 1);
+ ci->widget = child;
+ ci->left_attach = left_attach;
+ ci->right_attach = right_attach;
+ ci->top_attach = top_attach;
+ ci->bottom_attach = bottom_attach;
+
+ g_object_set_data (G_OBJECT (child), CHILD_INFO_KEY, ci);
+
+ priv->child_info = g_list_append (priv->child_info, ci);
+}
+
+void
+gtk_menu_set_attach (GtkMenu *menu,
+ GtkWidget *child,
+ guint left_attach,
+ guint right_attach,
+ guint top_attach,
+ guint bottom_attach)
+{
+ GtkMenuPrivate *priv;
+ ChildInfo *ci;
+
+ 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);
+
+ priv = gtk_menu_get_private (menu);
+
+ ci = g_object_get_data (G_OBJECT (child), CHILD_INFO_KEY);
+
+ if (!ci)
+ {
+ ci = g_new (ChildInfo, 1);
+ ci->widget = child;
+
+ g_object_set_data (G_OBJECT (child), CHILD_INFO_KEY, ci);
+
+ priv->child_info = g_list_append (priv->child_info, ci);
+ }
+
+ ci->left_attach = left_attach;
+ ci->right_attach = right_attach;
+ ci->top_attach = top_attach;
+ ci->bottom_attach = bottom_attach;
+
+ if (right_attach >= priv->columns)
+ gtk_menu_resize (menu, priv->rows?priv->rows:1, right_attach);
+
+ if (bottom_attach >= priv->rows)
+ gtk_menu_resize (menu, bottom_attach, priv->columns?priv->columns:1);
+
+ gtk_widget_queue_draw (GTK_WIDGET (menu));
+}
+
+gboolean
+gtk_menu_occupied (GtkMenu *menu,
+ guint left_attach,
+ guint right_attach,
+ guint top_attach,
+ guint bottom_attach)
+{
+ GList *i;
+ GtkMenuPrivate *priv;
+
+ g_return_val_if_fail (GTK_IS_MENU (menu), TRUE);
+
+ g_return_val_if_fail (left_attach < right_attach, TRUE);
+ g_return_val_if_fail (top_attach < bottom_attach, TRUE);
+
+ priv = gtk_menu_get_private (menu);
+
+ for (i = priv->child_info; i; i = i->next)
+ {
+ gboolean h_intersect = FALSE;
+ gboolean v_intersect = FALSE;
+ ChildInfo *ci = (ChildInfo *)i->data;
+
+ /* look if this item intersects with the given coordinates */
+ h_intersect = left_attach <= ci->left_attach &&
+ ci->left_attach <= right_attach;
+ h_intersect &= left_attach <= ci->right_attach &&
+ ci->right_attach <= right_attach;
+
+ v_intersect = top_attach <= ci->top_attach &&
+ ci->top_attach <= bottom_attach;
+ v_intersect &= top_attach <= ci->bottom_attach &&
+ ci->bottom_attach <= bottom_attach;
+
+ if (h_intersect && v_intersect)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static gint
gtk_menu_get_popup_delay (GtkMenuShell *menu_shell)
@@ -3101,6 +3731,77 @@ gtk_menu_get_popup_delay (GtkMenuShell *
NULL);
return popup_delay;
+}
+
+static void
+gtk_menu_move_current (GtkMenuShell *menu_shell,
+ GtkMenuDirectionType direction)
+{
+ GtkMenuPrivate *priv = gtk_menu_get_private (GTK_MENU (menu_shell));
+ ChildInfo *ci = NULL;
+ GtkWidget *match = NULL;
+
+ if (menu_shell->active_menu_item)
+ {
+ ci = g_object_get_data (G_OBJECT (menu_shell->active_menu_item),
+ CHILD_INFO_KEY);
+ }
+
+ /* only handle next/prev directions here */
+ if (priv->rows && priv->columns)
+ {
+ int i;
+
+ /* FIXME: BROKEN */
+
+ if (direction == GTK_MENU_DIR_NEXT)
+ {
+ for (i = ci->bottom_attach; i < priv->rows; i++)
+ {
+ match = find_child (menu_shell->children, i, ci->left_attach);
+ if (match)
+ break;
+ }
+
+ if (!match)
+ {
+ /* wrap around */
+ for (i = 0; i < ci->bottom_attach; i++)
+ {
+ match = find_child (menu_shell->children, i, ci->left_attach);
+ if (match)
+ break;
+ }
+ }
+ }
+ else if (direction == GTK_MENU_DIR_PREV)
+ {
+ for (i = ci->top_attach - 1; i >= 0; i--)
+ {
+ match = find_child (menu_shell->children, i, ci->left_attach);
+ if (match)
+ break;
+ }
+
+ if (!match)
+ {
+ /* wrap around */
+ for (i = priv->rows; i >= ci->bottom_attach; i--)
+ {
+ match = find_child (menu_shell->children, i, ci->left_attach);
+ if (match)
+ break;
+ }
+ }
+ }
+
+ if (match)
+ gtk_menu_shell_select_item (menu_shell, match);
+
+ 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 13 Apr 2003 19:58:42 -0000
@@ -188,6 +188,25 @@ 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);
+void gtk_menu_set_attach (GtkMenu *menu,
+ GtkWidget *child,
+ guint left_attach,
+ guint right_attach,
+ guint top_attach,
+ guint bottom_attach);
+gboolean gtk_menu_occupied (GtkMenu *menu,
+ 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]