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 &gtk_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