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