? 20080229-openismus-goocanvas-gridlines.patch
? 20080304-openismus-goocanvas-gridlines2.patch
? 20080325-openismus-goocanvas-gridlines3.patch
? 20080327-openismus-goocanvas-gridlines4.patch
Index: ChangeLog
===================================================================
RCS file: /cvs/cairo/goocanvas/ChangeLog,v
retrieving revision 1.132
diff -u -p -r1.132 ChangeLog
--- ChangeLog	27 Mar 2008 14:08:54 -0000	1.132
+++ ChangeLog	27 Mar 2008 14:17:55 -0000
@@ -1,3 +1,9 @@
+2008-03-27  Armin Burgmeier  <armin@openismus.com>
+
+	* src/goocanvastable.c: Implemented grid lines between table items by
+	adding"row-grid-line-width", "column-grid-line-width",
+	"row-border-spacing" and "column-border-spacing" properties.
+
 2008-03-27  Damon Chaplin  <damon@gnome.org>
 
 	* src/goocanvasitem.c (goo_canvas_item_get_simple_transform): 
Index: demo/table-demo.c
===================================================================
RCS file: /cvs/cairo/goocanvas/demo/table-demo.c,v
retrieving revision 1.7
diff -u -p -r1.7 table-demo.c
--- demo/table-demo.c	19 Jun 2007 11:22:53 -0000	1.7
+++ demo/table-demo.c	27 Mar 2008 14:17:55 -0000
@@ -54,7 +54,8 @@ create_table1 (GooCanvasItem *parent,
 	       gdouble        x,
 	       gdouble        y,
 	       gdouble        width,
-	       gdouble        height)
+	       gdouble        height,
+	       gboolean       with_grid_lines)
 {
   GooCanvasItem *table, *items[9];
   GooCanvasBounds bounds;
@@ -66,6 +67,18 @@ create_table1 (GooCanvasItem *parent,
 				NULL);
   goo_canvas_item_translate (table, x, y);
 
+  if(with_grid_lines)
+  {
+    g_object_set(G_OBJECT(table),
+                 "row-spacing",  2.0,
+                 "column-spacing", 2.0,
+                 "x-border-spacing", 1.0,
+                 "y-border-spacing", 1.0,
+                 "horz-grid-line-width", 1.0,
+                 "vert-grid-line-width", 2.0,
+                 NULL);
+  }
+
   items[i++] = create_item (table, 17.3, 12.9, 0, 0, 1, 1,
 			    TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
   items[i++] = create_item (table, 33.1, 17.2, 1, 0, 1, 1,
@@ -96,6 +109,66 @@ create_table1 (GooCanvasItem *parent,
     }
 }
 
+/* Creates a table with items spanning multiple cells */
+void
+create_table2 (GooCanvasItem *parent,
+	       gdouble        x,
+	       gdouble        y,
+	       gdouble        width,
+	       gdouble        height,
+	       gboolean       with_grid_lines)
+{
+  GooCanvasItem *table, *items[9];
+  GooCanvasBounds bounds;
+  gint i = 0;
+
+  table = goo_canvas_table_new (parent,
+				"width", width,
+				"height", height,
+				NULL);
+  goo_canvas_item_translate (table, x, y);
+
+  if(with_grid_lines)
+  {
+    g_object_set(G_OBJECT(table),
+                 "row-spacing",  2.0,
+                 "column-spacing", 2.0,
+                 "x-border-spacing", 1.0,
+                 "y-border-spacing", 1.0,
+                 "horz-grid-line-width", 1.0,
+                 "vert-grid-line-width", 2.0,
+                 NULL);
+  }
+
+  items[i++] = create_item (table, 17.3, 12.9, 0, 0, 2, 2,
+			    TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+  items[i++] = create_item (table, 33.1, 17.2, 0, 2, 1, 1,
+			    TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+  items[i++] = create_item (table, 41.6, 23.9, 1, 2, 1, 1,
+			    TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+
+  items[i++] = create_item (table, 7.1, 5.7, 0, 3, 2, 1,
+			    TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+  items[i++] = create_item (table, 13.5, 18.2, 2, 0, 1, 1,
+			    TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+  items[i++] = create_item (table, 25.2, 22.1, 2, 1, 1, 3,
+			    TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+
+  items[i++] = create_item (table, 11.3, 11.7, 3, 0, 1, 1,
+			    TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+  items[i++] = create_item (table, 21.7, 18.8, 3, 1, 1, 1,
+			    TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+  items[i++] = create_item (table, 22.2, 13.8, 3, 2, 1, 2,
+			    TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+
+  for (i = 0; i < 9; i++)
+    {
+      goo_canvas_item_get_bounds (items[i], &bounds);
+      g_print ("Item %i: %g,%g - %g,%g\n", i,
+	       bounds.x1 - x, bounds.y1 - y,
+	       bounds.x2 - x, bounds.y2 - y);
+    }
+}
 
 void
 setup_canvas (GtkWidget *canvas)
@@ -105,13 +178,31 @@ setup_canvas (GtkWidget *canvas)
   root = goo_canvas_get_root_item (GOO_CANVAS (canvas));
 
   g_print ("\nTable at default size...\n");
-  create_table1 (root, 50, 50, -1, -1);
+  create_table1 (root, 50, 50, -1, -1, FALSE);
 
   g_print ("\nTable at reduced size...\n");
-  create_table1 (root, 250, 50, 30, 30);
+  create_table1 (root, 250, 50, 30, 30, FALSE);
 
   g_print ("\nTable at enlarged size...\n");
-  create_table1 (root, 450, 50, 100, 100);
+  create_table1 (root, 450, 50, 100, 100, FALSE);
+
+  g_printf("\nTable with grid lines at default size...\n");
+  create_table1 (root, 50, 250, -1, -1, TRUE);
+
+  g_printf("\nTable with grid lines at reduced size...\n");
+  create_table1 (root, 250, 250, 30, 30, TRUE);
+
+  g_printf("\nTable with grid lines at enlarged size...\n");
+  create_table1 (root, 450, 250, 150, 150, TRUE);
+
+  g_printf("Multispanning table with grid lines at default size...\n");
+  create_table2 (root, 50, 450, -1, -1, TRUE);
+
+  g_printf("Multispanning table with grid lines at reduced size...\n");
+  create_table2 (root, 250, 450, 30, 30, TRUE);
+
+  g_printf("Multispanning table with grid lines at enlarged size...\n");
+  create_table2 (root, 450, 450, 150, 150, TRUE);
 }
 
 
Index: src/goocanvastable.c
===================================================================
RCS file: /cvs/cairo/goocanvas/src/goocanvastable.c,v
retrieving revision 1.15
diff -u -p -r1.15 goocanvastable.c
--- src/goocanvastable.c	23 Nov 2007 15:34:33 -0000	1.15
+++ src/goocanvastable.c	27 Mar 2008 14:17:57 -0000
@@ -56,7 +56,11 @@ enum
   PROP_ROW_SPACING,
   PROP_COLUMN_SPACING,
   PROP_HOMOGENEOUS_ROWS,
-  PROP_HOMOGENEOUS_COLUMNS
+  PROP_HOMOGENEOUS_COLUMNS,
+  PROP_X_BORDER_SPACING,
+  PROP_Y_BORDER_SPACING,
+  PROP_VERT_GRID_LINE_WIDTH,
+  PROP_HORZ_GRID_LINE_WIDTH
 };
 
 enum
@@ -106,10 +110,15 @@ struct _GooCanvasTableChild
 typedef struct _GooCanvasTableDimensionLayoutData GooCanvasTableDimensionLayoutData;
 struct _GooCanvasTableDimensionLayoutData
 {
-  /* This is the actual spacing for after the row or column. It is set in
+  /* This is the actual spacing for after the row or column. This is the
+     spacing for that row plus the grid line width. It is set in
      goo_canvas_table_init_layout_data(). */
   gdouble spacing;
 
+  /* Stores the grid line visibilty for the grid lines to the right or bottom
+     of this row/column, respectivel, for each cell in the other dimension. */
+  guint32* grid_line_visibility;
+
   /* The requisition is calculated in goo_canvas_table_size_request_pass[123]*/
   gdouble requisition;
 
@@ -139,6 +148,15 @@ struct _GooCanvasTableChildLayoutData
   gdouble start_pad[2], end_pad[2];
 };
 
+#define GOO_CANVAS_TABLE_SET_GRID_LINE_VISIBILITY(dim, x, y) \
+  ((dim)[(x)].grid_line_visibility[(y)/32] |= (1 << ((y) % 32)))
+
+#define GOO_CANVAS_TABLE_UNSET_GRID_LINE_VISIBILITY(dim, x, y) \
+  ((dim)[(x)].grid_line_visibility[(y)/32] &= ~(1 << ((y) % 32)))
+
+#define GOO_CANVAS_TABLE_IS_GRID_LINE_VISIBLE(dim, x, y) \
+  ((dim)[(x)].grid_line_visibility[(y)/32] & (1 << ((y) % 32)))
+
 /* The children array is only kept around while doing the layout.
    It gets freed in goo_canvas_table_allocate_area(). */
 struct _GooCanvasTableLayoutData
@@ -152,6 +170,18 @@ struct _GooCanvasTableLayoutData
   /* This is the border width used, possibly rounded to an integer. */
   gdouble border_width;
 
+  /* The actual property value */
+  gdouble prop_grid_line_width[2];
+
+  /* The grid line width in both directions
+   * (possibly rounded if integer_layout is set) */
+  gdouble grid_line_width[2];
+
+  /* This is the actual spacing between the uppermost, bottommost, leftmost
+     and rightmost cells from the widget border. This is the border spacing
+     for that dimension. */
+  gdouble border_spacing[2];
+
   /* These are in the table's coordinate space. */
   gdouble natural_size[2];
   gdouble requested_size[2];
@@ -176,6 +206,7 @@ static void goo_canvas_table_set_propert
 					   const GValue       *value,
 					   GParamSpec         *pspec);
 
+static void goo_canvas_table_init_layout_data (GooCanvasTable *table);
 static void goo_canvas_table_free_layout_data (GooCanvasTableData *table_data);
 
 G_DEFINE_TYPE_WITH_CODE (GooCanvasTable, goo_canvas_table,
@@ -232,6 +263,31 @@ goo_canvas_table_install_common_properti
 							 _("If all columns are the same width"),
 							 FALSE,
 							 G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, PROP_X_BORDER_SPACING,
+                                   g_param_spec_double("x-border-spacing",
+                                                       _("X border spacing"),
+                                                       _("The amount of spacing between the lefmost and rightmost cells and the border grid line"),
+                                                       0.0, G_MAXDOUBLE, 0.0,
+                                                       G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, PROP_Y_BORDER_SPACING,
+                                   g_param_spec_double("y-border-spacing",
+                                                       _("Y border spacing"),
+                                                       _("The amount of spacing between the topmost and bottommost cells and the border grid line"),
+                                                       0.0, G_MAXDOUBLE, 0.0,
+                                                       G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, PROP_HORZ_GRID_LINE_WIDTH,
+                                   g_param_spec_double("horz-grid-line-width",
+                                                        _("Horizontal grid line width"),
+                                                        _("The width of the grid line to draw between rows"),
+                                                        0.0, G_MAXDOUBLE, 0.0,
+                                                        G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class, PROP_VERT_GRID_LINE_WIDTH,
+                                   g_param_spec_double("vert-grid-line-width",
+                                                       _("Vertical grid line width"),
+                                                       _("The width of the grid line to draw between columns"),
+                                                       0.0, G_MAXDOUBLE, 0.0,
+                                                       G_PARAM_READWRITE));
 
   /*
    * Child properties.
@@ -373,6 +429,7 @@ goo_canvas_table_init_data (GooCanvasTab
   table_data->border_width = 0.0;
 
   table_data->children = g_array_new (0, 0, sizeof (GooCanvasTableChild));
+  /*table_data->layout_data = NULL;*/
 }
 
 
@@ -381,6 +438,7 @@ static void
 goo_canvas_table_free_data (GooCanvasTableData *table_data)
 {
   gint d;
+  gint i;
 
   g_array_free (table_data->children, TRUE);
 
@@ -399,6 +457,7 @@ goo_canvas_table_init (GooCanvasTable *t
 {
   table->table_data = g_slice_new0 (GooCanvasTableData);
   goo_canvas_table_init_data (table->table_data);
+  goo_canvas_table_init_layout_data (table);
 }
 
 
@@ -525,6 +584,18 @@ goo_canvas_table_get_common_property (GO
     case PROP_HOMOGENEOUS_COLUMNS:
       g_value_set_boolean (value, table_data->dimensions[HORZ].homogeneous);
       break;
+    case PROP_X_BORDER_SPACING:
+      g_value_set_double (value, table_data->layout_data->border_spacing[HORZ]);
+      break;
+    case PROP_Y_BORDER_SPACING:
+      g_value_set_double (value, table_data->layout_data->border_spacing[VERT]);
+      break;
+    case PROP_HORZ_GRID_LINE_WIDTH:
+      g_value_set_double (value, table_data->layout_data->prop_grid_line_width[HORZ]);
+      break;
+    case PROP_VERT_GRID_LINE_WIDTH:
+      g_value_set_double (value, table_data->layout_data->prop_grid_line_width[VERT]);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -574,6 +645,18 @@ goo_canvas_table_set_common_property (GO
     case PROP_HOMOGENEOUS_COLUMNS:
       table_data->dimensions[HORZ].homogeneous = g_value_get_boolean (value);
       break;
+    case PROP_X_BORDER_SPACING:
+      table_data->layout_data->border_spacing[HORZ] = g_value_get_double (value);
+      break;
+    case PROP_Y_BORDER_SPACING:
+      table_data->layout_data->border_spacing[VERT] = g_value_get_double (value);
+      break;
+    case PROP_HORZ_GRID_LINE_WIDTH:
+      table_data->layout_data->prop_grid_line_width[HORZ] = g_value_get_double (value);
+      break;
+    case PROP_VERT_GRID_LINE_WIDTH:
+      table_data->layout_data->prop_grid_line_width[VERT] = g_value_get_double (value);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -611,25 +694,82 @@ static void
 goo_canvas_table_update_dimensions (GooCanvasTableData    *table_data,
 				    GooCanvasTableChild   *table_child)
 {
-  gint d, size, i;
+  GooCanvasTableLayoutData* layout_data;
+  gint d, size[2], i;
+  layout_data = table_data->layout_data;
+  size[0] = table_child->start[0] + table_child->size[0];
+  size[1] = table_child->start[1] + table_child->size[1];
 
   for (d = 0; d < 2; d++)
     {
-      size = table_child->start[d] + table_child->size[d];
+      if (size[d] > table_data->dimensions[d].size)
+        {
+          table_data->dimensions[d].spacings = g_realloc (table_data->dimensions[d].spacings, size[d] * sizeof (gdouble));
 
-      if (size > table_data->dimensions[d].size)
-	{
-	  table_data->dimensions[d].spacings = g_realloc (table_data->dimensions[d].spacings, size * sizeof (gdouble));
+          /* Initialize new spacings to -1.0 so the default is used. */
+          for (i = table_data->dimensions[d].size; i < size[d]; i++)
+            table_data->dimensions[d].spacings[i] = -1.0;
 
-	  /* Initialize new spacings to -1.0 so the default is used. */
-	  for (i = table_data->dimensions[d].size; i < size; i++)
-	    table_data->dimensions[d].spacings[i] = -1.0;
+          /* Update grid line visibility, stored in layout data
+             dimension for backward compatibility */
+          g_assert (size[d] > 0);
+          layout_data->dldata[d] = g_renew (GooCanvasTableDimensionLayoutData, layout_data->dldata[d], size[d]);
+          layout_data->dldata[d][size[d]-1].grid_line_visibility = NULL; /* unused */
 
-	  table_data->dimensions[d].size = size;
-	}
+          for (i = MAX (table_data->dimensions[d].size - 1, 0); i + 1 < size[d]; i++)
+            layout_data->dldata[d][i].grid_line_visibility = NULL;
+        }
     }
+
+  table_data->dimensions[0].size = MAX (size[0], table_data->dimensions[0].size);
+  table_data->dimensions[1].size = MAX (size[1], table_data->dimensions[1].size);
 }
 
+/* Sets or unsets grid line visibility for a given child */
+static void
+goo_canvas_update_grid_line_visibility(GooCanvasTableData *table_data)
+{
+  GooCanvasTableLayoutData *layout_data;
+  GooCanvasTableChild *table_child;
+  guint32 grid_line_size;
+  guint32 c;
+  gint d;
+  gint i;
+  gint j;
+
+  layout_data = table_data->layout_data;
+  for (d = 0; d < 2; d++)
+    {
+      /* The amount of guint32s we need to store grid line visibility
+       * for a particular row or column. In case of vertical grid lines
+       * we store the grid lines (right to) a particular column for each row.
+       * Therefore we need one bit for each row. This is also why this depends
+       * on dimensions[1-d] instead of dimensions[d]. */
+      grid_line_size = ((table_data->dimensions[1-d].size + 31) / 32);
+
+      /* Make all lines visible */
+      for(i = 0; i + 1 < table_data->dimensions[d].size; i++)
+      {
+        layout_data->dldata[d][i].grid_line_visibility = g_realloc(layout_data->dldata[d][i].grid_line_visibility, grid_line_size * sizeof(guint32));
+        memset(layout_data->dldata[d][i].grid_line_visibility, 0xff, grid_line_size * sizeof(guint32));
+      }
+
+      /* Remove lines for each child spanning multiple rows/colmuns */
+      for(c = 0; c < table_data->children->len; c++)
+        {
+          table_child = &g_array_index(table_data->children, GooCanvasTableChild, c);
+          /* Foreach cell the child is spanning */
+          for (i = table_child->start[d]; i < table_child->start[d] + table_child->size[d] - 1; i++)
+            {
+              /* Iterate through occupied cells in the other dimension */
+              for (j = table_child->start[1-d]; j < table_child->start[1-d] + table_child->size[1-d]; j++)
+                {
+                  GOO_CANVAS_TABLE_UNSET_GRID_LINE_VISIBILITY(layout_data->dldata[d], i, j);
+                }
+            }
+        }
+    }
+}
 
 static void
 goo_canvas_table_add_child_internal (GooCanvasTableData *table_data,
@@ -928,6 +1068,9 @@ goo_canvas_table_set_common_child_proper
     }
 
   goo_canvas_table_update_dimensions (table_data, table_child);
+
+  /* Update grid line visibility */
+  goo_canvas_update_grid_line_visibility(table_data);
 }
 
 
@@ -993,8 +1136,13 @@ goo_canvas_table_set_model    (GooCanvas
 static void
 goo_canvas_table_free_layout_data (GooCanvasTableData *table_data)
 {
+  guint i;
   if (table_data->layout_data)
     {
+      for (i = 0; i < table_data->dimensions[VERT].size; i++)
+        g_free (table_data->layout_data->dldata[VERT][i].grid_line_visibility);
+      for (i = 0; i < table_data->dimensions[HORZ].size; i++)
+        g_free (table_data->layout_data->dldata[HORZ][i].grid_line_visibility);
       g_free (table_data->layout_data->dldata[HORZ]);
       g_free (table_data->layout_data->dldata[VERT]);
       g_free (table_data->layout_data->children);
@@ -1015,26 +1163,53 @@ goo_canvas_table_init_layout_data (GooCa
   GooCanvasTableDimensionLayoutData *dldata;
   gint d, i;
 
-  /* Free any previous data. */
-  goo_canvas_table_free_layout_data (table->table_data);
-
-  layout_data = g_slice_new (GooCanvasTableLayoutData);
+  layout_data = table_data->layout_data;
+  if (layout_data == NULL)
+    {
+      layout_data = g_slice_new (GooCanvasTableLayoutData);
+      layout_data->children = NULL;
+      for (d = 0; d < 2; d++)
+        {
+          layout_data->dldata[d] = NULL;
+          layout_data->prop_grid_line_width[d] = 0.0;
+          layout_data->grid_line_width[d] = 0.0;
+          layout_data->border_spacing[d] = 0.0;
+	}
+      table_data->layout_data = layout_data;
+    }
 
-  table_data->layout_data = layout_data;
-  layout_data->children = g_new (GooCanvasTableChildLayoutData,
-				 table_data->children->len);
+  layout_data->children = g_renew (GooCanvasTableChildLayoutData,
+                                   layout_data->children,
+                                   table_data->children->len);
   layout_data->last_width = -1;
-  layout_data->integer_layout = simple->canvas->integer_layout;
+
+  /* If we are not yet added to a canvas, integer layout is irrelevant anyway.
+     We still need the layout_data for private fields, such as border spacing
+     and grid line visibility. */
+  if (simple->canvas)
+    layout_data->integer_layout = simple->canvas->integer_layout;
+  else
+    layout_data->integer_layout = FALSE;
   layout_data->border_width = table_data->border_width;
   if (layout_data->integer_layout)
     layout_data->border_width = floor (layout_data->border_width + 0.5);
 
+  layout_data->grid_line_width[0] = layout_data->prop_grid_line_width[0];
+  layout_data->grid_line_width[1] = layout_data->prop_grid_line_width[1];
+  if (layout_data->integer_layout)
+    {
+      layout_data->grid_line_width[0] = floor (layout_data->grid_line_width[0] + 0.5);
+      layout_data->grid_line_width[1] = floor (layout_data->grid_line_width[1] + 0.5);
+    }
+
   for (d = 0; d < 2; d++)
     {
       dimension = &table_data->dimensions[d];
 
-      layout_data->dldata[d] = g_new (GooCanvasTableDimensionLayoutData,
-				      dimension->size);
+      /* Already allocated in goo_canvas_table_update_dimensions() */
+      /*layout_data->dldata[d] = g_renew (GooCanvasTableDimensionLayoutData,
+                                        layout_data->dldata[d],
+				        dimension->size);*/
       dldata = layout_data->dldata[d];
 
       for (i = 0; i < dimension->size; i++)
@@ -1045,6 +1220,9 @@ goo_canvas_table_init_layout_data (GooCa
 	  else
 	    dldata[i].spacing = dimension->default_spacing;
 
+          /* Add grid line widths to spacing */
+          dldata[i].spacing += layout_data->prop_grid_line_width[1-d];
+
 	  /* In integer layout mode, round spacings to the nearest integer. */
 	  if (layout_data->integer_layout)
 	    dldata[i].spacing = floor (dldata[i].spacing + 0.5);
@@ -1402,8 +1580,6 @@ goo_canvas_table_size_allocate_pass1 (Go
   dimension = &table_data->dimensions[d];
   dldata = layout_data->dldata[d];
 
-  total_size = layout_data->allocated_size[d] - layout_data->border_width * 2.0;
-
   natural_size = 0;
   nexpand = 0;
   nshrink = 0;
@@ -1418,7 +1594,22 @@ goo_canvas_table_size_allocate_pass1 (Go
     }
   for (i = 0; i + 1 < dimension->size; i++)
     natural_size += dldata[i].spacing;
-      
+
+  /* Note: natural_size does not contain without border width and
+     border spacing here. */
+
+  /* total_size is the size available for allocating widgets. We always
+     allocate space for border_width, but for right/bottom border_spacing only
+     if all children can be allocated without being shrunk. */
+  if (layout_data->allocated_size[d] < layout_data->border_width * 2.0 + layout_data->border_spacing[d] + layout_data->grid_line_width[1-d])
+    total_size = 0;
+  else if (layout_data->allocated_size[d] < layout_data->border_width * 2.0 + layout_data->border_spacing[d] + layout_data->grid_line_width[1-d] + natural_size)
+    total_size = layout_data->allocated_size[d] - layout_data->border_width * 2.0 - layout_data->border_spacing[d] - layout_data->grid_line_width[1-d];
+  else if (layout_data->allocated_size[d] < layout_data->border_width * 2.0 + (layout_data->border_spacing[d] + layout_data->grid_line_width[1-d]) * 2.0 + natural_size)
+    total_size = natural_size;
+  else
+    total_size = layout_data->allocated_size[d] - layout_data->border_width * 2.0 - (layout_data->border_spacing[d] + layout_data->grid_line_width[1-d])* 2.0;
+
   if (dimension->homogeneous)
     {
       /* If the table is homogeneous in this dimension we check if any of
@@ -1535,7 +1726,7 @@ goo_canvas_table_size_allocate_pass2 (Go
   dimension = &table_data->dimensions[d];
   dldata = layout_data->dldata[d];
 
-  pos = layout_data->border_width;
+  pos = layout_data->border_width + layout_data->border_spacing[d] + layout_data->grid_line_width[1-d];
   for (i = 0; i < dimension->size; i++)
     {
       dldata[i].start = pos;
@@ -1724,7 +1915,7 @@ goo_canvas_table_update_requested_height
       if (row < end)
 	height += rows[row].spacing;
     }
-  height += layout_data->border_width * 2.0;
+  height += (layout_data->border_width + layout_data->border_spacing[VERT] + layout_data->grid_line_width[HORZ]) * 2.0;
 
   layout_data->natural_size[VERT] = height;
 }
@@ -1783,7 +1974,7 @@ goo_canvas_table_get_requested_area (Goo
       if (column < end)
 	width += columns[column].spacing;
     }
-  width += layout_data->border_width * 2.0;
+  width += (layout_data->border_width + layout_data->border_spacing[HORZ] + layout_data->grid_line_width[VERT]) * 2.0;
   
   /* Save the natural size, so we know if we have to clip children. */
   layout_data->natural_size[HORZ] = width;
@@ -1810,7 +2001,7 @@ goo_canvas_table_get_requested_area (Goo
       if (row < end)
 	height += rows[row].spacing;
     }
-  height += layout_data->border_width * 2.0;
+  height += (layout_data->border_width + layout_data->border_spacing[VERT] + layout_data->grid_line_width[HORZ]) * 2.0;
 
   /* Save the natural size, so we know if we have to clip children. */
   layout_data->natural_size[VERT] = height;
@@ -2010,12 +2201,17 @@ goo_canvas_table_paint (GooCanvasItem   
   GooCanvasTableLayoutData *layout_data = table_data->layout_data;
   GooCanvasTableDimensionLayoutData *rows = layout_data->dldata[VERT];
   GooCanvasTableDimensionLayoutData *columns = layout_data->dldata[HORZ];
+  gdouble vert_grid_line_width = layout_data->grid_line_width[VERT];
+  gdouble horz_grid_line_width = layout_data->grid_line_width[HORZ];
   GArray *children = table_data->children;
   GooCanvasTableChild *table_child;
   GooCanvasItem *child;
   gboolean check_clip = FALSE, clip;
   gint start_column, end_column, start_row, end_row, i, j;
   gdouble x, y, end_x, end_y;
+  gdouble frame_width, frame_height;
+  gdouble line_start, line_end, half_spacing_before, half_spacing_after;
+  gboolean old_grid_line_visibility, cur_grid_line_visibility;
 
   /* Skip the item if the bounds don't intersect the expose rectangle. */
   if (simple->bounds.x1 > bounds->x2 || simple->bounds.x2 < bounds->x1
@@ -2047,6 +2243,165 @@ goo_canvas_table_paint (GooCanvasItem   
       || layout_data->allocated_size[VERT] < layout_data->natural_size[VERT])
     check_clip = TRUE;
 
+  /* frame_width/frame_height is the size of the table we draw the grid lines
+     around. This normally is the allocated size, except when we are shrunk
+     in which case we use the natural size. The grid will be clipped in that
+     case. */
+  frame_width = MAX (layout_data->allocated_size[HORZ], layout_data->natural_size[HORZ]);
+  frame_height = MAX (layout_data->allocated_size[VERT], layout_data->natural_size[VERT]);
+
+  /* Save current line width, line cap etc. for drawing items after having
+     drawn grid lines */
+  cairo_save (cr);
+
+  /* Draw border and grid lines */
+  if (check_clip)
+    {
+      cairo_rectangle (cr,
+                       layout_data->border_width,
+                       layout_data->border_width,
+                       layout_data->allocated_size[HORZ] - 2*layout_data->border_width,
+                       layout_data->allocated_size[VERT] - 2*layout_data->border_width);
+      cairo_clip (cr);
+    }
+
+  cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+
+  /* Horizontal grid lines */
+  if (horz_grid_line_width > 0.0)
+    {
+      cairo_set_line_width (cr, horz_grid_line_width);
+
+      /* Outer lines */
+      line_start = layout_data->border_width;
+      line_end = frame_width - layout_data->border_width;
+
+      cairo_move_to (cr, line_start, layout_data->border_width + horz_grid_line_width/2);
+      cairo_rel_line_to (cr, line_end - line_start, 0);
+
+      cairo_move_to (cr, line_start, frame_height - layout_data->border_width - horz_grid_line_width/2);
+      cairo_rel_line_to (cr, line_end - line_start, 0);
+
+      /* Inner lines. Make sure we don't do overlapping drawing operations,
+         so we could easily draw alpha transparent borders */
+      for (i = 0; i + 1 < table_data->dimensions[VERT].size; i++)
+        {
+          for (j = 0; j < table_data->dimensions[HORZ].size; j++)
+            {
+              cur_grid_line_visibility = GOO_CANVAS_TABLE_IS_GRID_LINE_VISIBLE(layout_data->dldata[VERT], i, j);
+              if (cur_grid_line_visibility)
+                {
+                  half_spacing_before = half_spacing_after = ((columns[j-1].spacing - vert_grid_line_width)/2.0);
+                  if (simple->canvas->integer_layout)
+                    {
+                      half_spacing_before = floor (half_spacing_before);
+                      half_spacing_after = ceil (half_spacing_after);
+                    }
+
+                  if (j == 0)
+                    line_start = layout_data->border_width + vert_grid_line_width;
+                  else
+                    if (old_grid_line_visibility)
+                      line_start = columns[j].start - half_spacing_after;
+                    else
+                      line_start = columns[j-1].end + half_spacing_before;
+
+                  half_spacing_after = (columns[j].spacing - vert_grid_line_width)/2.0;
+                  if (simple->canvas->integer_layout)
+                    half_spacing_after = ceil (half_spacing_after);
+
+                  if (j == table_data->dimensions[HORZ].size - 1)
+                    line_end = frame_width - layout_data->border_width - vert_grid_line_width;
+                  else
+                    line_end = columns[j + 1].start - half_spacing_after;
+
+                  cairo_move_to (cr, line_start, rows[i].end + rows[i].spacing/2.0);
+                  cairo_rel_line_to (cr, line_end - line_start, 0);
+                }
+
+              old_grid_line_visibility = cur_grid_line_visibility;
+            }
+        }
+      
+      cairo_stroke (cr);
+    }
+
+  /* Vertical grid lines */
+  if (vert_grid_line_width > 0.0)
+    {
+      cairo_set_line_width (cr, vert_grid_line_width);
+
+      /* Outer lines */
+      line_start = layout_data->border_width + horz_grid_line_width;
+      line_end = frame_height - layout_data->border_width - horz_grid_line_width;
+
+      cairo_move_to (cr, layout_data->border_width + vert_grid_line_width/2, line_start);
+      cairo_rel_line_to (cr, 0, line_end - line_start);
+
+      cairo_move_to (cr, frame_width - layout_data->border_width - vert_grid_line_width/2, line_start);
+      cairo_rel_line_to (cr, 0, line_end - line_start);
+
+      /* Inner lines. Make sure we don't do overlapping drawing operations,
+         so we could easily draw alpha transparent borders. We need to
+         take additionally care that we don't cross already drawn
+         horizontal lines. */
+      for (i = 0; i + 1 < table_data->dimensions[HORZ].size; i++)
+        {
+          for (j = 0; j < table_data->dimensions[VERT].size; j++)
+            {
+              cur_grid_line_visibility = GOO_CANVAS_TABLE_IS_GRID_LINE_VISIBLE(layout_data->dldata[HORZ], i, j);
+              if (cur_grid_line_visibility)
+                {
+                  half_spacing_before = half_spacing_after = (rows[j-1].spacing - horz_grid_line_width)/2.0;
+                  if (simple->canvas->integer_layout)
+                    {
+                      half_spacing_before = floor (half_spacing_before);
+                      half_spacing_after = ceil (half_spacing_after);
+                    }
+
+                  if (j == 0)
+                    line_start = layout_data->border_width + horz_grid_line_width;
+                  else
+                    if (old_grid_line_visibility)
+                      line_start = rows[j].start - half_spacing_after;
+                    else
+                      /* Don't draw top part if already drawn by horizontal grid line */
+                      if (GOO_CANVAS_TABLE_IS_GRID_LINE_VISIBLE(layout_data->dldata[VERT], j-1, i)
+                          || GOO_CANVAS_TABLE_IS_GRID_LINE_VISIBLE(layout_data->dldata[VERT], j-1, i+1))
+                        line_start = rows[j].start - half_spacing_after;
+                      else
+                        line_start = rows[j-1].end + half_spacing_before;
+
+                  half_spacing_before = half_spacing_after = (rows[j].spacing - horz_grid_line_width)/2.0;
+                  if (simple->canvas->integer_layout)
+                    {
+                      half_spacing_before = floor (half_spacing_before);
+                      half_spacing_after = ceil (half_spacing_after);
+                    }
+
+                  if (j == table_data->dimensions[VERT].size - 1)
+                    line_end = frame_height - layout_data->border_width - horz_grid_line_width;
+                  else
+                    /* Don't draw bottom part if already drawn by horizontal grid line */
+                    if (GOO_CANVAS_TABLE_IS_GRID_LINE_VISIBLE(layout_data->dldata[VERT], j, i)
+                        || GOO_CANVAS_TABLE_IS_GRID_LINE_VISIBLE(layout_data->dldata[VERT], j, i+1))
+                      line_end = rows[j].end + half_spacing_before;
+                    else
+                      line_end = rows[j + 1].start - half_spacing_after;
+
+                  cairo_move_to (cr, columns[i].end + columns[i].spacing/2.0, line_start);
+                  cairo_rel_line_to (cr, 0, line_end - line_start);
+                }
+
+              old_grid_line_visibility = cur_grid_line_visibility;
+            }
+        }
+
+      cairo_stroke (cr);
+    }
+
+  cairo_restore (cr);
+
   for (i = 0; i < group->items->len; i++)
     {
       child = group->items->pdata[i];
@@ -2368,7 +2723,19 @@ goo_canvas_table_model_class_init (GooCa
 static void
 goo_canvas_table_model_init (GooCanvasTableModel *tmodel)
 {
+  gint d;
   goo_canvas_table_init_data (&tmodel->table_data);
+
+  /* Allocate layout data which is used to store some private fields
+     due to backwards compatibility. */
+  tmodel->table_data.layout_data = g_slice_new(GooCanvasTableLayoutData);
+  tmodel->table_data.layout_data->children = NULL;
+  for (d = 0; d < 2; d++)
+    {
+      tmodel->table_data.layout_data->dldata[d] = NULL;
+      tmodel->table_data.layout_data->grid_line_width[d] = 0.0;
+      tmodel->table_data.layout_data->border_spacing[d] = 0.0;
+    }
 }
 
 

