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