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