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