Table menu patch for GtkMenu



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]