]> Pileus Git - ~andy/gtk/blob - gtk/gtktable.c
Don't use this same functions as idle callbacks and directly, to avoid
[~andy/gtk] / gtk / gtktable.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "gtktable.h"
28 #include "gtkintl.h"
29
30 enum
31 {
32   PROP_0,
33   PROP_N_ROWS,
34   PROP_N_COLUMNS,
35   PROP_COLUMN_SPACING,
36   PROP_ROW_SPACING,
37   PROP_HOMOGENEOUS
38 };
39
40 enum
41 {
42   CHILD_PROP_0,
43   CHILD_PROP_LEFT_ATTACH,
44   CHILD_PROP_RIGHT_ATTACH,
45   CHILD_PROP_TOP_ATTACH,
46   CHILD_PROP_BOTTOM_ATTACH,
47   CHILD_PROP_X_OPTIONS,
48   CHILD_PROP_Y_OPTIONS,
49   CHILD_PROP_X_PADDING,
50   CHILD_PROP_Y_PADDING
51 };
52   
53
54 static void gtk_table_class_init    (GtkTableClass  *klass);
55 static void gtk_table_init          (GtkTable       *table);
56 static void gtk_table_finalize      (GObject        *object);
57 static void gtk_table_size_request  (GtkWidget      *widget,
58                                      GtkRequisition *requisition);
59 static void gtk_table_size_allocate (GtkWidget      *widget,
60                                      GtkAllocation  *allocation);
61 static void gtk_table_add           (GtkContainer   *container,
62                                      GtkWidget      *widget);
63 static void gtk_table_remove        (GtkContainer   *container,
64                                      GtkWidget      *widget);
65 static void gtk_table_forall        (GtkContainer   *container,
66                                      gboolean        include_internals,
67                                      GtkCallback     callback,
68                                      gpointer        callback_data);
69 static void gtk_table_get_property  (GObject         *object,
70                                      guint            prop_id,
71                                      GValue          *value,
72                                      GParamSpec      *pspec);
73 static void gtk_table_set_property  (GObject         *object,
74                                      guint            prop_id,
75                                      const GValue    *value,
76                                      GParamSpec      *pspec);
77 static void gtk_table_set_child_property (GtkContainer    *container,
78                                           GtkWidget       *child,
79                                           guint            property_id,
80                                           const GValue    *value,
81                                           GParamSpec      *pspec);
82 static void gtk_table_get_child_property (GtkContainer    *container,
83                                           GtkWidget       *child,
84                                           guint            property_id,
85                                           GValue          *value,
86                                           GParamSpec      *pspec);
87 static GtkType gtk_table_child_type (GtkContainer   *container);
88
89
90 static void gtk_table_size_request_init  (GtkTable *table);
91 static void gtk_table_size_request_pass1 (GtkTable *table);
92 static void gtk_table_size_request_pass2 (GtkTable *table);
93 static void gtk_table_size_request_pass3 (GtkTable *table);
94
95 static void gtk_table_size_allocate_init  (GtkTable *table);
96 static void gtk_table_size_allocate_pass1 (GtkTable *table);
97 static void gtk_table_size_allocate_pass2 (GtkTable *table);
98
99
100 static GtkContainerClass *parent_class = NULL;
101
102
103 GtkType
104 gtk_table_get_type (void)
105 {
106   static GtkType table_type = 0;
107   
108   if (!table_type)
109     {
110       static const GtkTypeInfo table_info =
111       {
112         "GtkTable",
113         sizeof (GtkTable),
114         sizeof (GtkTableClass),
115         (GtkClassInitFunc) gtk_table_class_init,
116         (GtkObjectInitFunc) gtk_table_init,
117         /* reserved_1 */ NULL,
118         /* reserved_2 */ NULL,
119         (GtkClassInitFunc) NULL,
120       };
121       
122       table_type = gtk_type_unique (gtk_container_get_type (), &table_info);
123     }
124   
125   return table_type;
126 }
127
128 static void
129 gtk_table_class_init (GtkTableClass *class)
130 {
131   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
132   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
133   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
134   
135   parent_class = g_type_class_peek_parent (class);
136
137   gobject_class->finalize = gtk_table_finalize;
138
139   gobject_class->get_property = gtk_table_get_property;
140   gobject_class->set_property = gtk_table_set_property;
141   
142   widget_class->size_request = gtk_table_size_request;
143   widget_class->size_allocate = gtk_table_size_allocate;
144   
145   container_class->add = gtk_table_add;
146   container_class->remove = gtk_table_remove;
147   container_class->forall = gtk_table_forall;
148   container_class->child_type = gtk_table_child_type;
149   container_class->set_child_property = gtk_table_set_child_property;
150   container_class->get_child_property = gtk_table_get_child_property;
151   
152
153   g_object_class_install_property (gobject_class,
154                                    PROP_N_ROWS,
155                                    g_param_spec_uint ("n_rows",
156                                                      _("Rows"),
157                                                      _("The number of rows in the table"),
158                                                      0,
159                                                      G_MAXUINT,
160                                                      0,
161                                                      G_PARAM_READWRITE));
162   g_object_class_install_property (gobject_class,
163                                    PROP_N_COLUMNS,
164                                    g_param_spec_uint ("n_columns",
165                                                      _("Columns"),
166                                                      _("The number of columns in the table"),
167                                                      0,
168                                                      G_MAXUINT,
169                                                      0,
170                                                      G_PARAM_READWRITE));
171   g_object_class_install_property (gobject_class,
172                                    PROP_ROW_SPACING,
173                                    g_param_spec_uint ("row_spacing",
174                                                      _("Row spacing"),
175                                                      _("The amount of space between two consecutive rows"),
176                                                      0,
177                                                      G_MAXUINT,
178                                                      0,
179                                                      G_PARAM_READWRITE));
180   g_object_class_install_property (gobject_class,
181                                    PROP_COLUMN_SPACING,
182                                    g_param_spec_uint ("column_spacing",
183                                                      _("Column spacing"),
184                                                      _("The amount of space between two consecutive columns"),
185                                                      0,
186                                                      G_MAXUINT,
187                                                      0,
188                                                      G_PARAM_READWRITE));
189   g_object_class_install_property (gobject_class,
190                                    PROP_HOMOGENEOUS,
191                                    g_param_spec_boolean ("homogeneous",
192                                                          _("Homogenous"),
193                                                          _("If TRUE this means the table cells are all the same width/height"),
194                                                          FALSE,
195                                                          G_PARAM_READWRITE));
196
197   gtk_container_class_install_child_property (container_class,
198                                               CHILD_PROP_LEFT_ATTACH,
199                                               g_param_spec_uint ("left_attach", NULL, NULL,
200                                                                  0, 65535, 0,
201                                                                  G_PARAM_READWRITE));
202   gtk_container_class_install_child_property (container_class,
203                                               CHILD_PROP_RIGHT_ATTACH,
204                                               g_param_spec_uint ("right_attach", NULL, NULL,
205                                                                  1, 65535, 1,
206                                                                  G_PARAM_READWRITE));
207   gtk_container_class_install_child_property (container_class,
208                                               CHILD_PROP_TOP_ATTACH,
209                                               g_param_spec_uint ("top_attach", NULL, NULL,
210                                                                  0, 65535, 0,
211                                                                  G_PARAM_READWRITE));
212   gtk_container_class_install_child_property (container_class,
213                                               CHILD_PROP_BOTTOM_ATTACH,
214                                               g_param_spec_uint ("bottom_attach", NULL, NULL,
215                                                                  1, 65535, 1,
216                                                                  G_PARAM_READWRITE));
217   gtk_container_class_install_child_property (container_class,
218                                               CHILD_PROP_X_OPTIONS,
219                                               g_param_spec_flags ("x_options", NULL, NULL,
220                                                                   GTK_TYPE_ATTACH_OPTIONS, GTK_EXPAND | GTK_FILL,
221                                                                   G_PARAM_READWRITE));
222   gtk_container_class_install_child_property (container_class,
223                                               CHILD_PROP_Y_OPTIONS,
224                                               g_param_spec_flags ("y_options", NULL, NULL,
225                                                                   GTK_TYPE_ATTACH_OPTIONS, GTK_EXPAND | GTK_FILL,
226                                                                   G_PARAM_READWRITE));
227   gtk_container_class_install_child_property (container_class,
228                                               CHILD_PROP_X_PADDING,
229                                               g_param_spec_uint ("x_padding", NULL, NULL,
230                                                                  0, 65535, 0,
231                                                                  G_PARAM_READWRITE));
232   gtk_container_class_install_child_property (container_class,
233                                               CHILD_PROP_Y_PADDING,
234                                               g_param_spec_uint ("y_padding", NULL, NULL,
235                                                                  0, 65535, 0,
236                                                                  G_PARAM_READWRITE));
237 }
238
239 static GtkType
240 gtk_table_child_type (GtkContainer   *container)
241 {
242   return GTK_TYPE_WIDGET;
243 }
244
245 static void
246 gtk_table_get_property (GObject      *object,
247                         guint         prop_id,
248                         GValue       *value,
249                         GParamSpec   *pspec)
250 {
251   GtkTable *table;
252
253   table = GTK_TABLE (object);
254
255   switch (prop_id)
256     {
257     case PROP_N_ROWS:
258       g_value_set_uint (value, table->nrows);
259       break;
260     case PROP_N_COLUMNS:
261       g_value_set_uint (value, table->ncols);
262       break;
263     case PROP_ROW_SPACING:
264       g_value_set_uint (value, table->row_spacing);
265       break;
266     case PROP_COLUMN_SPACING:
267       g_value_set_uint (value, table->column_spacing);
268       break;
269     case PROP_HOMOGENEOUS:
270       g_value_set_boolean (value, table->homogeneous);
271       break;
272     default:
273       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
274       break;
275     }
276 }
277
278 static void
279 gtk_table_set_property (GObject      *object,
280                         guint         prop_id,
281                         const GValue *value,
282                         GParamSpec   *pspec)
283 {
284   GtkTable *table;
285
286   table = GTK_TABLE (object);
287
288   switch (prop_id)
289     {
290     case PROP_N_ROWS:
291       gtk_table_resize (table, g_value_get_uint (value), table->ncols);
292       break;
293     case PROP_N_COLUMNS:
294       gtk_table_resize (table, table->nrows, g_value_get_uint (value));
295       break;
296     case PROP_ROW_SPACING:
297       gtk_table_set_row_spacings (table, g_value_get_uint (value));
298       break;
299     case PROP_COLUMN_SPACING:
300       gtk_table_set_col_spacings (table, g_value_get_uint (value));
301       break;
302     case PROP_HOMOGENEOUS:
303       gtk_table_set_homogeneous (table, g_value_get_boolean (value));
304       break;
305     default:
306       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
307       break;
308     }
309 }
310
311 static void
312 gtk_table_set_child_property (GtkContainer    *container,
313                               GtkWidget       *child,
314                               guint            property_id,
315                               const GValue    *value,
316                               GParamSpec      *pspec)
317 {
318   GtkTable *table = GTK_TABLE (container);
319   GtkTableChild *table_child;
320   GList *list;
321
322   table_child = NULL;
323   for (list = table->children; list; list = list->next)
324     {
325       table_child = list->data;
326
327       if (table_child->widget == child)
328         break;
329     }
330   if (!list)
331     {
332       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
333       return;
334     }
335
336   switch (property_id)
337     {
338     case CHILD_PROP_LEFT_ATTACH:
339       table_child->left_attach = g_value_get_uint (value);
340       if (table_child->right_attach <= table_child->left_attach)
341         table_child->right_attach = table_child->left_attach + 1;
342       if (table_child->right_attach >= table->ncols)
343         gtk_table_resize (table, table->nrows, table_child->right_attach);
344       break;
345     case CHILD_PROP_RIGHT_ATTACH:
346       table_child->right_attach = g_value_get_uint (value);
347       if (table_child->right_attach <= table_child->left_attach)
348         table_child->left_attach = table_child->right_attach - 1;
349       if (table_child->right_attach >= table->ncols)
350         gtk_table_resize (table, table->nrows, table_child->right_attach);
351       break;
352     case CHILD_PROP_TOP_ATTACH:
353       table_child->top_attach = g_value_get_uint (value);
354       if (table_child->bottom_attach <= table_child->top_attach)
355         table_child->bottom_attach = table_child->top_attach + 1;
356       if (table_child->bottom_attach >= table->nrows)
357         gtk_table_resize (table, table_child->bottom_attach, table->ncols);
358       break;
359     case CHILD_PROP_BOTTOM_ATTACH:
360       table_child->bottom_attach = g_value_get_uint (value);
361       if (table_child->bottom_attach <= table_child->top_attach)
362         table_child->top_attach = table_child->bottom_attach - 1;
363       if (table_child->bottom_attach >= table->nrows)
364         gtk_table_resize (table, table_child->bottom_attach, table->ncols);
365       break;
366     case CHILD_PROP_X_OPTIONS:
367       table_child->xexpand = (g_value_get_flags (value) & GTK_EXPAND) != 0;
368       table_child->xshrink = (g_value_get_flags (value) & GTK_SHRINK) != 0;
369       table_child->xfill = (g_value_get_flags (value) & GTK_FILL) != 0;
370       break;
371     case CHILD_PROP_Y_OPTIONS:
372       table_child->yexpand = (g_value_get_flags (value) & GTK_EXPAND) != 0;
373       table_child->yshrink = (g_value_get_flags (value) & GTK_SHRINK) != 0;
374       table_child->yfill = (g_value_get_flags (value) & GTK_FILL) != 0;
375       break;
376     case CHILD_PROP_X_PADDING:
377       table_child->xpadding = g_value_get_uint (value);
378       break;
379     case CHILD_PROP_Y_PADDING:
380       table_child->ypadding = g_value_get_uint (value);
381       break;
382     default:
383       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
384       break;
385     }
386   if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (table))
387     gtk_widget_queue_resize (child);
388 }
389
390 static void
391 gtk_table_get_child_property (GtkContainer    *container,
392                               GtkWidget       *child,
393                               guint            property_id,
394                               GValue          *value,
395                               GParamSpec      *pspec)
396 {
397   GtkTable *table = GTK_TABLE (container);
398   GtkTableChild *table_child;
399   GList *list;
400
401   table_child = NULL;
402   for (list = table->children; list; list = list->next)
403     {
404       table_child = list->data;
405
406       if (table_child->widget == child)
407         break;
408     }
409   if (!list)
410     {
411       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
412       return;
413     }
414
415   switch (property_id)
416     {
417     case CHILD_PROP_LEFT_ATTACH:
418       g_value_set_uint (value, table_child->left_attach);
419       break;
420     case CHILD_PROP_RIGHT_ATTACH:
421       g_value_set_uint (value, table_child->right_attach);
422       break;
423     case CHILD_PROP_TOP_ATTACH:
424       g_value_set_uint (value, table_child->top_attach);
425       break;
426     case CHILD_PROP_BOTTOM_ATTACH:
427       g_value_set_uint (value, table_child->bottom_attach);
428       break;
429     case CHILD_PROP_X_OPTIONS:
430       g_value_set_flags (value, (table_child->xexpand * GTK_EXPAND |
431                                  table_child->xshrink * GTK_SHRINK |
432                                  table_child->xfill * GTK_FILL));
433       break;
434     case CHILD_PROP_Y_OPTIONS:
435       g_value_set_flags (value, (table_child->yexpand * GTK_EXPAND |
436                                  table_child->yshrink * GTK_SHRINK |
437                                  table_child->yfill * GTK_FILL));
438       break;
439     case CHILD_PROP_X_PADDING:
440       g_value_set_uint (value, table_child->xpadding);
441       break;
442     case CHILD_PROP_Y_PADDING:
443       g_value_set_uint (value, table_child->ypadding);
444       break;
445     default:
446       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
447       break;
448     }
449 }
450
451 static void
452 gtk_table_init (GtkTable *table)
453 {
454   GTK_WIDGET_SET_FLAGS (table, GTK_NO_WINDOW);
455   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (table), FALSE);
456   
457   table->children = NULL;
458   table->rows = NULL;
459   table->cols = NULL;
460   table->nrows = 0;
461   table->ncols = 0;
462   table->column_spacing = 0;
463   table->row_spacing = 0;
464   table->homogeneous = FALSE;
465
466   gtk_table_resize (table, 1, 1);
467 }
468
469 GtkWidget*
470 gtk_table_new (guint    rows,
471                guint    columns,
472                gboolean homogeneous)
473 {
474   GtkTable *table;
475
476   if (rows == 0)
477           rows = 1;
478   if (columns == 0)
479           columns = 1;
480   
481   table = gtk_type_new (gtk_table_get_type ());
482   
483   table->homogeneous = (homogeneous ? TRUE : FALSE);
484
485   gtk_table_resize (table, rows, columns);
486   
487   return GTK_WIDGET (table);
488 }
489
490 void
491 gtk_table_resize (GtkTable *table,
492                   guint     n_rows,
493                   guint     n_cols)
494 {
495   g_return_if_fail (GTK_IS_TABLE (table));
496   g_return_if_fail (n_rows > 0 && n_rows < 65536);
497   g_return_if_fail (n_rows > 0 && n_cols < 65536);
498
499   n_rows = MAX (n_rows, 1);
500   n_cols = MAX (n_cols, 1);
501
502   if (n_rows != table->nrows ||
503       n_cols != table->ncols)
504     {
505       GList *list;
506       
507       for (list = table->children; list; list = list->next)
508         {
509           GtkTableChild *child;
510           
511           child = list->data;
512           
513           n_rows = MAX (n_rows, child->bottom_attach);
514           n_cols = MAX (n_cols, child->right_attach);
515         }
516       
517       if (n_rows != table->nrows)
518         {
519           guint i;
520
521           i = table->nrows;
522           table->nrows = n_rows;
523           table->rows = g_realloc (table->rows, table->nrows * sizeof (GtkTableRowCol));
524           
525           for (; i < table->nrows; i++)
526             {
527               table->rows[i].requisition = 0;
528               table->rows[i].allocation = 0;
529               table->rows[i].spacing = table->row_spacing;
530               table->rows[i].need_expand = 0;
531               table->rows[i].need_shrink = 0;
532               table->rows[i].expand = 0;
533               table->rows[i].shrink = 0;
534             }
535
536           g_object_notify (G_OBJECT (table), "n_rows");
537         }
538
539       if (n_cols != table->ncols)
540         {
541           guint i;
542
543           i = table->ncols;
544           table->ncols = n_cols;
545           table->cols = g_realloc (table->cols, table->ncols * sizeof (GtkTableRowCol));
546           
547           for (; i < table->ncols; i++)
548             {
549               table->cols[i].requisition = 0;
550               table->cols[i].allocation = 0;
551               table->cols[i].spacing = table->column_spacing;
552               table->cols[i].need_expand = 0;
553               table->cols[i].need_shrink = 0;
554               table->cols[i].expand = 0;
555               table->cols[i].shrink = 0;
556             }
557
558           g_object_notify (G_OBJECT (table), "n_columns");
559         }
560     }
561 }
562
563 void
564 gtk_table_attach (GtkTable        *table,
565                   GtkWidget       *child,
566                   guint            left_attach,
567                   guint            right_attach,
568                   guint            top_attach,
569                   guint            bottom_attach,
570                   GtkAttachOptions xoptions,
571                   GtkAttachOptions yoptions,
572                   guint            xpadding,
573                   guint            ypadding)
574 {
575   GtkTableChild *table_child;
576   
577   g_return_if_fail (GTK_IS_TABLE (table));
578   g_return_if_fail (GTK_IS_WIDGET (child));
579   g_return_if_fail (child->parent == NULL);
580   
581   /* g_return_if_fail (left_attach >= 0); */
582   g_return_if_fail (left_attach < right_attach);
583   /* g_return_if_fail (top_attach >= 0); */
584   g_return_if_fail (top_attach < bottom_attach);
585   
586   if (right_attach >= table->ncols)
587     gtk_table_resize (table, table->nrows, right_attach);
588   
589   if (bottom_attach >= table->nrows)
590     gtk_table_resize (table, bottom_attach, table->ncols);
591   
592   table_child = g_new (GtkTableChild, 1);
593   table_child->widget = child;
594   table_child->left_attach = left_attach;
595   table_child->right_attach = right_attach;
596   table_child->top_attach = top_attach;
597   table_child->bottom_attach = bottom_attach;
598   table_child->xexpand = (xoptions & GTK_EXPAND) != 0;
599   table_child->xshrink = (xoptions & GTK_SHRINK) != 0;
600   table_child->xfill = (xoptions & GTK_FILL) != 0;
601   table_child->xpadding = xpadding;
602   table_child->yexpand = (yoptions & GTK_EXPAND) != 0;
603   table_child->yshrink = (yoptions & GTK_SHRINK) != 0;
604   table_child->yfill = (yoptions & GTK_FILL) != 0;
605   table_child->ypadding = ypadding;
606   
607   table->children = g_list_prepend (table->children, table_child);
608   
609   gtk_widget_set_parent (child, GTK_WIDGET (table));
610 }
611
612 void
613 gtk_table_attach_defaults (GtkTable  *table,
614                            GtkWidget *widget,
615                            guint      left_attach,
616                            guint      right_attach,
617                            guint      top_attach,
618                            guint      bottom_attach)
619 {
620   gtk_table_attach (table, widget,
621                     left_attach, right_attach,
622                     top_attach, bottom_attach,
623                     GTK_EXPAND | GTK_FILL,
624                     GTK_EXPAND | GTK_FILL,
625                     0, 0);
626 }
627
628 void
629 gtk_table_set_row_spacing (GtkTable *table,
630                            guint     row,
631                            guint     spacing)
632 {
633   g_return_if_fail (GTK_IS_TABLE (table));
634   g_return_if_fail (row < table->nrows);
635   
636   if (table->rows[row].spacing != spacing)
637     {
638       table->rows[row].spacing = spacing;
639       
640       if (GTK_WIDGET_VISIBLE (table))
641         gtk_widget_queue_resize (GTK_WIDGET (table));
642     }
643 }
644
645 /**
646  * gtk_table_get_row_spacing:
647  * @table: a #GtkTable
648  * @row: a row in the table, 0 indicates the first row
649  *
650  * Gets the amount of space between row @row, and
651  * row @row + 1. See gtk_table_set_row_spacing().
652  *
653  * Return value: the row spacing
654  **/
655 guint
656 gtk_table_get_row_spacing (GtkTable *table,
657                            guint     row)
658 {
659   g_return_val_if_fail (GTK_IS_TABLE (table), 0);
660   g_return_val_if_fail (row < table->nrows - 1, 0);
661  
662   return table->rows[row].spacing;
663 }
664
665 void
666 gtk_table_set_col_spacing (GtkTable *table,
667                            guint     column,
668                            guint     spacing)
669 {
670   g_return_if_fail (GTK_IS_TABLE (table));
671   g_return_if_fail (column < table->ncols);
672   
673   if (table->cols[column].spacing != spacing)
674     {
675       table->cols[column].spacing = spacing;
676       
677       if (GTK_WIDGET_VISIBLE (table))
678         gtk_widget_queue_resize (GTK_WIDGET (table));
679     }
680 }
681
682 /**
683  * gtk_table_get_col_spacing:
684  * @table: a #GtkTable
685  * @column: a column in the table, 0 indicates the first column
686  *
687  * Gets the amount of space between column @col, and
688  * column @col + 1. See gtk_table_set_col_spacing().
689  *
690  * Return value: the column spacing
691  **/
692 guint
693 gtk_table_get_col_spacing (GtkTable *table,
694                            guint     column)
695 {
696   g_return_val_if_fail (GTK_IS_TABLE (table), 0);
697   g_return_val_if_fail (column < table->ncols, 0);
698
699   return table->cols[column].spacing;
700 }
701
702 void
703 gtk_table_set_row_spacings (GtkTable *table,
704                             guint     spacing)
705 {
706   guint row;
707   
708   g_return_if_fail (GTK_IS_TABLE (table));
709   
710   table->row_spacing = spacing;
711   for (row = 0; row < table->nrows; row++)
712     table->rows[row].spacing = spacing;
713   
714   if (GTK_WIDGET_VISIBLE (table))
715     gtk_widget_queue_resize (GTK_WIDGET (table));
716
717   g_object_notify (G_OBJECT (table), "row_spacing");
718 }
719
720 /**
721  * gtk_table_get_default_row_spacing:
722  * @table: a #GtkTable
723  *
724  * Gets the default row spacing for the table. This is
725  * the spacing that will be used for newly added rows.
726  * (See gtk_table_set_row_spacings())
727  *
728  * Returns value: the default row spacing
729  **/
730 guint
731 gtk_table_get_default_row_spacing (GtkTable *table)
732 {
733   g_return_val_if_fail (GTK_IS_TABLE (table), 0);
734
735   return table->row_spacing;
736 }
737
738 void
739 gtk_table_set_col_spacings (GtkTable *table,
740                             guint     spacing)
741 {
742   guint col;
743   
744   g_return_if_fail (GTK_IS_TABLE (table));
745   
746   table->column_spacing = spacing;
747   for (col = 0; col < table->ncols; col++)
748     table->cols[col].spacing = spacing;
749   
750   if (GTK_WIDGET_VISIBLE (table))
751     gtk_widget_queue_resize (GTK_WIDGET (table));
752
753   g_object_notify (G_OBJECT (table), "column_spacing");
754 }
755
756 /**
757  * gtk_table_get_default_col_spacing:
758  * @table: a #GtkTable
759  *
760  * Gets the default column spacing for the table. This is
761  * the spacing that will be used for newly added columns.
762  * (See gtk_table_set_col_spacings())
763  *
764  * Returns value: the default column spacing
765  **/
766 guint
767 gtk_table_get_default_col_spacing (GtkTable *table)
768 {
769   g_return_val_if_fail (GTK_IS_TABLE (table), 0);
770
771   return table->column_spacing;
772 }
773
774 void
775 gtk_table_set_homogeneous (GtkTable *table,
776                            gboolean  homogeneous)
777 {
778   g_return_if_fail (GTK_IS_TABLE (table));
779
780   homogeneous = (homogeneous != 0);
781   if (homogeneous != table->homogeneous)
782     {
783       table->homogeneous = homogeneous;
784       
785       if (GTK_WIDGET_VISIBLE (table))
786         gtk_widget_queue_resize (GTK_WIDGET (table));
787
788       g_object_notify (G_OBJECT (table), "homogeneous");
789     }
790 }
791
792 /**
793  * gtk_table_get_homogeneous:
794  * @table: a #GtkTable
795  *
796  * Returns whether the table cells are all constrained to the same
797  * width and height. (See gtk_table_set_homogenous ())
798  *
799  * Return value: %TRUE if the cells are all constrained to the same size
800  **/
801 gboolean
802 gtk_table_get_homogeneous (GtkTable *table)
803 {
804   g_return_val_if_fail (GTK_IS_TABLE (table), FALSE);
805
806   return table->homogeneous;
807 }
808
809 static void
810 gtk_table_finalize (GObject *object)
811 {
812   GtkTable *table;
813   
814   g_return_if_fail (GTK_IS_TABLE (object));
815   
816   table = GTK_TABLE (object);
817   
818   g_free (table->rows);
819   g_free (table->cols);
820   
821   G_OBJECT_CLASS (parent_class)->finalize (object);
822 }
823
824 static void
825 gtk_table_size_request (GtkWidget      *widget,
826                         GtkRequisition *requisition)
827 {
828   GtkTable *table;
829   gint row, col;
830   
831   g_return_if_fail (GTK_IS_TABLE (widget));
832   g_return_if_fail (requisition != NULL);
833   
834   table = GTK_TABLE (widget);
835   
836   requisition->width = 0;
837   requisition->height = 0;
838   
839   gtk_table_size_request_init (table);
840   gtk_table_size_request_pass1 (table);
841   gtk_table_size_request_pass2 (table);
842   gtk_table_size_request_pass3 (table);
843   gtk_table_size_request_pass2 (table);
844   
845   for (col = 0; col < table->ncols; col++)
846     requisition->width += table->cols[col].requisition;
847   for (col = 0; col + 1 < table->ncols; col++)
848     requisition->width += table->cols[col].spacing;
849   
850   for (row = 0; row < table->nrows; row++)
851     requisition->height += table->rows[row].requisition;
852   for (row = 0; row + 1 < table->nrows; row++)
853     requisition->height += table->rows[row].spacing;
854   
855   requisition->width += GTK_CONTAINER (table)->border_width * 2;
856   requisition->height += GTK_CONTAINER (table)->border_width * 2;
857 }
858
859 static void
860 gtk_table_size_allocate (GtkWidget     *widget,
861                          GtkAllocation *allocation)
862 {
863   GtkTable *table;
864   
865   g_return_if_fail (GTK_IS_TABLE (widget));
866   g_return_if_fail (allocation != NULL);
867   
868   widget->allocation = *allocation;
869   table = GTK_TABLE (widget);
870   
871   gtk_table_size_allocate_init (table);
872   gtk_table_size_allocate_pass1 (table);
873   gtk_table_size_allocate_pass2 (table);
874 }
875
876 static void
877 gtk_table_add (GtkContainer *container,
878                GtkWidget    *widget)
879 {
880   g_return_if_fail (GTK_IS_TABLE (container));
881   g_return_if_fail (widget != NULL);
882   
883   gtk_table_attach_defaults (GTK_TABLE (container), widget, 0, 1, 0, 1);
884 }
885
886 static void
887 gtk_table_remove (GtkContainer *container,
888                   GtkWidget    *widget)
889 {
890   GtkTable *table;
891   GtkTableChild *child;
892   GList *children;
893   
894   g_return_if_fail (GTK_IS_TABLE (container));
895   g_return_if_fail (widget != NULL);
896   
897   table = GTK_TABLE (container);
898   children = table->children;
899   
900   while (children)
901     {
902       child = children->data;
903       children = children->next;
904       
905       if (child->widget == widget)
906         {
907           gboolean was_visible = GTK_WIDGET_VISIBLE (widget);
908           
909           gtk_widget_unparent (widget);
910           
911           table->children = g_list_remove (table->children, child);
912           g_free (child);
913           
914           if (was_visible && GTK_WIDGET_VISIBLE (container))
915             gtk_widget_queue_resize (GTK_WIDGET (container));
916           break;
917         }
918     }
919 }
920
921 static void
922 gtk_table_forall (GtkContainer *container,
923                   gboolean      include_internals,
924                   GtkCallback   callback,
925                   gpointer      callback_data)
926 {
927   GtkTable *table;
928   GtkTableChild *child;
929   GList *children;
930   
931   g_return_if_fail (GTK_IS_TABLE (container));
932   g_return_if_fail (callback != NULL);
933   
934   table = GTK_TABLE (container);
935   children = table->children;
936   
937   while (children)
938     {
939       child = children->data;
940       children = children->next;
941       
942       (* callback) (child->widget, callback_data);
943     }
944 }
945
946 static void
947 gtk_table_size_request_init (GtkTable *table)
948 {
949   GtkTableChild *child;
950   GList *children;
951   gint row, col;
952   
953   for (row = 0; row < table->nrows; row++)
954     {
955       table->rows[row].requisition = 0;
956       table->rows[row].expand = FALSE;
957     }
958   for (col = 0; col < table->ncols; col++)
959     {
960       table->cols[col].requisition = 0;
961       table->cols[col].expand = FALSE;
962     }
963   
964   children = table->children;
965   while (children)
966     {
967       child = children->data;
968       children = children->next;
969       
970       if (GTK_WIDGET_VISIBLE (child->widget))
971         gtk_widget_size_request (child->widget, NULL);
972
973       if (child->left_attach == (child->right_attach - 1) && child->xexpand)
974         table->cols[child->left_attach].expand = TRUE;
975       
976       if (child->top_attach == (child->bottom_attach - 1) && child->yexpand)
977         table->rows[child->top_attach].expand = TRUE;
978     }
979 }
980
981 static void
982 gtk_table_size_request_pass1 (GtkTable *table)
983 {
984   GtkTableChild *child;
985   GList *children;
986   gint width;
987   gint height;
988   
989   children = table->children;
990   while (children)
991     {
992       child = children->data;
993       children = children->next;
994       
995       if (GTK_WIDGET_VISIBLE (child->widget))
996         {
997           GtkRequisition child_requisition;
998           gtk_widget_get_child_requisition (child->widget, &child_requisition);
999
1000           /* Child spans a single column.
1001            */
1002           if (child->left_attach == (child->right_attach - 1))
1003             {
1004               width = child_requisition.width + child->xpadding * 2;
1005               table->cols[child->left_attach].requisition = MAX (table->cols[child->left_attach].requisition, width);
1006             }
1007           
1008           /* Child spans a single row.
1009            */
1010           if (child->top_attach == (child->bottom_attach - 1))
1011             {
1012               height = child_requisition.height + child->ypadding * 2;
1013               table->rows[child->top_attach].requisition = MAX (table->rows[child->top_attach].requisition, height);
1014             }
1015         }
1016     }
1017 }
1018
1019 static void
1020 gtk_table_size_request_pass2 (GtkTable *table)
1021 {
1022   gint max_width;
1023   gint max_height;
1024   gint row, col;
1025   
1026   if (table->homogeneous)
1027     {
1028       max_width = 0;
1029       max_height = 0;
1030       
1031       for (col = 0; col < table->ncols; col++)
1032         max_width = MAX (max_width, table->cols[col].requisition);
1033       for (row = 0; row < table->nrows; row++)
1034         max_height = MAX (max_height, table->rows[row].requisition);
1035       
1036       for (col = 0; col < table->ncols; col++)
1037         table->cols[col].requisition = max_width;
1038       for (row = 0; row < table->nrows; row++)
1039         table->rows[row].requisition = max_height;
1040     }
1041 }
1042
1043 static void
1044 gtk_table_size_request_pass3 (GtkTable *table)
1045 {
1046   GtkTableChild *child;
1047   GList *children;
1048   gint width, height;
1049   gint row, col;
1050   gint extra;
1051   
1052   children = table->children;
1053   while (children)
1054     {
1055       child = children->data;
1056       children = children->next;
1057       
1058       if (GTK_WIDGET_VISIBLE (child->widget))
1059         {
1060           /* Child spans multiple columns.
1061            */
1062           if (child->left_attach != (child->right_attach - 1))
1063             {
1064               GtkRequisition child_requisition;
1065
1066               gtk_widget_get_child_requisition (child->widget, &child_requisition);
1067               
1068               /* Check and see if there is already enough space
1069                *  for the child.
1070                */
1071               width = 0;
1072               for (col = child->left_attach; col < child->right_attach; col++)
1073                 {
1074                   width += table->cols[col].requisition;
1075                   if ((col + 1) < child->right_attach)
1076                     width += table->cols[col].spacing;
1077                 }
1078               
1079               /* If we need to request more space for this child to fill
1080                *  its requisition, then divide up the needed space amongst the
1081                *  columns it spans, favoring expandable columns if any.
1082                */
1083               if (width < child_requisition.width + child->xpadding * 2)
1084                 {
1085                   gint n_expand = 0;
1086                   gboolean force_expand = FALSE;
1087                   
1088                   width = child_requisition.width + child->xpadding * 2 - width;
1089
1090                   for (col = child->left_attach; col < child->right_attach; col++)
1091                     if (table->cols[col].expand)
1092                       n_expand++;
1093
1094                   if (n_expand == 0)
1095                     {
1096                       n_expand = (child->right_attach - child->left_attach);
1097                       force_expand = TRUE;
1098                     }
1099                     
1100                   for (col = child->left_attach; col < child->right_attach; col++)
1101                     if (force_expand || table->cols[col].expand)
1102                       {
1103                         extra = width / n_expand;
1104                         table->cols[col].requisition += extra;
1105                         width -= extra;
1106                         n_expand--;
1107                       }
1108                 }
1109             }
1110           
1111           /* Child spans multiple rows.
1112            */
1113           if (child->top_attach != (child->bottom_attach - 1))
1114             {
1115               GtkRequisition child_requisition;
1116
1117               gtk_widget_get_child_requisition (child->widget, &child_requisition);
1118
1119               /* Check and see if there is already enough space
1120                *  for the child.
1121                */
1122               height = 0;
1123               for (row = child->top_attach; row < child->bottom_attach; row++)
1124                 {
1125                   height += table->rows[row].requisition;
1126                   if ((row + 1) < child->bottom_attach)
1127                     height += table->rows[row].spacing;
1128                 }
1129               
1130               /* If we need to request more space for this child to fill
1131                *  its requisition, then divide up the needed space amongst the
1132                *  rows it spans, favoring expandable rows if any.
1133                */
1134               if (height < child_requisition.height + child->ypadding * 2)
1135                 {
1136                   gint n_expand = 0;
1137                   gboolean force_expand = FALSE;
1138                   
1139                   height = child_requisition.height + child->ypadding * 2 - height;
1140                   
1141                   for (row = child->top_attach; row < child->bottom_attach; row++)
1142                     {
1143                       if (table->rows[row].expand)
1144                         n_expand++;
1145                     }
1146
1147                   if (n_expand == 0)
1148                     {
1149                       n_expand = (child->bottom_attach - child->top_attach);
1150                       force_expand = TRUE;
1151                     }
1152                     
1153                   for (row = child->top_attach; row < child->bottom_attach; row++)
1154                     if (force_expand || table->rows[row].expand)
1155                       {
1156                         extra = height / n_expand;
1157                         table->rows[row].requisition += extra;
1158                         height -= extra;
1159                         n_expand--;
1160                       }
1161                 }
1162             }
1163         }
1164     }
1165 }
1166
1167 static void
1168 gtk_table_size_allocate_init (GtkTable *table)
1169 {
1170   GtkTableChild *child;
1171   GList *children;
1172   gint row, col;
1173   gint has_expand;
1174   gint has_shrink;
1175   
1176   /* Initialize the rows and cols.
1177    *  By default, rows and cols do not expand and do shrink.
1178    *  Those values are modified by the children that occupy
1179    *  the rows and cols.
1180    */
1181   for (col = 0; col < table->ncols; col++)
1182     {
1183       table->cols[col].allocation = table->cols[col].requisition;
1184       table->cols[col].need_expand = FALSE;
1185       table->cols[col].need_shrink = TRUE;
1186       table->cols[col].expand = FALSE;
1187       table->cols[col].shrink = TRUE;
1188       table->cols[col].empty = TRUE;
1189     }
1190   for (row = 0; row < table->nrows; row++)
1191     {
1192       table->rows[row].allocation = table->rows[row].requisition;
1193       table->rows[row].need_expand = FALSE;
1194       table->rows[row].need_shrink = TRUE;
1195       table->rows[row].expand = FALSE;
1196       table->rows[row].shrink = TRUE;
1197       table->rows[row].empty = TRUE;
1198     }
1199   
1200   /* Loop over all the children and adjust the row and col values
1201    *  based on whether the children want to be allowed to expand
1202    *  or shrink. This loop handles children that occupy a single
1203    *  row or column.
1204    */
1205   children = table->children;
1206   while (children)
1207     {
1208       child = children->data;
1209       children = children->next;
1210       
1211       if (GTK_WIDGET_VISIBLE (child->widget))
1212         {
1213           if (child->left_attach == (child->right_attach - 1))
1214             {
1215               if (child->xexpand)
1216                 table->cols[child->left_attach].expand = TRUE;
1217               
1218               if (!child->xshrink)
1219                 table->cols[child->left_attach].shrink = FALSE;
1220               
1221               table->cols[child->left_attach].empty = FALSE;
1222             }
1223           
1224           if (child->top_attach == (child->bottom_attach - 1))
1225             {
1226               if (child->yexpand)
1227                 table->rows[child->top_attach].expand = TRUE;
1228               
1229               if (!child->yshrink)
1230                 table->rows[child->top_attach].shrink = FALSE;
1231
1232               table->rows[child->top_attach].empty = FALSE;
1233             }
1234         }
1235     }
1236   
1237   /* Loop over all the children again and this time handle children
1238    *  which span multiple rows or columns.
1239    */
1240   children = table->children;
1241   while (children)
1242     {
1243       child = children->data;
1244       children = children->next;
1245       
1246       if (GTK_WIDGET_VISIBLE (child->widget))
1247         {
1248           if (child->left_attach != (child->right_attach - 1))
1249             {
1250               for (col = child->left_attach; col < child->right_attach; col++)
1251                 table->cols[col].empty = FALSE;
1252
1253               if (child->xexpand)
1254                 {
1255                   has_expand = FALSE;
1256                   for (col = child->left_attach; col < child->right_attach; col++)
1257                     if (table->cols[col].expand)
1258                       {
1259                         has_expand = TRUE;
1260                         break;
1261                       }
1262                   
1263                   if (!has_expand)
1264                     for (col = child->left_attach; col < child->right_attach; col++)
1265                       table->cols[col].need_expand = TRUE;
1266                 }
1267               
1268               if (!child->xshrink)
1269                 {
1270                   has_shrink = TRUE;
1271                   for (col = child->left_attach; col < child->right_attach; col++)
1272                     if (!table->cols[col].shrink)
1273                       {
1274                         has_shrink = FALSE;
1275                         break;
1276                       }
1277                   
1278                   if (has_shrink)
1279                     for (col = child->left_attach; col < child->right_attach; col++)
1280                       table->cols[col].need_shrink = FALSE;
1281                 }
1282             }
1283           
1284           if (child->top_attach != (child->bottom_attach - 1))
1285             {
1286               for (row = child->top_attach; row < child->bottom_attach; row++)
1287                 table->rows[row].empty = FALSE;
1288
1289               if (child->yexpand)
1290                 {
1291                   has_expand = FALSE;
1292                   for (row = child->top_attach; row < child->bottom_attach; row++)
1293                     if (table->rows[row].expand)
1294                       {
1295                         has_expand = TRUE;
1296                         break;
1297                       }
1298                   
1299                   if (!has_expand)
1300                     for (row = child->top_attach; row < child->bottom_attach; row++)
1301                       table->rows[row].need_expand = TRUE;
1302                 }
1303               
1304               if (!child->yshrink)
1305                 {
1306                   has_shrink = TRUE;
1307                   for (row = child->top_attach; row < child->bottom_attach; row++)
1308                     if (!table->rows[row].shrink)
1309                       {
1310                         has_shrink = FALSE;
1311                         break;
1312                       }
1313                   
1314                   if (has_shrink)
1315                     for (row = child->top_attach; row < child->bottom_attach; row++)
1316                       table->rows[row].need_shrink = FALSE;
1317                 }
1318             }
1319         }
1320     }
1321   
1322   /* Loop over the columns and set the expand and shrink values
1323    *  if the column can be expanded or shrunk.
1324    */
1325   for (col = 0; col < table->ncols; col++)
1326     {
1327       if (table->cols[col].empty)
1328         {
1329           table->cols[col].expand = FALSE;
1330           table->cols[col].shrink = FALSE;
1331         }
1332       else
1333         {
1334           if (table->cols[col].need_expand)
1335             table->cols[col].expand = TRUE;
1336           if (!table->cols[col].need_shrink)
1337             table->cols[col].shrink = FALSE;
1338         }
1339     }
1340   
1341   /* Loop over the rows and set the expand and shrink values
1342    *  if the row can be expanded or shrunk.
1343    */
1344   for (row = 0; row < table->nrows; row++)
1345     {
1346       if (table->rows[row].empty)
1347         {
1348           table->rows[row].expand = FALSE;
1349           table->rows[row].shrink = FALSE;
1350         }
1351       else
1352         {
1353           if (table->rows[row].need_expand)
1354             table->rows[row].expand = TRUE;
1355           if (!table->rows[row].need_shrink)
1356             table->rows[row].shrink = FALSE;
1357         }
1358     }
1359 }
1360
1361 static void
1362 gtk_table_size_allocate_pass1 (GtkTable *table)
1363 {
1364   gint real_width;
1365   gint real_height;
1366   gint width, height;
1367   gint row, col;
1368   gint nexpand;
1369   gint nshrink;
1370   gint extra;
1371   
1372   /* If we were allocated more space than we requested
1373    *  then we have to expand any expandable rows and columns
1374    *  to fill in the extra space.
1375    */
1376   
1377   real_width = GTK_WIDGET (table)->allocation.width - GTK_CONTAINER (table)->border_width * 2;
1378   real_height = GTK_WIDGET (table)->allocation.height - GTK_CONTAINER (table)->border_width * 2;
1379   
1380   if (table->homogeneous)
1381     {
1382       if (!table->children)
1383         nexpand = 1;
1384       else
1385         {
1386           nexpand = 0;
1387           for (col = 0; col < table->ncols; col++)
1388             if (table->cols[col].expand)
1389               {
1390                 nexpand += 1;
1391                 break;
1392               }
1393         }
1394       if (nexpand)
1395         {
1396           width = real_width;
1397           for (col = 0; col + 1 < table->ncols; col++)
1398             width -= table->cols[col].spacing;
1399           
1400           for (col = 0; col < table->ncols; col++)
1401             {
1402               extra = width / (table->ncols - col);
1403               table->cols[col].allocation = MAX (1, extra);
1404               width -= extra;
1405             }
1406         }
1407     }
1408   else
1409     {
1410       width = 0;
1411       nexpand = 0;
1412       nshrink = 0;
1413       
1414       for (col = 0; col < table->ncols; col++)
1415         {
1416           width += table->cols[col].requisition;
1417           if (table->cols[col].expand)
1418             nexpand += 1;
1419           if (table->cols[col].shrink)
1420             nshrink += 1;
1421         }
1422       for (col = 0; col + 1 < table->ncols; col++)
1423         width += table->cols[col].spacing;
1424       
1425       /* Check to see if we were allocated more width than we requested.
1426        */
1427       if ((width < real_width) && (nexpand >= 1))
1428         {
1429           width = real_width - width;
1430           
1431           for (col = 0; col < table->ncols; col++)
1432             if (table->cols[col].expand)
1433               {
1434                 extra = width / nexpand;
1435                 table->cols[col].allocation += extra;
1436                 
1437                 width -= extra;
1438                 nexpand -= 1;
1439               }
1440         }
1441       
1442       /* Check to see if we were allocated less width than we requested,
1443        * then shrink until we fit the size give.
1444        */
1445       if (width > real_width)
1446         {
1447           gint total_nshrink = nshrink;
1448
1449           extra = width - real_width;
1450           while (total_nshrink > 0 && extra > 0)
1451             {
1452               nshrink = total_nshrink;
1453               for (col = 0; col < table->ncols; col++)
1454                 if (table->cols[col].shrink)
1455                   {
1456                     gint allocation = table->cols[col].allocation;
1457
1458                     table->cols[col].allocation = MAX (1, (gint) table->cols[col].allocation - extra / nshrink);
1459                     extra -= allocation - table->cols[col].allocation;
1460                     nshrink -= 1;
1461                     if (table->cols[col].allocation < 2)
1462                       {
1463                         total_nshrink -= 1;
1464                         table->cols[col].shrink = FALSE;
1465                       }
1466                   }
1467             }
1468         }
1469     }
1470   
1471   if (table->homogeneous)
1472     {
1473       if (!table->children)
1474         nexpand = 1;
1475       else
1476         {
1477           nexpand = 0;
1478           for (row = 0; row < table->nrows; row++)
1479             if (table->rows[row].expand)
1480               {
1481                 nexpand += 1;
1482                 break;
1483               }
1484         }
1485       if (nexpand)
1486         {
1487           height = real_height;
1488           
1489           for (row = 0; row + 1 < table->nrows; row++)
1490             height -= table->rows[row].spacing;
1491           
1492           
1493           for (row = 0; row < table->nrows; row++)
1494             {
1495               extra = height / (table->nrows - row);
1496               table->rows[row].allocation = MAX (1, extra);
1497               height -= extra;
1498             }
1499         }
1500     }
1501   else
1502     {
1503       height = 0;
1504       nexpand = 0;
1505       nshrink = 0;
1506       
1507       for (row = 0; row < table->nrows; row++)
1508         {
1509           height += table->rows[row].requisition;
1510           if (table->rows[row].expand)
1511             nexpand += 1;
1512           if (table->rows[row].shrink)
1513             nshrink += 1;
1514         }
1515       for (row = 0; row + 1 < table->nrows; row++)
1516         height += table->rows[row].spacing;
1517       
1518       /* Check to see if we were allocated more height than we requested.
1519        */
1520       if ((height < real_height) && (nexpand >= 1))
1521         {
1522           height = real_height - height;
1523           
1524           for (row = 0; row < table->nrows; row++)
1525             if (table->rows[row].expand)
1526               {
1527                 extra = height / nexpand;
1528                 table->rows[row].allocation += extra;
1529                 
1530                 height -= extra;
1531                 nexpand -= 1;
1532               }
1533         }
1534       
1535       /* Check to see if we were allocated less height than we requested.
1536        * then shrink until we fit the size give.
1537        */
1538       if (height > real_height)
1539         {
1540           gint total_nshrink = nshrink;
1541           
1542           extra = height - real_height;
1543           while (total_nshrink > 0 && extra > 0)
1544             {
1545               nshrink = total_nshrink;
1546               for (row = 0; row < table->nrows; row++)
1547                 if (table->rows[row].shrink)
1548                   {
1549                     gint allocation = table->rows[row].allocation;
1550                     
1551                     table->rows[row].allocation = MAX (1, (gint) table->rows[row].allocation - extra / nshrink);
1552                     extra -= allocation - table->rows[row].allocation;
1553                     nshrink -= 1;
1554                     if (table->rows[row].allocation < 2)
1555                       {
1556                         total_nshrink -= 1;
1557                         table->rows[row].shrink = FALSE;
1558                       }
1559                   }
1560             }
1561         }
1562     }
1563 }
1564
1565 static void
1566 gtk_table_size_allocate_pass2 (GtkTable *table)
1567 {
1568   GtkTableChild *child;
1569   GList *children;
1570   gint max_width;
1571   gint max_height;
1572   gint x, y;
1573   gint row, col;
1574   GtkAllocation allocation;
1575   GtkWidget *widget = GTK_WIDGET (table);
1576   
1577   children = table->children;
1578   while (children)
1579     {
1580       child = children->data;
1581       children = children->next;
1582       
1583       if (GTK_WIDGET_VISIBLE (child->widget))
1584         {
1585           GtkRequisition child_requisition;
1586           gtk_widget_get_child_requisition (child->widget, &child_requisition);
1587
1588           x = GTK_WIDGET (table)->allocation.x + GTK_CONTAINER (table)->border_width;
1589           y = GTK_WIDGET (table)->allocation.y + GTK_CONTAINER (table)->border_width;
1590           max_width = 0;
1591           max_height = 0;
1592           
1593           for (col = 0; col < child->left_attach; col++)
1594             {
1595               x += table->cols[col].allocation;
1596               x += table->cols[col].spacing;
1597             }
1598           
1599           for (col = child->left_attach; col < child->right_attach; col++)
1600             {
1601               max_width += table->cols[col].allocation;
1602               if ((col + 1) < child->right_attach)
1603                 max_width += table->cols[col].spacing;
1604             }
1605           
1606           for (row = 0; row < child->top_attach; row++)
1607             {
1608               y += table->rows[row].allocation;
1609               y += table->rows[row].spacing;
1610             }
1611           
1612           for (row = child->top_attach; row < child->bottom_attach; row++)
1613             {
1614               max_height += table->rows[row].allocation;
1615               if ((row + 1) < child->bottom_attach)
1616                 max_height += table->rows[row].spacing;
1617             }
1618           
1619           if (child->xfill)
1620             {
1621               allocation.width = MAX (1, max_width - (gint)child->xpadding * 2);
1622               allocation.x = x + (max_width - allocation.width) / 2;
1623             }
1624           else
1625             {
1626               allocation.width = child_requisition.width;
1627               allocation.x = x + (max_width - allocation.width) / 2;
1628             }
1629           
1630           if (child->yfill)
1631             {
1632               allocation.height = MAX (1, max_height - (gint)child->ypadding * 2);
1633               allocation.y = y + (max_height - allocation.height) / 2;
1634             }
1635           else
1636             {
1637               allocation.height = child_requisition.height;
1638               allocation.y = y + (max_height - allocation.height) / 2;
1639             }
1640
1641           if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1642             allocation.x = widget->allocation.x + widget->allocation.width
1643               - (allocation.x - widget->allocation.x) - allocation.width;
1644           
1645           gtk_widget_size_allocate (child->widget, &allocation);
1646         }
1647     }
1648 }