The Table menu patch; another try



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]