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