1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Red Hat, Inc.
3 * Author: Matthias Clasen
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "config.h"
20
21 #include <string.h>
22
23 #include "gtkgrid.h"
24
25 #include "gtkorientableprivate.h"
26 #include "gtkrender.h"
27 #include "gtksizerequest.h"
28 #include "gtkwidgetprivate.h"
29 #include "gtkcontainerprivate.h"
30 #include "gtkcsscustomgadgetprivate.h"
31 #include "gtkprivate.h"
32 #include "gtkintl.h"
33
34
35 /**
36 * SECTION:gtkgrid
37 * @Short_description: Pack widgets in rows and columns
38 * @Title: GtkGrid
39 * @See_also: #GtkBox
40 *
41 * GtkGrid is a container which arranges its child widgets in
42 * rows and columns, with arbitrary positions and horizontal/vertical spans.
43 *
44 * Children are added using gtk_grid_attach(). They can span multiple
45 * rows or columns. It is also possible to add a child next to an
46 * existing child, using gtk_grid_attach_next_to(). The behaviour of
47 * GtkGrid when several children occupy the same grid cell is undefined.
48 *
49 * GtkGrid can be used like a #GtkBox by just using gtk_container_add(),
50 * which will place children next to each other in the direction determined
51 * by the #GtkOrientable:orientation property. However, if all you want is a
52 * single row or column, then #GtkBox is the preferred widget.
53 *
54 * # CSS nodes
55 *
56 * GtkGrid uses a single CSS node with name grid.
57 */
58
59 typedef struct _GtkGridChild GtkGridChild;
60 typedef struct _GtkGridChildAttach GtkGridChildAttach;
61 typedef struct _GtkGridRowProperties GtkGridRowProperties;
62 typedef struct _GtkGridLine GtkGridLine;
63 typedef struct _GtkGridLines GtkGridLines;
64 typedef struct _GtkGridLineData GtkGridLineData;
65 typedef struct _GtkGridRequest GtkGridRequest;
66
67 struct _GtkGridChildAttach
68 {
69 gint pos;
70 gint span;
71 };
72
73 struct _GtkGridRowProperties
74 {
75 gint row;
76 GtkBaselinePosition baseline_position;
77 };
78
79 static const GtkGridRowProperties gtk_grid_row_properties_default = {
80 0,
81 GTK_BASELINE_POSITION_CENTER
82 };
83
84 struct _GtkGridChild
85 {
86 GtkWidget *widget;
87 GtkGridChildAttach attach[2];
88 };
89
90 #define CHILD_LEFT(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].pos)
91 #define CHILD_WIDTH(child) ((child)->attach[GTK_ORIENTATION_HORIZONTAL].span)
92 #define CHILD_TOP(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].pos)
93 #define CHILD_HEIGHT(child) ((child)->attach[GTK_ORIENTATION_VERTICAL].span)
94
95 /* A GtkGridLineData struct contains row/column specific parts
96 * of the grid.
97 */
98 struct _GtkGridLineData
99 {
100 gint16 spacing;
101 guint homogeneous : 1;
102 };
103
104 struct _GtkGridPrivate
105 {
106 GList *children;
107 GList *row_properties;
108
109 GtkCssGadget *gadget;
110
111 GtkOrientation orientation;
112 gint baseline_row;
113
114 GtkGridLineData linedata[2];
115 };
116
117 #define ROWS(priv) (&(priv)->linedata[GTK_ORIENTATION_HORIZONTAL])
118 #define COLUMNS(priv) (&(priv)->linedata[GTK_ORIENTATION_VERTICAL])
119
120 /* A GtkGridLine struct represents a single row or column
121 * during size requests
122 */
123 struct _GtkGridLine
124 {
125 gint minimum;
126 gint natural;
127 gint minimum_above;
128 gint minimum_below;
129 gint natural_above;
130 gint natural_below;
131
132 gint position;
133 gint allocation;
134 gint allocated_baseline;
135
136 guint need_expand : 1;
137 guint expand : 1;
138 guint empty : 1;
139 };
140
141 struct _GtkGridLines
142 {
143 GtkGridLine *lines;
144 gint min, max;
145 };
146
147 struct _GtkGridRequest
148 {
149 GtkGrid *grid;
150 GtkGridLines lines[2];
151 };
152
153
154 enum
155 {
156 PROP_0,
157 PROP_ROW_SPACING,
158 PROP_COLUMN_SPACING,
159 PROP_ROW_HOMOGENEOUS,
160 PROP_COLUMN_HOMOGENEOUS,
161 PROP_BASELINE_ROW,
162 N_PROPERTIES,
163 PROP_ORIENTATION
164 };
165
166 static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
167
168 enum
169 {
170 CHILD_PROP_0,
171 CHILD_PROP_LEFT_ATTACH,
172 CHILD_PROP_TOP_ATTACH,
173 CHILD_PROP_WIDTH,
174 CHILD_PROP_HEIGHT,
175 N_CHILD_PROPERTIES
176 };
177
178 static GParamSpec *child_properties[N_CHILD_PROPERTIES] = { NULL, };
179
180 G_DEFINE_TYPE_WITH_CODE (GtkGrid, gtk_grid, GTK_TYPE_CONTAINER,
181 G_ADD_PRIVATE (GtkGrid)
182 G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
183
184
185 static void gtk_grid_row_properties_free (GtkGridRowProperties *props);
186
187 static void
gtk_grid_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)188 gtk_grid_get_property (GObject *object,
189 guint prop_id,
190 GValue *value,
191 GParamSpec *pspec)
192 {
193 GtkGrid *grid = GTK_GRID (object);
194 GtkGridPrivate *priv = grid->priv;
195
196 switch (prop_id)
197 {
198 case PROP_ORIENTATION:
199 g_value_set_enum (value, priv->orientation);
200 break;
201
202 case PROP_ROW_SPACING:
203 g_value_set_int (value, COLUMNS (priv)->spacing);
204 break;
205
206 case PROP_COLUMN_SPACING:
207 g_value_set_int (value, ROWS (priv)->spacing);
208 break;
209
210 case PROP_ROW_HOMOGENEOUS:
211 g_value_set_boolean (value, COLUMNS (priv)->homogeneous);
212 break;
213
214 case PROP_COLUMN_HOMOGENEOUS:
215 g_value_set_boolean (value, ROWS (priv)->homogeneous);
216 break;
217
218 case PROP_BASELINE_ROW:
219 g_value_set_int (value, priv->baseline_row);
220 break;
221
222 default:
223 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224 break;
225 }
226 }
227
228 static void
gtk_grid_set_orientation(GtkGrid * grid,GtkOrientation orientation)229 gtk_grid_set_orientation (GtkGrid *grid,
230 GtkOrientation orientation)
231 {
232 GtkGridPrivate *priv = grid->priv;
233
234 if (priv->orientation != orientation)
235 {
236 priv->orientation = orientation;
237 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (grid));
238
239 g_object_notify (G_OBJECT (grid), "orientation");
240 }
241 }
242
243 static void
gtk_grid_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)244 gtk_grid_set_property (GObject *object,
245 guint prop_id,
246 const GValue *value,
247 GParamSpec *pspec)
248 {
249 GtkGrid *grid = GTK_GRID (object);
250
251 switch (prop_id)
252 {
253 case PROP_ORIENTATION:
254 gtk_grid_set_orientation (grid, g_value_get_enum (value));
255 break;
256
257 case PROP_ROW_SPACING:
258 gtk_grid_set_row_spacing (grid, g_value_get_int (value));
259 break;
260
261 case PROP_COLUMN_SPACING:
262 gtk_grid_set_column_spacing (grid, g_value_get_int (value));
263 break;
264
265 case PROP_ROW_HOMOGENEOUS:
266 gtk_grid_set_row_homogeneous (grid, g_value_get_boolean (value));
267 break;
268
269 case PROP_COLUMN_HOMOGENEOUS:
270 gtk_grid_set_column_homogeneous (grid, g_value_get_boolean (value));
271 break;
272
273 case PROP_BASELINE_ROW:
274 gtk_grid_set_baseline_row (grid, g_value_get_int (value));
275 break;
276
277 default:
278 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
279 break;
280 }
281 }
282
283 static GtkGridChild *
find_grid_child(GtkGrid * grid,GtkWidget * widget)284 find_grid_child (GtkGrid *grid,
285 GtkWidget *widget)
286 {
287 GtkGridPrivate *priv = grid->priv;
288 GtkGridChild *child;
289 GList *list;
290
291 for (list = priv->children; list; list = list->next)
292 {
293 child = list->data;
294
295 if (child->widget == widget)
296 return child;
297 }
298
299 return NULL;
300 }
301
302 static void
gtk_grid_get_child_property(GtkContainer * container,GtkWidget * child,guint property_id,GValue * value,GParamSpec * pspec)303 gtk_grid_get_child_property (GtkContainer *container,
304 GtkWidget *child,
305 guint property_id,
306 GValue *value,
307 GParamSpec *pspec)
308 {
309 GtkGrid *grid = GTK_GRID (container);
310 GtkGridChild *grid_child;
311
312 grid_child = find_grid_child (grid, child);
313
314 if (grid_child == NULL)
315 {
316 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
317 return;
318 }
319
320 switch (property_id)
321 {
322 case CHILD_PROP_LEFT_ATTACH:
323 g_value_set_int (value, CHILD_LEFT (grid_child));
324 break;
325
326 case CHILD_PROP_TOP_ATTACH:
327 g_value_set_int (value, CHILD_TOP (grid_child));
328 break;
329
330 case CHILD_PROP_WIDTH:
331 g_value_set_int (value, CHILD_WIDTH (grid_child));
332 break;
333
334 case CHILD_PROP_HEIGHT:
335 g_value_set_int (value, CHILD_HEIGHT (grid_child));
336 break;
337
338 default:
339 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
340 break;
341 }
342 }
343
344 static void
gtk_grid_set_child_property(GtkContainer * container,GtkWidget * child,guint property_id,const GValue * value,GParamSpec * pspec)345 gtk_grid_set_child_property (GtkContainer *container,
346 GtkWidget *child,
347 guint property_id,
348 const GValue *value,
349 GParamSpec *pspec)
350 {
351 GtkGrid *grid = GTK_GRID (container);
352 GtkGridChild *grid_child;
353
354 grid_child = find_grid_child (grid, child);
355
356 if (grid_child == NULL)
357 {
358 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
359 return;
360 }
361
362 switch (property_id)
363 {
364 case CHILD_PROP_LEFT_ATTACH:
365 CHILD_LEFT (grid_child) = g_value_get_int (value);
366 break;
367
368 case CHILD_PROP_TOP_ATTACH:
369 CHILD_TOP (grid_child) = g_value_get_int (value);
370 break;
371
372 case CHILD_PROP_WIDTH:
373 CHILD_WIDTH (grid_child) = g_value_get_int (value);
374 break;
375
376 case CHILD_PROP_HEIGHT:
377 CHILD_HEIGHT (grid_child) = g_value_get_int (value);
378 break;
379
380 default:
381 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
382 break;
383 }
384
385 if (_gtk_widget_get_visible (child) &&
386 _gtk_widget_get_visible (GTK_WIDGET (grid)))
387 gtk_widget_queue_resize (child);
388 }
389
390 static void
gtk_grid_finalize(GObject * object)391 gtk_grid_finalize (GObject *object)
392 {
393 GtkGrid *grid = GTK_GRID (object);
394 GtkGridPrivate *priv = grid->priv;
395
396 g_list_free_full (priv->row_properties, (GDestroyNotify)gtk_grid_row_properties_free);
397
398 g_clear_object (&priv->gadget);
399
400 G_OBJECT_CLASS (gtk_grid_parent_class)->finalize (object);
401 }
402
403 static void
grid_attach(GtkGrid * grid,GtkWidget * widget,gint left,gint top,gint width,gint height)404 grid_attach (GtkGrid *grid,
405 GtkWidget *widget,
406 gint left,
407 gint top,
408 gint width,
409 gint height)
410 {
411 GtkGridPrivate *priv = grid->priv;
412 GtkGridChild *child;
413
414 child = g_slice_new (GtkGridChild);
415 child->widget = widget;
416 CHILD_LEFT (child) = left;
417 CHILD_TOP (child) = top;
418 CHILD_WIDTH (child) = width;
419 CHILD_HEIGHT (child) = height;
420
421 priv->children = g_list_prepend (priv->children, child);
422
423 gtk_widget_set_parent (widget, GTK_WIDGET (grid));
424 }
425
426 /* Find the position 'touching' existing
427 * children. @orientation and @max determine
428 * from which direction to approach (horizontal
429 * + max = right, vertical + !max = top, etc).
430 * @op_pos, @op_span determine the rows/columns
431 * in which the touching has to happen.
432 */
433 static gint
find_attach_position(GtkGrid * grid,GtkOrientation orientation,gint op_pos,gint op_span,gboolean max)434 find_attach_position (GtkGrid *grid,
435 GtkOrientation orientation,
436 gint op_pos,
437 gint op_span,
438 gboolean max)
439 {
440 GtkGridPrivate *priv = grid->priv;
441 GtkGridChild *grid_child;
442 GtkGridChildAttach *attach;
443 GtkGridChildAttach *opposite;
444 GList *list;
445 gint pos;
446 gboolean hit;
447
448 if (max)
449 pos = -G_MAXINT;
450 else
451 pos = G_MAXINT;
452
453 hit = FALSE;
454
455 for (list = priv->children; list; list = list->next)
456 {
457 grid_child = list->data;
458
459 attach = &grid_child->attach[orientation];
460 opposite = &grid_child->attach[1 - orientation];
461
462 /* check if the ranges overlap */
463 if (opposite->pos <= op_pos + op_span && op_pos <= opposite->pos + opposite->span)
464 {
465 hit = TRUE;
466
467 if (max)
468 pos = MAX (pos, attach->pos + attach->span);
469 else
470 pos = MIN (pos, attach->pos);
471 }
472 }
473
474 if (!hit)
475 pos = 0;
476
477 return pos;
478 }
479
480 static void
gtk_grid_add(GtkContainer * container,GtkWidget * child)481 gtk_grid_add (GtkContainer *container,
482 GtkWidget *child)
483 {
484 GtkGrid *grid = GTK_GRID (container);
485 GtkGridPrivate *priv = grid->priv;
486 gint pos[2] = { 0, 0 };
487
488 pos[priv->orientation] = find_attach_position (grid, priv->orientation, 0, 1, TRUE);
489 grid_attach (grid, child, pos[0], pos[1], 1, 1);
490 }
491
492 static void
gtk_grid_remove(GtkContainer * container,GtkWidget * child)493 gtk_grid_remove (GtkContainer *container,
494 GtkWidget *child)
495 {
496 GtkGrid *grid = GTK_GRID (container);
497 GtkGridPrivate *priv = grid->priv;
498 GtkGridChild *grid_child;
499 GList *list;
500
501 for (list = priv->children; list; list = list->next)
502 {
503 grid_child = list->data;
504
505 if (grid_child->widget == child)
506 {
507 gboolean was_visible = _gtk_widget_get_visible (child);
508
509 gtk_widget_unparent (child);
510
511 priv->children = g_list_remove (priv->children, grid_child);
512
513 g_slice_free (GtkGridChild, grid_child);
514
515 if (was_visible && _gtk_widget_get_visible (GTK_WIDGET (grid)))
516 gtk_widget_queue_resize (GTK_WIDGET (grid));
517
518 break;
519 }
520 }
521 }
522
523 static void
gtk_grid_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)524 gtk_grid_forall (GtkContainer *container,
525 gboolean include_internals,
526 GtkCallback callback,
527 gpointer callback_data)
528 {
529 GtkGrid *grid = GTK_GRID (container);
530 GtkGridPrivate *priv = grid->priv;
531 GtkGridChild *child;
532 GList *list;
533
534 list = priv->children;
535 while (list)
536 {
537 child = list->data;
538 list = list->next;
539
540 (* callback) (child->widget, callback_data);
541 }
542 }
543
544 static GType
gtk_grid_child_type(GtkContainer * container)545 gtk_grid_child_type (GtkContainer *container)
546 {
547 return GTK_TYPE_WIDGET;
548 }
549
550 /* Calculates the min and max numbers for both orientations.
551 */
552 static void
gtk_grid_request_count_lines(GtkGridRequest * request)553 gtk_grid_request_count_lines (GtkGridRequest *request)
554 {
555 GtkGridPrivate *priv = request->grid->priv;
556 GtkGridChild *child;
557 GtkGridChildAttach *attach;
558 GList *list;
559 gint min[2];
560 gint max[2];
561
562 min[0] = min[1] = G_MAXINT;
563 max[0] = max[1] = G_MININT;
564
565 for (list = priv->children; list; list = list->next)
566 {
567 child = list->data;
568 attach = child->attach;
569
570 min[0] = MIN (min[0], attach[0].pos);
571 max[0] = MAX (max[0], attach[0].pos + attach[0].span);
572 min[1] = MIN (min[1], attach[1].pos);
573 max[1] = MAX (max[1], attach[1].pos + attach[1].span);
574 }
575
576 request->lines[0].min = min[0];
577 request->lines[0].max = max[0];
578 request->lines[1].min = min[1];
579 request->lines[1].max = max[1];
580 }
581
582 /* Sets line sizes to 0 and marks lines as expand
583 * if they have a non-spanning expanding child.
584 */
585 static void
gtk_grid_request_init(GtkGridRequest * request,GtkOrientation orientation)586 gtk_grid_request_init (GtkGridRequest *request,
587 GtkOrientation orientation)
588 {
589 GtkGridPrivate *priv = request->grid->priv;
590 GtkGridChild *child;
591 GtkGridChildAttach *attach;
592 GtkGridLines *lines;
593 GList *list;
594 gint i;
595
596 lines = &request->lines[orientation];
597
598 for (i = 0; i < lines->max - lines->min; i++)
599 {
600 lines->lines[i].minimum = 0;
601 lines->lines[i].natural = 0;
602 lines->lines[i].minimum_above = -1;
603 lines->lines[i].minimum_below = -1;
604 lines->lines[i].natural_above = -1;
605 lines->lines[i].natural_below = -1;
606 lines->lines[i].expand = FALSE;
607 lines->lines[i].empty = TRUE;
608 }
609
610 for (list = priv->children; list; list = list->next)
611 {
612 child = list->data;
613
614 attach = &child->attach[orientation];
615 if (attach->span == 1 && gtk_widget_compute_expand (child->widget, orientation))
616 lines->lines[attach->pos - lines->min].expand = TRUE;
617 }
618 }
619
620 /* Sums allocations for lines spanned by child and their spacing.
621 */
622 static gint
compute_allocation_for_child(GtkGridRequest * request,GtkGridChild * child,GtkOrientation orientation)623 compute_allocation_for_child (GtkGridRequest *request,
624 GtkGridChild *child,
625 GtkOrientation orientation)
626 {
627 GtkGridPrivate *priv = request->grid->priv;
628 GtkGridLineData *linedata;
629 GtkGridLines *lines;
630 GtkGridLine *line;
631 GtkGridChildAttach *attach;
632 gint size;
633 gint i;
634
635 linedata = &priv->linedata[orientation];
636 lines = &request->lines[orientation];
637 attach = &child->attach[orientation];
638
639 size = (attach->span - 1) * linedata->spacing;
640 for (i = 0; i < attach->span; i++)
641 {
642 line = &lines->lines[attach->pos - lines->min + i];
643 size += line->allocation;
644 }
645
646 return size;
647 }
648
649 static void
compute_request_for_child(GtkGridRequest * request,GtkGridChild * child,GtkOrientation orientation,gboolean contextual,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)650 compute_request_for_child (GtkGridRequest *request,
651 GtkGridChild *child,
652 GtkOrientation orientation,
653 gboolean contextual,
654 gint *minimum,
655 gint *natural,
656 gint *minimum_baseline,
657 gint *natural_baseline)
658 {
659 if (minimum_baseline)
660 *minimum_baseline = -1;
661 if (natural_baseline)
662 *natural_baseline = -1;
663 if (contextual)
664 {
665 gint size;
666
667 size = compute_allocation_for_child (request, child, 1 - orientation);
668 if (orientation == GTK_ORIENTATION_HORIZONTAL)
669 gtk_widget_get_preferred_width_for_height (child->widget,
670 size,
671 minimum, natural);
672 else
673 gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
674 size,
675 minimum, natural,
676 minimum_baseline, natural_baseline);
677 }
678 else
679 {
680 if (orientation == GTK_ORIENTATION_HORIZONTAL)
681 gtk_widget_get_preferred_width (child->widget, minimum, natural);
682 else
683 gtk_widget_get_preferred_height_and_baseline_for_width (child->widget,
684 -1,
685 minimum, natural,
686 minimum_baseline, natural_baseline);
687 }
688 }
689
690 /* Sets requisition to max. of non-spanning children.
691 * If contextual is TRUE, requires allocations of
692 * lines in the opposite orientation to be set.
693 */
694 static void
gtk_grid_request_non_spanning(GtkGridRequest * request,GtkOrientation orientation,gboolean contextual)695 gtk_grid_request_non_spanning (GtkGridRequest *request,
696 GtkOrientation orientation,
697 gboolean contextual)
698 {
699 GtkGridPrivate *priv = request->grid->priv;
700 GtkGridChild *child;
701 GtkGridChildAttach *attach;
702 GtkGridLines *lines;
703 GtkGridLine *line;
704 GList *list;
705 gint i;
706 GtkBaselinePosition baseline_pos;
707 gint minimum, minimum_baseline;
708 gint natural, natural_baseline;
709
710 lines = &request->lines[orientation];
711
712 for (list = priv->children; list; list = list->next)
713 {
714 child = list->data;
715
716 if (!_gtk_widget_get_visible (child->widget))
717 continue;
718
719 attach = &child->attach[orientation];
720 if (attach->span != 1)
721 continue;
722
723 compute_request_for_child (request, child, orientation, contextual, &minimum, &natural, &minimum_baseline, &natural_baseline);
724
725 line = &lines->lines[attach->pos - lines->min];
726
727 if (minimum_baseline != -1)
728 {
729 line->minimum_above = MAX (line->minimum_above, minimum_baseline);
730 line->minimum_below = MAX (line->minimum_below, minimum - minimum_baseline);
731 line->natural_above = MAX (line->natural_above, natural_baseline);
732 line->natural_below = MAX (line->natural_below, natural - natural_baseline);
733 }
734 else
735 {
736 line->minimum = MAX (line->minimum, minimum);
737 line->natural = MAX (line->natural, natural);
738 }
739 }
740
741 for (i = 0; i < lines->max - lines->min; i++)
742 {
743 line = &lines->lines[i];
744
745 if (line->minimum_above != -1)
746 {
747 line->minimum = MAX (line->minimum, line->minimum_above + line->minimum_below);
748 line->natural = MAX (line->natural, line->natural_above + line->natural_below);
749
750 baseline_pos = gtk_grid_get_row_baseline_position (request->grid, i + lines->min);
751
752 switch (baseline_pos)
753 {
754 case GTK_BASELINE_POSITION_TOP:
755 line->minimum_above += 0;
756 line->minimum_below += line->minimum - (line->minimum_above + line->minimum_below);
757 line->natural_above += 0;
758 line->natural_below += line->natural - (line->natural_above + line->natural_below);
759 break;
760 case GTK_BASELINE_POSITION_CENTER:
761 line->minimum_above += (line->minimum - (line->minimum_above + line->minimum_below))/2;
762 line->minimum_below += (line->minimum - (line->minimum_above + line->minimum_below))/2;
763 line->natural_above += (line->natural - (line->natural_above + line->natural_below))/2;
764 line->natural_below += (line->natural - (line->natural_above + line->natural_below))/2;
765 break;
766 case GTK_BASELINE_POSITION_BOTTOM:
767 line->minimum_above += line->minimum - (line->minimum_above + line->minimum_below);
768 line->minimum_below += 0;
769 line->natural_above += line->natural - (line->natural_above + line->natural_below);
770 line->natural_below += 0;
771 break;
772 }
773 }
774 }
775 }
776
777 /* Enforce homogeneous sizes.
778 */
779 static void
gtk_grid_request_homogeneous(GtkGridRequest * request,GtkOrientation orientation)780 gtk_grid_request_homogeneous (GtkGridRequest *request,
781 GtkOrientation orientation)
782 {
783 GtkGridPrivate *priv = request->grid->priv;
784 GtkGridLineData *linedata;
785 GtkGridLines *lines;
786 gint minimum, natural;
787 gint i;
788
789 linedata = &priv->linedata[orientation];
790 lines = &request->lines[orientation];
791
792 if (!linedata->homogeneous)
793 return;
794
795 minimum = 0;
796 natural = 0;
797
798 for (i = 0; i < lines->max - lines->min; i++)
799 {
800 minimum = MAX (minimum, lines->lines[i].minimum);
801 natural = MAX (natural, lines->lines[i].natural);
802 }
803
804 for (i = 0; i < lines->max - lines->min; i++)
805 {
806 lines->lines[i].minimum = minimum;
807 lines->lines[i].natural = natural;
808 /* TODO: Do we want to adjust the baseline here too?
809 * And if so, also in the homogenous resize.
810 */
811 }
812 }
813
814 /* Deals with spanning children.
815 * Requires expand fields of lines to be set for
816 * non-spanning children.
817 */
818 static void
gtk_grid_request_spanning(GtkGridRequest * request,GtkOrientation orientation,gboolean contextual)819 gtk_grid_request_spanning (GtkGridRequest *request,
820 GtkOrientation orientation,
821 gboolean contextual)
822 {
823 GtkGridPrivate *priv = request->grid->priv;
824 GList *list;
825 GtkGridChild *child;
826 GtkGridChildAttach *attach;
827 GtkGridLineData *linedata;
828 GtkGridLines *lines;
829 GtkGridLine *line;
830 gint minimum;
831 gint natural;
832 gint span_minimum;
833 gint span_natural;
834 gint span_expand;
835 gboolean force_expand;
836 gint extra;
837 gint expand;
838 gint line_extra;
839 gint i;
840
841 linedata = &priv->linedata[orientation];
842 lines = &request->lines[orientation];
843
844 for (list = priv->children; list; list = list->next)
845 {
846 child = list->data;
847
848 if (!_gtk_widget_get_visible (child->widget))
849 continue;
850
851 attach = &child->attach[orientation];
852 if (attach->span == 1)
853 continue;
854
855 /* We ignore baselines for spanning children */
856 compute_request_for_child (request, child, orientation, contextual, &minimum, &natural, NULL, NULL);
857
858 span_minimum = (attach->span - 1) * linedata->spacing;
859 span_natural = (attach->span - 1) * linedata->spacing;
860 span_expand = 0;
861 force_expand = FALSE;
862 for (i = 0; i < attach->span; i++)
863 {
864 line = &lines->lines[attach->pos - lines->min + i];
865 span_minimum += line->minimum;
866 span_natural += line->natural;
867 if (line->expand)
868 span_expand += 1;
869 }
870 if (span_expand == 0)
871 {
872 span_expand = attach->span;
873 force_expand = TRUE;
874 }
875
876 /* If we need to request more space for this child to fill
877 * its requisition, then divide up the needed space amongst the
878 * lines it spans, favoring expandable lines if any.
879 *
880 * When doing homogeneous allocation though, try to keep the
881 * line allocations even, since we're going to force them to
882 * be the same anyway, and we don't want to introduce unnecessary
883 * extra space.
884 */
885 if (span_minimum < minimum)
886 {
887 if (linedata->homogeneous)
888 {
889 gint total, m;
890
891 total = minimum - (attach->span - 1) * linedata->spacing;
892 m = total / attach->span + (total % attach->span ? 1 : 0);
893 for (i = 0; i < attach->span; i++)
894 {
895 line = &lines->lines[attach->pos - lines->min + i];
896 line->minimum = MAX(line->minimum, m);
897 }
898 }
899 else
900 {
901 extra = minimum - span_minimum;
902 expand = span_expand;
903 for (i = 0; i < attach->span; i++)
904 {
905 line = &lines->lines[attach->pos - lines->min + i];
906 if (force_expand || line->expand)
907 {
908 line_extra = extra / expand;
909 line->minimum += line_extra;
910 extra -= line_extra;
911 expand -= 1;
912 }
913 }
914 }
915 }
916
917 if (span_natural < natural)
918 {
919 if (linedata->homogeneous)
920 {
921 gint total, n;
922
923 total = natural - (attach->span - 1) * linedata->spacing;
924 n = total / attach->span + (total % attach->span ? 1 : 0);
925 for (i = 0; i < attach->span; i++)
926 {
927 line = &lines->lines[attach->pos - lines->min + i];
928 line->natural = MAX(line->natural, n);
929 }
930 }
931 else
932 {
933 extra = natural - span_natural;
934 expand = span_expand;
935 for (i = 0; i < attach->span; i++)
936 {
937 line = &lines->lines[attach->pos - lines->min + i];
938 if (force_expand || line->expand)
939 {
940 line_extra = extra / expand;
941 line->natural += line_extra;
942 extra -= line_extra;
943 expand -= 1;
944 }
945 }
946 }
947 }
948 }
949 }
950
951 /* Marks empty and expanding lines and counts them.
952 */
953 static void
gtk_grid_request_compute_expand(GtkGridRequest * request,GtkOrientation orientation,gint min,gint max,gint * nonempty_lines,gint * expand_lines)954 gtk_grid_request_compute_expand (GtkGridRequest *request,
955 GtkOrientation orientation,
956 gint min,
957 gint max,
958 gint *nonempty_lines,
959 gint *expand_lines)
960 {
961 GtkGridPrivate *priv = request->grid->priv;
962 GtkGridChild *child;
963 GtkGridChildAttach *attach;
964 GList *list;
965 gint i;
966 GtkGridLines *lines;
967 GtkGridLine *line;
968 gboolean has_expand;
969 gint expand;
970 gint empty;
971
972 lines = &request->lines[orientation];
973
974 min = MAX (min, lines->min);
975 max = MIN (max, lines->max);
976
977 for (i = min - lines->min; i < max - lines->min; i++)
978 {
979 lines->lines[i].need_expand = FALSE;
980 lines->lines[i].expand = FALSE;
981 lines->lines[i].empty = TRUE;
982 }
983
984 for (list = priv->children; list; list = list->next)
985 {
986 child = list->data;
987
988 if (!_gtk_widget_get_visible (child->widget))
989 continue;
990
991 attach = &child->attach[orientation];
992 if (attach->span != 1)
993 continue;
994
995 if (attach->pos >= max || attach->pos < min)
996 continue;
997
998 line = &lines->lines[attach->pos - lines->min];
999 line->empty = FALSE;
1000 if (gtk_widget_compute_expand (child->widget, orientation))
1001 line->expand = TRUE;
1002 }
1003
1004 for (list = priv->children; list; list = list->next)
1005 {
1006 child = list->data;
1007
1008 if (!_gtk_widget_get_visible (child->widget))
1009 continue;
1010
1011 attach = &child->attach[orientation];
1012 if (attach->span == 1)
1013 continue;
1014
1015 has_expand = FALSE;
1016 for (i = 0; i < attach->span; i++)
1017 {
1018 line = &lines->lines[attach->pos - lines->min + i];
1019
1020 if (line->expand)
1021 has_expand = TRUE;
1022
1023 if (attach->pos + i >= max || attach->pos + 1 < min)
1024 continue;
1025
1026 line->empty = FALSE;
1027 }
1028
1029 if (!has_expand && gtk_widget_compute_expand (child->widget, orientation))
1030 {
1031 for (i = 0; i < attach->span; i++)
1032 {
1033 if (attach->pos + i >= max || attach->pos + 1 < min)
1034 continue;
1035
1036 line = &lines->lines[attach->pos - lines->min + i];
1037 line->need_expand = TRUE;
1038 }
1039 }
1040 }
1041
1042 empty = 0;
1043 expand = 0;
1044 for (i = min - lines->min; i < max - lines->min; i++)
1045 {
1046 line = &lines->lines[i];
1047
1048 if (line->need_expand)
1049 line->expand = TRUE;
1050
1051 if (line->empty)
1052 empty += 1;
1053
1054 if (line->expand)
1055 expand += 1;
1056 }
1057
1058 if (nonempty_lines)
1059 *nonempty_lines = max - min - empty;
1060
1061 if (expand_lines)
1062 *expand_lines = expand;
1063 }
1064
1065 /* Sums the minimum and natural fields of lines and their spacing.
1066 */
1067 static void
gtk_grid_request_sum(GtkGridRequest * request,GtkOrientation orientation,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)1068 gtk_grid_request_sum (GtkGridRequest *request,
1069 GtkOrientation orientation,
1070 gint *minimum,
1071 gint *natural,
1072 gint *minimum_baseline,
1073 gint *natural_baseline)
1074 {
1075 GtkGridPrivate *priv = request->grid->priv;
1076 GtkGridLineData *linedata;
1077 GtkGridLines *lines;
1078 gint i;
1079 gint min, nat;
1080 gint nonempty;
1081
1082 gtk_grid_request_compute_expand (request, orientation, G_MININT, G_MAXINT, &nonempty, NULL);
1083
1084 linedata = &priv->linedata[orientation];
1085 lines = &request->lines[orientation];
1086
1087 min = 0;
1088 nat = 0;
1089 for (i = 0; i < lines->max - lines->min; i++)
1090 {
1091 if (orientation == GTK_ORIENTATION_VERTICAL &&
1092 lines->min + i == priv->baseline_row &&
1093 lines->lines[i].minimum_above != -1)
1094 {
1095 if (minimum_baseline)
1096 *minimum_baseline = min + lines->lines[i].minimum_above;
1097 if (natural_baseline)
1098 *natural_baseline = nat + lines->lines[i].natural_above;
1099 }
1100
1101 min += lines->lines[i].minimum;
1102 nat += lines->lines[i].natural;
1103
1104 if (!lines->lines[i].empty)
1105 {
1106 min += linedata->spacing;
1107 nat += linedata->spacing;
1108 }
1109 }
1110
1111 /* Remove last spacing, if any was applied */
1112 if (nonempty > 0)
1113 {
1114 min -= linedata->spacing;
1115 nat -= linedata->spacing;
1116 }
1117
1118 *minimum = min;
1119 *natural = nat;
1120 }
1121
1122 /* Computes minimum and natural fields of lines.
1123 * When contextual is TRUE, requires allocation of
1124 * lines in the opposite orientation to be set.
1125 */
1126 static void
gtk_grid_request_run(GtkGridRequest * request,GtkOrientation orientation,gboolean contextual)1127 gtk_grid_request_run (GtkGridRequest *request,
1128 GtkOrientation orientation,
1129 gboolean contextual)
1130 {
1131 gtk_grid_request_init (request, orientation);
1132 gtk_grid_request_non_spanning (request, orientation, contextual);
1133 gtk_grid_request_homogeneous (request, orientation);
1134 gtk_grid_request_spanning (request, orientation, contextual);
1135 gtk_grid_request_homogeneous (request, orientation);
1136 }
1137
1138 static void
gtk_grid_distribute_non_homogeneous(GtkGridLines * lines,gint nonempty,gint expand,gint size,gint min,gint max)1139 gtk_grid_distribute_non_homogeneous (GtkGridLines *lines,
1140 gint nonempty,
1141 gint expand,
1142 gint size,
1143 gint min,
1144 gint max)
1145 {
1146 GtkRequestedSize *sizes;
1147 GtkGridLine *line;
1148 gint extra;
1149 gint rest;
1150 int i, j;
1151
1152 if (nonempty == 0)
1153 return;
1154
1155 sizes = g_newa (GtkRequestedSize, nonempty);
1156
1157 j = 0;
1158 for (i = min - lines->min; i < max - lines->min; i++)
1159 {
1160 line = &lines->lines[i];
1161 if (line->empty)
1162 continue;
1163
1164 size -= line->minimum;
1165
1166 sizes[j].minimum_size = line->minimum;
1167 sizes[j].natural_size = line->natural;
1168 sizes[j].data = line;
1169 j++;
1170 }
1171
1172 size = gtk_distribute_natural_allocation (MAX (0, size), nonempty, sizes);
1173
1174 if (expand > 0)
1175 {
1176 extra = size / expand;
1177 rest = size % expand;
1178 }
1179 else
1180 {
1181 extra = 0;
1182 rest = 0;
1183 }
1184
1185 j = 0;
1186 for (i = min - lines->min; i < max - lines->min; i++)
1187 {
1188 line = &lines->lines[i];
1189 if (line->empty)
1190 continue;
1191
1192 g_assert (line == sizes[j].data);
1193
1194 line->allocation = sizes[j].minimum_size;
1195 if (line->expand)
1196 {
1197 line->allocation += extra;
1198 if (rest > 0)
1199 {
1200 line->allocation += 1;
1201 rest -= 1;
1202 }
1203 }
1204
1205 j++;
1206 }
1207 }
1208
1209 /* Requires that the minimum and natural fields of lines
1210 * have been set, computes the allocation field of lines
1211 * by distributing total_size among lines.
1212 */
1213 static void
gtk_grid_request_allocate(GtkGridRequest * request,GtkOrientation orientation,gint total_size)1214 gtk_grid_request_allocate (GtkGridRequest *request,
1215 GtkOrientation orientation,
1216 gint total_size)
1217 {
1218 GtkGridPrivate *priv = request->grid->priv;
1219 GtkGridLineData *linedata;
1220 GtkGridLines *lines;
1221 GtkGridLine *line;
1222 gint nonempty1, nonempty2;
1223 gint expand1, expand2;
1224 gint i;
1225 GtkBaselinePosition baseline_pos;
1226 gint baseline;
1227 gint extra, extra2;
1228 gint rest;
1229 gint size1, size2;
1230 gint split, split_pos;
1231
1232 linedata = &priv->linedata[orientation];
1233 lines = &request->lines[orientation];
1234
1235 baseline = gtk_widget_get_allocated_baseline (GTK_WIDGET (request->grid));
1236
1237 if (orientation == GTK_ORIENTATION_VERTICAL && baseline != -1 &&
1238 priv->baseline_row >= lines->min && priv->baseline_row < lines->max &&
1239 lines->lines[priv->baseline_row - lines->min].minimum_above != -1)
1240 {
1241 split = priv->baseline_row;
1242 split_pos = baseline - lines->lines[priv->baseline_row - lines->min].minimum_above;
1243 gtk_grid_request_compute_expand (request, orientation, lines->min, split, &nonempty1, &expand1);
1244 gtk_grid_request_compute_expand (request, orientation, split, lines->max, &nonempty2, &expand2);
1245
1246 if (nonempty2 > 0)
1247 {
1248 size1 = split_pos - (nonempty1) * linedata->spacing;
1249 size2 = (total_size - split_pos) - (nonempty2 - 1) * linedata->spacing;
1250 }
1251 else
1252 {
1253 size1 = total_size - (nonempty1 - 1) * linedata->spacing;
1254 size2 = 0;
1255 }
1256 }
1257 else
1258 {
1259 gtk_grid_request_compute_expand (request, orientation, lines->min, lines->max, &nonempty1, &expand1);
1260 nonempty2 = expand2 = 0;
1261 split = lines->max;
1262
1263 size1 = total_size - (nonempty1 - 1) * linedata->spacing;
1264 size2 = 0;
1265 }
1266
1267 if (nonempty1 == 0 && nonempty2 == 0)
1268 return;
1269
1270 if (linedata->homogeneous)
1271 {
1272 if (nonempty1 > 0)
1273 {
1274 extra = size1 / nonempty1;
1275 rest = size1 % nonempty1;
1276 }
1277 else
1278 {
1279 extra = 0;
1280 rest = 0;
1281 }
1282 if (nonempty2 > 0)
1283 {
1284 extra2 = size2 / nonempty2;
1285 if (extra2 < extra || nonempty1 == 0)
1286 {
1287 extra = extra2;
1288 rest = size2 % nonempty2;
1289 }
1290 }
1291
1292 for (i = 0; i < lines->max - lines->min; i++)
1293 {
1294 line = &lines->lines[i];
1295 if (line->empty)
1296 continue;
1297
1298 line->allocation = extra;
1299 if (rest > 0)
1300 {
1301 line->allocation += 1;
1302 rest -= 1;
1303 }
1304 }
1305 }
1306 else
1307 {
1308 gtk_grid_distribute_non_homogeneous (lines,
1309 nonempty1,
1310 expand1,
1311 size1,
1312 lines->min,
1313 split);
1314 gtk_grid_distribute_non_homogeneous (lines,
1315 nonempty2,
1316 expand2,
1317 size2,
1318 split,
1319 lines->max);
1320 }
1321
1322 for (i = 0; i < lines->max - lines->min; i++)
1323 {
1324 line = &lines->lines[i];
1325 if (line->empty)
1326 continue;
1327
1328 if (line->minimum_above != -1)
1329 {
1330 /* Note: This is overridden in gtk_grid_request_position for the allocated baseline */
1331 baseline_pos = gtk_grid_get_row_baseline_position (request->grid, i + lines->min);
1332
1333 switch (baseline_pos)
1334 {
1335 case GTK_BASELINE_POSITION_TOP:
1336 line->allocated_baseline =
1337 line->minimum_above;
1338 break;
1339 case GTK_BASELINE_POSITION_CENTER:
1340 line->allocated_baseline =
1341 line->minimum_above +
1342 (line->allocation - (line->minimum_above + line->minimum_below)) / 2;
1343 break;
1344 case GTK_BASELINE_POSITION_BOTTOM:
1345 line->allocated_baseline =
1346 line->allocation - line->minimum_below;
1347 break;
1348 }
1349 }
1350 else
1351 line->allocated_baseline = -1;
1352 }
1353 }
1354
1355 /* Computes the position fields from allocation and spacing.
1356 */
1357 static void
gtk_grid_request_position(GtkGridRequest * request,GtkOrientation orientation)1358 gtk_grid_request_position (GtkGridRequest *request,
1359 GtkOrientation orientation)
1360 {
1361 GtkGridPrivate *priv = request->grid->priv;
1362 GtkGridLineData *linedata;
1363 GtkGridLines *lines;
1364 GtkGridLine *line;
1365 gint position, old_position;
1366 int allocated_baseline;
1367 gint i, j;
1368
1369 linedata = &priv->linedata[orientation];
1370 lines = &request->lines[orientation];
1371
1372 allocated_baseline = gtk_widget_get_allocated_baseline (GTK_WIDGET(request->grid));
1373
1374 position = 0;
1375 for (i = 0; i < lines->max - lines->min; i++)
1376 {
1377 line = &lines->lines[i];
1378
1379 if (orientation == GTK_ORIENTATION_VERTICAL &&
1380 i + lines->min == priv->baseline_row &&
1381 allocated_baseline != -1 &&
1382 lines->lines[i].minimum_above != -1)
1383 {
1384 old_position = position;
1385 position = allocated_baseline - line->minimum_above;
1386
1387 /* Back-patch previous rows */
1388 for (j = 0; j < i; j++)
1389 {
1390 if (!lines->lines[j].empty)
1391 lines->lines[j].position += position - old_position;
1392 }
1393 }
1394
1395 if (!line->empty)
1396 {
1397 line->position = position;
1398 position += line->allocation + linedata->spacing;
1399
1400 if (orientation == GTK_ORIENTATION_VERTICAL &&
1401 i + lines->min == priv->baseline_row &&
1402 allocated_baseline != -1 &&
1403 lines->lines[i].minimum_above != -1)
1404 line->allocated_baseline = allocated_baseline - line->position;
1405 }
1406 }
1407 }
1408
1409 static void
gtk_grid_get_size(GtkGrid * grid,GtkOrientation orientation,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)1410 gtk_grid_get_size (GtkGrid *grid,
1411 GtkOrientation orientation,
1412 gint *minimum,
1413 gint *natural,
1414 gint *minimum_baseline,
1415 gint *natural_baseline)
1416 {
1417 GtkGridRequest request;
1418 GtkGridLines *lines;
1419
1420 *minimum = 0;
1421 *natural = 0;
1422
1423 if (minimum_baseline)
1424 *minimum_baseline = -1;
1425
1426 if (natural_baseline)
1427 *natural_baseline = -1;
1428
1429 if (grid->priv->children == NULL)
1430 return;
1431
1432 request.grid = grid;
1433 gtk_grid_request_count_lines (&request);
1434 lines = &request.lines[orientation];
1435 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1436 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1437
1438 gtk_grid_request_run (&request, orientation, FALSE);
1439 gtk_grid_request_sum (&request, orientation, minimum, natural,
1440 minimum_baseline, natural_baseline);
1441 }
1442
1443 static void
gtk_grid_get_size_for_size(GtkGrid * grid,GtkOrientation orientation,gint size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)1444 gtk_grid_get_size_for_size (GtkGrid *grid,
1445 GtkOrientation orientation,
1446 gint size,
1447 gint *minimum,
1448 gint *natural,
1449 gint *minimum_baseline,
1450 gint *natural_baseline)
1451 {
1452 GtkGridRequest request;
1453 GtkGridLines *lines;
1454 gint min_size, nat_size;
1455
1456 *minimum = 0;
1457 *natural = 0;
1458
1459 if (minimum_baseline)
1460 *minimum_baseline = -1;
1461
1462 if (natural_baseline)
1463 *natural_baseline = -1;
1464
1465 if (grid->priv->children == NULL)
1466 return;
1467
1468 request.grid = grid;
1469 gtk_grid_request_count_lines (&request);
1470 lines = &request.lines[0];
1471 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1472 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1473 lines = &request.lines[1];
1474 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1475 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1476
1477 gtk_grid_request_run (&request, 1 - orientation, FALSE);
1478 gtk_grid_request_sum (&request, 1 - orientation, &min_size, &nat_size, NULL, NULL);
1479 gtk_grid_request_allocate (&request, 1 - orientation, MAX (size, min_size));
1480
1481 gtk_grid_request_run (&request, orientation, TRUE);
1482 gtk_grid_request_sum (&request, orientation, minimum, natural, minimum_baseline, natural_baseline);
1483 }
1484
1485 static void
gtk_grid_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)1486 gtk_grid_get_preferred_width (GtkWidget *widget,
1487 gint *minimum,
1488 gint *natural)
1489 {
1490 gtk_css_gadget_get_preferred_size (GTK_GRID (widget)->priv->gadget,
1491 GTK_ORIENTATION_HORIZONTAL,
1492 -1,
1493 minimum, natural,
1494 NULL, NULL);
1495 }
1496
1497 static void
gtk_grid_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)1498 gtk_grid_get_preferred_height (GtkWidget *widget,
1499 gint *minimum,
1500 gint *natural)
1501 {
1502 gtk_css_gadget_get_preferred_size (GTK_GRID (widget)->priv->gadget,
1503 GTK_ORIENTATION_VERTICAL,
1504 -1,
1505 minimum, natural,
1506 NULL, NULL);
1507 }
1508
1509 static void
gtk_grid_get_preferred_width_for_height(GtkWidget * widget,gint height,gint * minimum,gint * natural)1510 gtk_grid_get_preferred_width_for_height (GtkWidget *widget,
1511 gint height,
1512 gint *minimum,
1513 gint *natural)
1514 {
1515 gtk_css_gadget_get_preferred_size (GTK_GRID (widget)->priv->gadget,
1516 GTK_ORIENTATION_HORIZONTAL,
1517 height,
1518 minimum, natural,
1519 NULL, NULL);
1520 }
1521
1522 static void
gtk_grid_get_preferred_height_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural)1523 gtk_grid_get_preferred_height_for_width (GtkWidget *widget,
1524 gint width,
1525 gint *minimum,
1526 gint *natural)
1527 {
1528 gtk_css_gadget_get_preferred_size (GTK_GRID (widget)->priv->gadget,
1529 GTK_ORIENTATION_VERTICAL,
1530 width,
1531 minimum, natural,
1532 NULL, NULL);
1533 }
1534
1535 static void
gtk_grid_get_preferred_height_and_baseline_for_width(GtkWidget * widget,gint width,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)1536 gtk_grid_get_preferred_height_and_baseline_for_width (GtkWidget *widget,
1537 gint width,
1538 gint *minimum,
1539 gint *natural,
1540 gint *minimum_baseline,
1541 gint *natural_baseline)
1542 {
1543 gtk_css_gadget_get_preferred_size (GTK_GRID (widget)->priv->gadget,
1544 GTK_ORIENTATION_VERTICAL,
1545 width,
1546 minimum, natural,
1547 minimum_baseline, natural_baseline);
1548 }
1549
1550 static void
gtk_grid_measure(GtkCssGadget * gadget,GtkOrientation orientation,int for_size,int * minimum,int * natural,int * minimum_baseline,int * natural_baseline,gpointer data)1551 gtk_grid_measure (GtkCssGadget *gadget,
1552 GtkOrientation orientation,
1553 int for_size,
1554 int *minimum,
1555 int *natural,
1556 int *minimum_baseline,
1557 int *natural_baseline,
1558 gpointer data)
1559 {
1560 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
1561 GtkGrid *grid = GTK_GRID (widget);
1562
1563 if ((orientation == GTK_ORIENTATION_HORIZONTAL &&
1564 gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT) ||
1565 (orientation == GTK_ORIENTATION_VERTICAL &&
1566 gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH))
1567 gtk_grid_get_size_for_size (grid, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
1568 else
1569 gtk_grid_get_size (grid, orientation, minimum, natural, minimum_baseline, natural_baseline);
1570 }
1571
1572 static void
allocate_child(GtkGridRequest * request,GtkOrientation orientation,GtkGridChild * child,gint * position,gint * size,gint * baseline)1573 allocate_child (GtkGridRequest *request,
1574 GtkOrientation orientation,
1575 GtkGridChild *child,
1576 gint *position,
1577 gint *size,
1578 gint *baseline)
1579 {
1580 GtkGridPrivate *priv = request->grid->priv;
1581 GtkGridLineData *linedata;
1582 GtkGridLines *lines;
1583 GtkGridLine *line;
1584 GtkGridChildAttach *attach;
1585 gint i;
1586
1587 linedata = &priv->linedata[orientation];
1588 lines = &request->lines[orientation];
1589 attach = &child->attach[orientation];
1590
1591 *position = lines->lines[attach->pos - lines->min].position;
1592 if (attach->span == 1)
1593 *baseline = lines->lines[attach->pos - lines->min].allocated_baseline;
1594 else
1595 *baseline = -1;
1596
1597 *size = (attach->span - 1) * linedata->spacing;
1598 for (i = 0; i < attach->span; i++)
1599 {
1600 line = &lines->lines[attach->pos - lines->min + i];
1601 *size += line->allocation;
1602 }
1603 }
1604
1605 static void
gtk_grid_request_allocate_children(GtkGridRequest * request,const GtkAllocation * allocation)1606 gtk_grid_request_allocate_children (GtkGridRequest *request,
1607 const GtkAllocation *allocation)
1608 {
1609 GtkGridPrivate *priv = request->grid->priv;
1610 GList *list;
1611 GtkGridChild *child;
1612 GtkAllocation child_allocation;
1613 gint x, y, width, height, baseline, ignore;
1614
1615 for (list = priv->children; list; list = list->next)
1616 {
1617 child = list->data;
1618
1619 if (!_gtk_widget_get_visible (child->widget))
1620 continue;
1621
1622 allocate_child (request, GTK_ORIENTATION_HORIZONTAL, child, &x, &width, &ignore);
1623 allocate_child (request, GTK_ORIENTATION_VERTICAL, child, &y, &height, &baseline);
1624
1625 child_allocation.x = allocation->x + x;
1626 child_allocation.y = allocation->y + y;
1627 child_allocation.width = MAX (1, width);
1628 child_allocation.height = MAX (1, height);
1629
1630 if (gtk_widget_get_direction (GTK_WIDGET (request->grid)) == GTK_TEXT_DIR_RTL)
1631 child_allocation.x = allocation->x + allocation->width
1632 - (child_allocation.x - allocation->x) - child_allocation.width;
1633
1634 gtk_widget_size_allocate_with_baseline (child->widget, &child_allocation, baseline);
1635 }
1636 }
1637
1638 #define GET_SIZE(allocation, orientation) (orientation == GTK_ORIENTATION_HORIZONTAL ? allocation->width : allocation->height)
1639
1640 static void
gtk_grid_size_allocate(GtkWidget * widget,GtkAllocation * allocation)1641 gtk_grid_size_allocate (GtkWidget *widget,
1642 GtkAllocation *allocation)
1643 {
1644 GtkAllocation clip;
1645
1646 gtk_widget_set_allocation (widget, allocation);
1647
1648 gtk_css_gadget_allocate (GTK_GRID (widget)->priv->gadget,
1649 allocation,
1650 gtk_widget_get_allocated_baseline (widget),
1651 &clip);
1652
1653 gtk_widget_set_clip (widget, &clip);
1654 }
1655
1656 static void
gtk_grid_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip,gpointer data)1657 gtk_grid_allocate (GtkCssGadget *gadget,
1658 const GtkAllocation *allocation,
1659 int baseline,
1660 GtkAllocation *out_clip,
1661 gpointer data)
1662 {
1663 GtkWidget *widget = gtk_css_gadget_get_owner (gadget);
1664 GtkGrid *grid = GTK_GRID (widget);
1665 GtkGridPrivate *priv = grid->priv;
1666 GtkGridRequest request;
1667 GtkGridLines *lines;
1668 GtkOrientation orientation;
1669
1670 if (priv->children == NULL)
1671 return;
1672
1673 request.grid = grid;
1674
1675 gtk_grid_request_count_lines (&request);
1676 lines = &request.lines[0];
1677 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1678 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1679 lines = &request.lines[1];
1680 lines->lines = g_newa (GtkGridLine, lines->max - lines->min);
1681 memset (lines->lines, 0, (lines->max - lines->min) * sizeof (GtkGridLine));
1682
1683 if (gtk_widget_get_request_mode (widget) == GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT)
1684 orientation = GTK_ORIENTATION_HORIZONTAL;
1685 else
1686 orientation = GTK_ORIENTATION_VERTICAL;
1687
1688 gtk_grid_request_run (&request, 1 - orientation, FALSE);
1689 gtk_grid_request_allocate (&request, 1 - orientation, GET_SIZE (allocation, 1 - orientation));
1690 gtk_grid_request_run (&request, orientation, TRUE);
1691
1692 gtk_grid_request_allocate (&request, orientation, GET_SIZE (allocation, orientation));
1693
1694 gtk_grid_request_position (&request, 0);
1695 gtk_grid_request_position (&request, 1);
1696
1697 gtk_grid_request_allocate_children (&request, allocation);
1698
1699 gtk_container_get_children_clip (GTK_CONTAINER (grid), out_clip);
1700 }
1701
1702 static gboolean
gtk_grid_render(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height,gpointer data)1703 gtk_grid_render (GtkCssGadget *gadget,
1704 cairo_t *cr,
1705 int x,
1706 int y,
1707 int width,
1708 int height,
1709 gpointer data)
1710 {
1711 GTK_WIDGET_CLASS (gtk_grid_parent_class)->draw (gtk_css_gadget_get_owner (gadget), cr);
1712
1713 return FALSE;
1714 }
1715
1716 static gboolean
gtk_grid_draw(GtkWidget * widget,cairo_t * cr)1717 gtk_grid_draw (GtkWidget *widget,
1718 cairo_t *cr)
1719 {
1720 gtk_css_gadget_draw (GTK_GRID (widget)->priv->gadget, cr);
1721
1722 return FALSE;
1723 }
1724
1725 static void
gtk_grid_class_init(GtkGridClass * class)1726 gtk_grid_class_init (GtkGridClass *class)
1727 {
1728 GObjectClass *object_class = G_OBJECT_CLASS (class);
1729 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
1730 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
1731
1732 object_class->get_property = gtk_grid_get_property;
1733 object_class->set_property = gtk_grid_set_property;
1734 object_class->finalize = gtk_grid_finalize;
1735
1736 widget_class->size_allocate = gtk_grid_size_allocate;
1737 widget_class->get_preferred_width = gtk_grid_get_preferred_width;
1738 widget_class->get_preferred_height = gtk_grid_get_preferred_height;
1739 widget_class->get_preferred_width_for_height = gtk_grid_get_preferred_width_for_height;
1740 widget_class->get_preferred_height_for_width = gtk_grid_get_preferred_height_for_width;
1741 widget_class->get_preferred_height_and_baseline_for_width = gtk_grid_get_preferred_height_and_baseline_for_width;
1742 widget_class->draw = gtk_grid_draw;
1743
1744 container_class->add = gtk_grid_add;
1745 container_class->remove = gtk_grid_remove;
1746 container_class->forall = gtk_grid_forall;
1747 container_class->child_type = gtk_grid_child_type;
1748 container_class->set_child_property = gtk_grid_set_child_property;
1749 container_class->get_child_property = gtk_grid_get_child_property;
1750 gtk_container_class_handle_border_width (container_class);
1751
1752 g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation");
1753
1754 obj_properties[PROP_ROW_SPACING] =
1755 g_param_spec_int ("row-spacing",
1756 P_("Row spacing"),
1757 P_("The amount of space between two consecutive rows"),
1758 0, G_MAXINT16, 0,
1759 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1760
1761 obj_properties[PROP_COLUMN_SPACING] =
1762 g_param_spec_int ("column-spacing",
1763 P_("Column spacing"),
1764 P_("The amount of space between two consecutive columns"),
1765 0, G_MAXINT16, 0,
1766 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1767
1768 obj_properties[PROP_ROW_HOMOGENEOUS] =
1769 g_param_spec_boolean ("row-homogeneous",
1770 P_("Row Homogeneous"),
1771 P_("If TRUE, the rows are all the same height"),
1772 FALSE,
1773 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1774
1775 obj_properties[PROP_COLUMN_HOMOGENEOUS] =
1776 g_param_spec_boolean ("column-homogeneous",
1777 P_("Column Homogeneous"),
1778 P_("If TRUE, the columns are all the same width"),
1779 FALSE,
1780 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1781
1782 obj_properties[PROP_BASELINE_ROW] =
1783 g_param_spec_int ("baseline-row",
1784 P_("Baseline Row"),
1785 P_("The row to align the to the baseline when valign is GTK_ALIGN_BASELINE"),
1786 0, G_MAXINT, 0,
1787 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
1788
1789 g_object_class_install_properties (object_class,
1790 N_PROPERTIES,
1791 obj_properties);
1792
1793 child_properties[CHILD_PROP_LEFT_ATTACH] =
1794 g_param_spec_int ("left-attach",
1795 P_("Left attachment"),
1796 P_("The column number to attach the left side of the child to"),
1797 G_MININT, G_MAXINT, 0,
1798 GTK_PARAM_READWRITE);
1799
1800 child_properties[CHILD_PROP_TOP_ATTACH] =
1801 g_param_spec_int ("top-attach",
1802 P_("Top attachment"),
1803 P_("The row number to attach the top side of a child widget to"),
1804 G_MININT, G_MAXINT, 0,
1805 GTK_PARAM_READWRITE);
1806
1807 child_properties[CHILD_PROP_WIDTH] =
1808 g_param_spec_int ("width",
1809 P_("Width"),
1810 P_("The number of columns that a child spans"),
1811 1, G_MAXINT, 1,
1812 GTK_PARAM_READWRITE);
1813
1814 child_properties[CHILD_PROP_HEIGHT] =
1815 g_param_spec_int ("height",
1816 P_("Height"),
1817 P_("The number of rows that a child spans"),
1818 1, G_MAXINT, 1,
1819 GTK_PARAM_READWRITE);
1820
1821 gtk_container_class_install_child_properties (container_class, N_CHILD_PROPERTIES, child_properties);
1822 gtk_widget_class_set_css_name (widget_class, "grid");
1823 }
1824
1825 static void
gtk_grid_init(GtkGrid * grid)1826 gtk_grid_init (GtkGrid *grid)
1827 {
1828 GtkGridPrivate *priv;
1829
1830 grid->priv = gtk_grid_get_instance_private (grid);
1831 priv = grid->priv;
1832
1833 gtk_widget_set_has_window (GTK_WIDGET (grid), FALSE);
1834
1835 priv->children = NULL;
1836 priv->orientation = GTK_ORIENTATION_HORIZONTAL;
1837 priv->baseline_row = 0;
1838
1839 priv->linedata[0].spacing = 0;
1840 priv->linedata[1].spacing = 0;
1841
1842 priv->linedata[0].homogeneous = FALSE;
1843 priv->linedata[1].homogeneous = FALSE;
1844
1845 priv->gadget = gtk_css_custom_gadget_new_for_node (gtk_widget_get_css_node (GTK_WIDGET (grid)),
1846 GTK_WIDGET (grid),
1847 gtk_grid_measure,
1848 gtk_grid_allocate,
1849 gtk_grid_render,
1850 NULL,
1851 NULL);
1852
1853
1854 _gtk_orientable_set_style_classes (GTK_ORIENTABLE (grid));
1855 }
1856
1857 /**
1858 * gtk_grid_new:
1859 *
1860 * Creates a new grid widget.
1861 *
1862 * Returns: the new #GtkGrid
1863 */
1864 GtkWidget *
gtk_grid_new(void)1865 gtk_grid_new (void)
1866 {
1867 return g_object_new (GTK_TYPE_GRID, NULL);
1868 }
1869
1870 /**
1871 * gtk_grid_attach:
1872 * @grid: a #GtkGrid
1873 * @child: the widget to add
1874 * @left: the column number to attach the left side of @child to
1875 * @top: the row number to attach the top side of @child to
1876 * @width: the number of columns that @child will span
1877 * @height: the number of rows that @child will span
1878 *
1879 * Adds a widget to the grid.
1880 *
1881 * The position of @child is determined by @left and @top. The
1882 * number of “cells” that @child will occupy is determined by
1883 * @width and @height.
1884 */
1885 void
gtk_grid_attach(GtkGrid * grid,GtkWidget * child,gint left,gint top,gint width,gint height)1886 gtk_grid_attach (GtkGrid *grid,
1887 GtkWidget *child,
1888 gint left,
1889 gint top,
1890 gint width,
1891 gint height)
1892 {
1893 g_return_if_fail (GTK_IS_GRID (grid));
1894 g_return_if_fail (GTK_IS_WIDGET (child));
1895 g_return_if_fail (_gtk_widget_get_parent (child) == NULL);
1896 g_return_if_fail (width > 0);
1897 g_return_if_fail (height > 0);
1898
1899 grid_attach (grid, child, left, top, width, height);
1900 }
1901
1902 /**
1903 * gtk_grid_attach_next_to:
1904 * @grid: a #GtkGrid
1905 * @child: the widget to add
1906 * @sibling: (allow-none): the child of @grid that @child will be placed
1907 * next to, or %NULL to place @child at the beginning or end
1908 * @side: the side of @sibling that @child is positioned next to
1909 * @width: the number of columns that @child will span
1910 * @height: the number of rows that @child will span
1911 *
1912 * Adds a widget to the grid.
1913 *
1914 * The widget is placed next to @sibling, on the side determined by
1915 * @side. When @sibling is %NULL, the widget is placed in row (for
1916 * left or right placement) or column 0 (for top or bottom placement),
1917 * at the end indicated by @side.
1918 *
1919 * Attaching widgets labeled [1], [2], [3] with @sibling == %NULL and
1920 * @side == %GTK_POS_LEFT yields a layout of [3][2][1].
1921 */
1922 void
gtk_grid_attach_next_to(GtkGrid * grid,GtkWidget * child,GtkWidget * sibling,GtkPositionType side,gint width,gint height)1923 gtk_grid_attach_next_to (GtkGrid *grid,
1924 GtkWidget *child,
1925 GtkWidget *sibling,
1926 GtkPositionType side,
1927 gint width,
1928 gint height)
1929 {
1930 GtkGridChild *grid_sibling;
1931 gint left, top;
1932
1933 g_return_if_fail (GTK_IS_GRID (grid));
1934 g_return_if_fail (GTK_IS_WIDGET (child));
1935 g_return_if_fail (_gtk_widget_get_parent (child) == NULL);
1936 g_return_if_fail (sibling == NULL || _gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
1937 g_return_if_fail (width > 0);
1938 g_return_if_fail (height > 0);
1939
1940 if (sibling)
1941 {
1942 grid_sibling = find_grid_child (grid, sibling);
1943
1944 switch (side)
1945 {
1946 case GTK_POS_LEFT:
1947 left = CHILD_LEFT (grid_sibling) - width;
1948 top = CHILD_TOP (grid_sibling);
1949 break;
1950 case GTK_POS_RIGHT:
1951 left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling);
1952 top = CHILD_TOP (grid_sibling);
1953 break;
1954 case GTK_POS_TOP:
1955 left = CHILD_LEFT (grid_sibling);
1956 top = CHILD_TOP (grid_sibling) - height;
1957 break;
1958 case GTK_POS_BOTTOM:
1959 left = CHILD_LEFT (grid_sibling);
1960 top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling);
1961 break;
1962 default:
1963 g_assert_not_reached ();
1964 }
1965 }
1966 else
1967 {
1968 switch (side)
1969 {
1970 case GTK_POS_LEFT:
1971 left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, FALSE);
1972 left -= width;
1973 top = 0;
1974 break;
1975 case GTK_POS_RIGHT:
1976 left = find_attach_position (grid, GTK_ORIENTATION_HORIZONTAL, 0, height, TRUE);
1977 top = 0;
1978 break;
1979 case GTK_POS_TOP:
1980 left = 0;
1981 top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, FALSE);
1982 top -= height;
1983 break;
1984 case GTK_POS_BOTTOM:
1985 left = 0;
1986 top = find_attach_position (grid, GTK_ORIENTATION_VERTICAL, 0, width, TRUE);
1987 break;
1988 default:
1989 g_assert_not_reached ();
1990 }
1991 }
1992
1993 grid_attach (grid, child, left, top, width, height);
1994 }
1995
1996 /**
1997 * gtk_grid_get_child_at:
1998 * @grid: a #GtkGrid
1999 * @left: the left edge of the cell
2000 * @top: the top edge of the cell
2001 *
2002 * Gets the child of @grid whose area covers the grid
2003 * cell whose upper left corner is at @left, @top.
2004 *
2005 * Returns: (transfer none) (nullable): the child at the given position, or %NULL
2006 *
2007 * Since: 3.2
2008 */
2009 GtkWidget *
gtk_grid_get_child_at(GtkGrid * grid,gint left,gint top)2010 gtk_grid_get_child_at (GtkGrid *grid,
2011 gint left,
2012 gint top)
2013 {
2014 GtkGridPrivate *priv;
2015 GtkGridChild *child;
2016 GList *list;
2017
2018 g_return_val_if_fail (GTK_IS_GRID (grid), NULL);
2019
2020 priv = grid->priv;
2021
2022 for (list = priv->children; list; list = list->next)
2023 {
2024 child = list->data;
2025
2026 if (CHILD_LEFT (child) <= left &&
2027 CHILD_LEFT (child) + CHILD_WIDTH (child) > left &&
2028 CHILD_TOP (child) <= top &&
2029 CHILD_TOP (child) + CHILD_HEIGHT (child) > top)
2030 return child->widget;
2031 }
2032
2033 return NULL;
2034 }
2035
2036 /**
2037 * gtk_grid_insert_row:
2038 * @grid: a #GtkGrid
2039 * @position: the position to insert the row at
2040 *
2041 * Inserts a row at the specified position.
2042 *
2043 * Children which are attached at or below this position
2044 * are moved one row down. Children which span across this
2045 * position are grown to span the new row.
2046 *
2047 * Since: 3.2
2048 */
2049 void
gtk_grid_insert_row(GtkGrid * grid,gint position)2050 gtk_grid_insert_row (GtkGrid *grid,
2051 gint position)
2052 {
2053 GtkGridPrivate *priv;
2054 GtkGridChild *child;
2055 GList *list;
2056 gint top, height;
2057
2058 g_return_if_fail (GTK_IS_GRID (grid));
2059
2060 priv = grid->priv;
2061
2062 for (list = priv->children; list; list = list->next)
2063 {
2064 child = list->data;
2065
2066 top = CHILD_TOP (child);
2067 height = CHILD_HEIGHT (child);
2068
2069 if (top >= position)
2070 {
2071 CHILD_TOP (child) = top + 1;
2072 gtk_container_child_notify_by_pspec (GTK_CONTAINER (grid),
2073 child->widget,
2074 child_properties[CHILD_PROP_TOP_ATTACH]);
2075 }
2076 else if (top + height > position)
2077 {
2078 CHILD_HEIGHT (child) = height + 1;
2079 gtk_container_child_notify_by_pspec (GTK_CONTAINER (grid),
2080 child->widget,
2081 child_properties[CHILD_PROP_HEIGHT]);
2082 }
2083 }
2084
2085 for (list = priv->row_properties; list != NULL; list = list->next)
2086 {
2087 GtkGridRowProperties *prop = list->data;
2088
2089 if (prop->row >= position)
2090 prop->row += 1;
2091 }
2092 }
2093
2094 /**
2095 * gtk_grid_remove_row:
2096 * @grid: a #GtkGrid
2097 * @position: the position of the row to remove
2098 *
2099 * Removes a row from the grid.
2100 *
2101 * Children that are placed in this row are removed,
2102 * spanning children that overlap this row have their
2103 * height reduced by one, and children below the row
2104 * are moved up.
2105 *
2106 * Since: 3.10
2107 */
2108 void
gtk_grid_remove_row(GtkGrid * grid,gint position)2109 gtk_grid_remove_row (GtkGrid *grid,
2110 gint position)
2111 {
2112 GtkGridPrivate *priv;
2113 GtkGridChild *child;
2114 GList *list;
2115 gint top, height;
2116
2117 g_return_if_fail (GTK_IS_GRID (grid));
2118
2119 priv = grid->priv;
2120
2121 list = priv->children;
2122 while (list)
2123 {
2124 child = list->data;
2125 list = list->next;
2126
2127 top = CHILD_TOP (child);
2128 height = CHILD_HEIGHT (child);
2129
2130 if (top <= position && top + height > position)
2131 height--;
2132 if (top > position)
2133 top--;
2134
2135 if (height <= 0)
2136 gtk_container_remove (GTK_CONTAINER (grid), child->widget);
2137 else
2138 gtk_container_child_set (GTK_CONTAINER (grid), child->widget,
2139 "height", height,
2140 "top-attach", top,
2141 NULL);
2142 }
2143 }
2144
2145 /**
2146 * gtk_grid_insert_column:
2147 * @grid: a #GtkGrid
2148 * @position: the position to insert the column at
2149 *
2150 * Inserts a column at the specified position.
2151 *
2152 * Children which are attached at or to the right of this position
2153 * are moved one column to the right. Children which span across this
2154 * position are grown to span the new column.
2155 *
2156 * Since: 3.2
2157 */
2158 void
gtk_grid_insert_column(GtkGrid * grid,gint position)2159 gtk_grid_insert_column (GtkGrid *grid,
2160 gint position)
2161 {
2162 GtkGridPrivate *priv;
2163 GtkGridChild *child;
2164 GList *list;
2165 gint left, width;
2166
2167 g_return_if_fail (GTK_IS_GRID (grid));
2168
2169 priv = grid->priv;
2170
2171 for (list = priv->children; list; list = list->next)
2172 {
2173 child = list->data;
2174
2175 left = CHILD_LEFT (child);
2176 width = CHILD_WIDTH (child);
2177
2178 if (left >= position)
2179 {
2180 CHILD_LEFT (child) = left + 1;
2181 gtk_container_child_notify_by_pspec (GTK_CONTAINER (grid),
2182 child->widget,
2183 child_properties[CHILD_PROP_LEFT_ATTACH]);
2184 }
2185 else if (left + width > position)
2186 {
2187 CHILD_WIDTH (child) = width + 1;
2188 gtk_container_child_notify_by_pspec (GTK_CONTAINER (grid),
2189 child->widget,
2190 child_properties[CHILD_PROP_WIDTH]);
2191 }
2192 }
2193 }
2194
2195 /**
2196 * gtk_grid_remove_column:
2197 * @grid: a #GtkGrid
2198 * @position: the position of the column to remove
2199 *
2200 * Removes a column from the grid.
2201 *
2202 * Children that are placed in this column are removed,
2203 * spanning children that overlap this column have their
2204 * width reduced by one, and children after the column
2205 * are moved to the left.
2206 *
2207 * Since: 3.10
2208 */
2209 void
gtk_grid_remove_column(GtkGrid * grid,gint position)2210 gtk_grid_remove_column (GtkGrid *grid,
2211 gint position)
2212 {
2213 GtkGridPrivate *priv;
2214 GtkGridChild *child;
2215 GList *list;
2216 gint left, width;
2217
2218 g_return_if_fail (GTK_IS_GRID (grid));
2219
2220 priv = grid->priv;
2221
2222 list = priv->children;
2223 while (list)
2224 {
2225 child = list->data;
2226 list = list->next;
2227
2228 left = CHILD_LEFT (child);
2229 width = CHILD_WIDTH (child);
2230
2231 if (left <= position && left + width > position)
2232 width--;
2233 if (left > position)
2234 left--;
2235
2236 if (width <= 0)
2237 gtk_container_remove (GTK_CONTAINER (grid), child->widget);
2238 else
2239 gtk_container_child_set (GTK_CONTAINER (grid), child->widget,
2240 "width", width,
2241 "left-attach", left,
2242 NULL);
2243 }
2244 }
2245
2246 /**
2247 * gtk_grid_insert_next_to:
2248 * @grid: a #GtkGrid
2249 * @sibling: the child of @grid that the new row or column will be
2250 * placed next to
2251 * @side: the side of @sibling that @child is positioned next to
2252 *
2253 * Inserts a row or column at the specified position.
2254 *
2255 * The new row or column is placed next to @sibling, on the side
2256 * determined by @side. If @side is %GTK_POS_TOP or %GTK_POS_BOTTOM,
2257 * a row is inserted. If @side is %GTK_POS_LEFT of %GTK_POS_RIGHT,
2258 * a column is inserted.
2259 *
2260 * Since: 3.2
2261 */
2262 void
gtk_grid_insert_next_to(GtkGrid * grid,GtkWidget * sibling,GtkPositionType side)2263 gtk_grid_insert_next_to (GtkGrid *grid,
2264 GtkWidget *sibling,
2265 GtkPositionType side)
2266 {
2267 GtkGridChild *child;
2268
2269 g_return_if_fail (GTK_IS_GRID (grid));
2270 g_return_if_fail (GTK_IS_WIDGET (sibling));
2271 g_return_if_fail (_gtk_widget_get_parent (sibling) == (GtkWidget*)grid);
2272
2273 child = find_grid_child (grid, sibling);
2274
2275 switch (side)
2276 {
2277 case GTK_POS_LEFT:
2278 gtk_grid_insert_column (grid, CHILD_LEFT (child));
2279 break;
2280 case GTK_POS_RIGHT:
2281 gtk_grid_insert_column (grid, CHILD_LEFT (child) + CHILD_WIDTH (child));
2282 break;
2283 case GTK_POS_TOP:
2284 gtk_grid_insert_row (grid, CHILD_TOP (child));
2285 break;
2286 case GTK_POS_BOTTOM:
2287 gtk_grid_insert_row (grid, CHILD_TOP (child) + CHILD_HEIGHT (child));
2288 break;
2289 default:
2290 g_assert_not_reached ();
2291 }
2292 }
2293
2294 /**
2295 * gtk_grid_set_row_homogeneous:
2296 * @grid: a #GtkGrid
2297 * @homogeneous: %TRUE to make rows homogeneous
2298 *
2299 * Sets whether all rows of @grid will have the same height.
2300 */
2301 void
gtk_grid_set_row_homogeneous(GtkGrid * grid,gboolean homogeneous)2302 gtk_grid_set_row_homogeneous (GtkGrid *grid,
2303 gboolean homogeneous)
2304 {
2305 GtkGridPrivate *priv;
2306 g_return_if_fail (GTK_IS_GRID (grid));
2307
2308 priv = grid->priv;
2309
2310 /* Yes, homogeneous rows means all the columns have the same size */
2311 if (COLUMNS (priv)->homogeneous != homogeneous)
2312 {
2313 COLUMNS (priv)->homogeneous = homogeneous;
2314
2315 if (_gtk_widget_get_visible (GTK_WIDGET (grid)))
2316 gtk_widget_queue_resize (GTK_WIDGET (grid));
2317
2318 g_object_notify_by_pspec (G_OBJECT (grid), obj_properties [PROP_ROW_HOMOGENEOUS]);
2319 }
2320 }
2321
2322 /**
2323 * gtk_grid_get_row_homogeneous:
2324 * @grid: a #GtkGrid
2325 *
2326 * Returns whether all rows of @grid have the same height.
2327 *
2328 * Returns: whether all rows of @grid have the same height.
2329 */
2330 gboolean
gtk_grid_get_row_homogeneous(GtkGrid * grid)2331 gtk_grid_get_row_homogeneous (GtkGrid *grid)
2332 {
2333 GtkGridPrivate *priv;
2334 g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
2335
2336 priv = grid->priv;
2337
2338 return COLUMNS (priv)->homogeneous;
2339 }
2340
2341 /**
2342 * gtk_grid_set_column_homogeneous:
2343 * @grid: a #GtkGrid
2344 * @homogeneous: %TRUE to make columns homogeneous
2345 *
2346 * Sets whether all columns of @grid will have the same width.
2347 */
2348 void
gtk_grid_set_column_homogeneous(GtkGrid * grid,gboolean homogeneous)2349 gtk_grid_set_column_homogeneous (GtkGrid *grid,
2350 gboolean homogeneous)
2351 {
2352 GtkGridPrivate *priv;
2353 g_return_if_fail (GTK_IS_GRID (grid));
2354
2355 priv = grid->priv;
2356
2357 /* Yes, homogeneous columns means all the rows have the same size */
2358 if (ROWS (priv)->homogeneous != homogeneous)
2359 {
2360 ROWS (priv)->homogeneous = homogeneous;
2361
2362 if (_gtk_widget_get_visible (GTK_WIDGET (grid)))
2363 gtk_widget_queue_resize (GTK_WIDGET (grid));
2364
2365 g_object_notify_by_pspec (G_OBJECT (grid), obj_properties [PROP_COLUMN_HOMOGENEOUS]);
2366 }
2367 }
2368
2369 /**
2370 * gtk_grid_get_column_homogeneous:
2371 * @grid: a #GtkGrid
2372 *
2373 * Returns whether all columns of @grid have the same width.
2374 *
2375 * Returns: whether all columns of @grid have the same width.
2376 */
2377 gboolean
gtk_grid_get_column_homogeneous(GtkGrid * grid)2378 gtk_grid_get_column_homogeneous (GtkGrid *grid)
2379 {
2380 GtkGridPrivate *priv;
2381 g_return_val_if_fail (GTK_IS_GRID (grid), FALSE);
2382
2383 priv = grid->priv;
2384
2385 return ROWS (priv)->homogeneous;
2386 }
2387
2388 /**
2389 * gtk_grid_set_row_spacing:
2390 * @grid: a #GtkGrid
2391 * @spacing: the amount of space to insert between rows
2392 *
2393 * Sets the amount of space between rows of @grid.
2394 */
2395 void
gtk_grid_set_row_spacing(GtkGrid * grid,guint spacing)2396 gtk_grid_set_row_spacing (GtkGrid *grid,
2397 guint spacing)
2398 {
2399 GtkGridPrivate *priv;
2400 g_return_if_fail (GTK_IS_GRID (grid));
2401 g_return_if_fail (spacing <= G_MAXINT16);
2402
2403 priv = grid->priv;
2404
2405 if (COLUMNS (priv)->spacing != spacing)
2406 {
2407 COLUMNS (priv)->spacing = spacing;
2408
2409 if (_gtk_widget_get_visible (GTK_WIDGET (grid)))
2410 gtk_widget_queue_resize (GTK_WIDGET (grid));
2411
2412 g_object_notify_by_pspec (G_OBJECT (grid), obj_properties [PROP_ROW_SPACING]);
2413 }
2414 }
2415
2416 /**
2417 * gtk_grid_get_row_spacing:
2418 * @grid: a #GtkGrid
2419 *
2420 * Returns the amount of space between the rows of @grid.
2421 *
2422 * Returns: the row spacing of @grid
2423 */
2424 guint
gtk_grid_get_row_spacing(GtkGrid * grid)2425 gtk_grid_get_row_spacing (GtkGrid *grid)
2426 {
2427 GtkGridPrivate *priv;
2428 g_return_val_if_fail (GTK_IS_GRID (grid), 0);
2429
2430 priv = grid->priv;
2431
2432 return COLUMNS (priv)->spacing;
2433 }
2434
2435 /**
2436 * gtk_grid_set_column_spacing:
2437 * @grid: a #GtkGrid
2438 * @spacing: the amount of space to insert between columns
2439 *
2440 * Sets the amount of space between columns of @grid.
2441 */
2442 void
gtk_grid_set_column_spacing(GtkGrid * grid,guint spacing)2443 gtk_grid_set_column_spacing (GtkGrid *grid,
2444 guint spacing)
2445 {
2446 GtkGridPrivate *priv;
2447 g_return_if_fail (GTK_IS_GRID (grid));
2448 g_return_if_fail (spacing <= G_MAXINT16);
2449
2450 priv = grid->priv;
2451
2452 if (ROWS (priv)->spacing != spacing)
2453 {
2454 ROWS (priv)->spacing = spacing;
2455
2456 if (_gtk_widget_get_visible (GTK_WIDGET (grid)))
2457 gtk_widget_queue_resize (GTK_WIDGET (grid));
2458
2459 g_object_notify_by_pspec (G_OBJECT (grid), obj_properties [PROP_COLUMN_SPACING]);
2460 }
2461 }
2462
2463 /**
2464 * gtk_grid_get_column_spacing:
2465 * @grid: a #GtkGrid
2466 *
2467 * Returns the amount of space between the columns of @grid.
2468 *
2469 * Returns: the column spacing of @grid
2470 */
2471 guint
gtk_grid_get_column_spacing(GtkGrid * grid)2472 gtk_grid_get_column_spacing (GtkGrid *grid)
2473 {
2474 GtkGridPrivate *priv;
2475
2476 g_return_val_if_fail (GTK_IS_GRID (grid), 0);
2477
2478 priv = grid->priv;
2479
2480 return ROWS (priv)->spacing;
2481 }
2482
2483 static GtkGridRowProperties *
find_row_properties(GtkGrid * grid,gint row)2484 find_row_properties (GtkGrid *grid,
2485 gint row)
2486 {
2487 GList *l;
2488
2489 for (l = grid->priv->row_properties; l != NULL; l = l->next)
2490 {
2491 GtkGridRowProperties *prop = l->data;
2492 if (prop->row == row)
2493 return prop;
2494 }
2495
2496 return NULL;
2497 }
2498
2499 static void
gtk_grid_row_properties_free(GtkGridRowProperties * props)2500 gtk_grid_row_properties_free (GtkGridRowProperties *props)
2501 {
2502 g_slice_free (GtkGridRowProperties, props);
2503 }
2504
2505 static GtkGridRowProperties *
get_row_properties_or_create(GtkGrid * grid,gint row)2506 get_row_properties_or_create (GtkGrid *grid,
2507 gint row)
2508 {
2509 GtkGridRowProperties *props;
2510 GtkGridPrivate *priv = grid->priv;
2511
2512 props = find_row_properties (grid, row);
2513 if (props)
2514 return props;
2515
2516 props = g_slice_new (GtkGridRowProperties);
2517 *props = gtk_grid_row_properties_default;
2518 props->row = row;
2519
2520 priv->row_properties =
2521 g_list_prepend (priv->row_properties, props);
2522
2523 return props;
2524 }
2525
2526 static const GtkGridRowProperties *
get_row_properties_or_default(GtkGrid * grid,gint row)2527 get_row_properties_or_default (GtkGrid *grid,
2528 gint row)
2529 {
2530 GtkGridRowProperties *props;
2531
2532 props = find_row_properties (grid, row);
2533 if (props)
2534 return props;
2535 return >k_grid_row_properties_default;
2536 }
2537
2538 /**
2539 * gtk_grid_set_row_baseline_position:
2540 * @grid: a #GtkGrid
2541 * @row: a row index
2542 * @pos: a #GtkBaselinePosition
2543 *
2544 * Sets how the baseline should be positioned on @row of the
2545 * grid, in case that row is assigned more space than is requested.
2546 *
2547 * Since: 3.10
2548 */
2549 void
gtk_grid_set_row_baseline_position(GtkGrid * grid,gint row,GtkBaselinePosition pos)2550 gtk_grid_set_row_baseline_position (GtkGrid *grid,
2551 gint row,
2552 GtkBaselinePosition pos)
2553 {
2554 GtkGridRowProperties *props;
2555
2556 g_return_if_fail (GTK_IS_GRID (grid));
2557
2558 props = get_row_properties_or_create (grid, row);
2559
2560 if (props->baseline_position != pos)
2561 {
2562 props->baseline_position = pos;
2563
2564 if (_gtk_widget_get_visible (GTK_WIDGET (grid)))
2565 gtk_widget_queue_resize (GTK_WIDGET (grid));
2566 }
2567 }
2568
2569 /**
2570 * gtk_grid_get_row_baseline_position:
2571 * @grid: a #GtkGrid
2572 * @row: a row index
2573 *
2574 * Returns the baseline position of @row as set
2575 * by gtk_grid_set_row_baseline_position() or the default value
2576 * %GTK_BASELINE_POSITION_CENTER.
2577 *
2578 * Returns: the baseline position of @row
2579 *
2580 * Since: 3.10
2581 */
2582 GtkBaselinePosition
gtk_grid_get_row_baseline_position(GtkGrid * grid,gint row)2583 gtk_grid_get_row_baseline_position (GtkGrid *grid,
2584 gint row)
2585 {
2586 const GtkGridRowProperties *props;
2587
2588 g_return_val_if_fail (GTK_IS_GRID (grid), GTK_BASELINE_POSITION_CENTER);
2589
2590 props = get_row_properties_or_default (grid, row);
2591
2592 return props->baseline_position;
2593 }
2594
2595 /**
2596 * gtk_grid_set_baseline_row:
2597 * @grid: a #GtkGrid
2598 * @row: the row index
2599 *
2600 * Sets which row defines the global baseline for the entire grid.
2601 * Each row in the grid can have its own local baseline, but only
2602 * one of those is global, meaning it will be the baseline in the
2603 * parent of the @grid.
2604 *
2605 * Since: 3.10
2606 */
2607 void
gtk_grid_set_baseline_row(GtkGrid * grid,gint row)2608 gtk_grid_set_baseline_row (GtkGrid *grid,
2609 gint row)
2610 {
2611 GtkGridPrivate *priv;
2612
2613 g_return_if_fail (GTK_IS_GRID (grid));
2614
2615 priv = grid->priv;
2616
2617 if (priv->baseline_row != row)
2618 {
2619 priv->baseline_row = row;
2620
2621 if (_gtk_widget_get_visible (GTK_WIDGET (grid)))
2622 gtk_widget_queue_resize (GTK_WIDGET (grid));
2623 g_object_notify (G_OBJECT (grid), "baseline-row");
2624 }
2625 }
2626
2627 /**
2628 * gtk_grid_get_baseline_row:
2629 * @grid: a #GtkGrid
2630 *
2631 * Returns which row defines the global baseline of @grid.
2632 *
2633 * Returns: the row index defining the global baseline
2634 *
2635 * Since: 3.10
2636 */
2637 gint
gtk_grid_get_baseline_row(GtkGrid * grid)2638 gtk_grid_get_baseline_row (GtkGrid *grid)
2639 {
2640 GtkGridPrivate *priv;
2641
2642 g_return_val_if_fail (GTK_IS_GRID (grid), 0);
2643
2644 priv = grid->priv;
2645
2646 return priv->baseline_row;
2647 }
2648