1 /*
2  * Copyright © 2015 Red Hat Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19 
20 #include "config.h"
21 
22 #include "gtkboxgadgetprivate.h"
23 
24 #include "gtkcssnodeprivate.h"
25 #include "gtkmain.h"
26 #include "gtkprivate.h"
27 #include "gtksizerequest.h"
28 #include "gtkwidgetprivate.h"
29 
30 /* GtkBoxGadget is a container gadget implementation that arranges its
31  * children in a row, either horizontally or vertically. Children can
32  * be either widgets or gadgets, and can be set to expand horizontally
33  * or vertically, or both.
34  */
35 
36 typedef struct _GtkBoxGadgetPrivate GtkBoxGadgetPrivate;
37 struct _GtkBoxGadgetPrivate {
38   GtkOrientation orientation;
39   GArray *children;
40 
41   guint draw_focus       : 1;
42   guint draw_reverse     : 1;
43   guint allocate_reverse : 1;
44   guint align_reverse    : 1;
45 };
46 
47 typedef gboolean (* ComputeExpandFunc) (GObject *object, GtkOrientation orientation);
48 
49 typedef struct _GtkBoxGadgetChild GtkBoxGadgetChild;
50 struct _GtkBoxGadgetChild {
51   GObject *object;
52   gboolean expand;
53   GtkAlign align;
54 };
55 
G_DEFINE_TYPE_WITH_CODE(GtkBoxGadget,gtk_box_gadget,GTK_TYPE_CSS_GADGET,G_ADD_PRIVATE (GtkBoxGadget))56 G_DEFINE_TYPE_WITH_CODE (GtkBoxGadget, gtk_box_gadget, GTK_TYPE_CSS_GADGET,
57                          G_ADD_PRIVATE (GtkBoxGadget))
58 
59 static gboolean
60 gtk_box_gadget_child_is_visible (GObject *child)
61 {
62   if (GTK_IS_WIDGET (child))
63     return gtk_widget_get_visible (GTK_WIDGET (child));
64   else
65     return gtk_css_gadget_get_visible (GTK_CSS_GADGET (child));
66 }
67 
68 static gboolean
gtk_box_gadget_child_compute_expand(GtkBoxGadget * gadget,GtkBoxGadgetChild * child)69 gtk_box_gadget_child_compute_expand (GtkBoxGadget      *gadget,
70                                      GtkBoxGadgetChild *child)
71 {
72   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
73 
74   if (child->expand)
75     return TRUE;
76 
77   if (GTK_IS_WIDGET (child->object))
78     return gtk_widget_compute_expand (GTK_WIDGET (child->object), priv->orientation);
79 
80   return FALSE;
81 }
82 
83 static GtkAlign
gtk_box_gadget_child_get_align(GtkBoxGadget * gadget,GtkBoxGadgetChild * child)84 gtk_box_gadget_child_get_align (GtkBoxGadget      *gadget,
85                                 GtkBoxGadgetChild *child)
86 {
87   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
88   GtkAlign align;
89 
90   if (GTK_IS_WIDGET (child->object))
91     {
92       if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
93         g_object_get (child->object, "valign", &align, NULL);
94       else
95         g_object_get (child->object, "halign", &align, NULL);
96     }
97   else
98     align = child->align;
99 
100   return align;
101 }
102 
103 static GtkAlign
effective_align(GtkAlign align,gboolean reverse)104 effective_align (GtkAlign align,
105                  gboolean reverse)
106 {
107   switch (align)
108     {
109     case GTK_ALIGN_START:
110       return reverse ? GTK_ALIGN_END : GTK_ALIGN_START;
111     case GTK_ALIGN_END:
112       return reverse ? GTK_ALIGN_START : GTK_ALIGN_END;
113     default:
114       return align;
115     }
116 }
117 
118 static void
gtk_box_gadget_measure_child(GObject * child,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)119 gtk_box_gadget_measure_child (GObject        *child,
120                               GtkOrientation  orientation,
121                               gint            for_size,
122                               gint           *minimum,
123                               gint           *natural,
124                               gint           *minimum_baseline,
125                               gint           *natural_baseline)
126 {
127   if (GTK_IS_WIDGET (child))
128     {
129       _gtk_widget_get_preferred_size_for_size (GTK_WIDGET (child),
130                                                orientation,
131                                                for_size,
132                                                minimum, natural,
133                                                minimum_baseline, natural_baseline);
134     }
135   else
136     {
137       gtk_css_gadget_get_preferred_size (GTK_CSS_GADGET (child),
138                                          orientation,
139                                          for_size,
140                                          minimum, natural,
141                                          minimum_baseline, natural_baseline);
142     }
143 }
144 
145 static void
gtk_box_gadget_distribute(GtkBoxGadget * gadget,gint for_size,gint size,GtkRequestedSize * sizes)146 gtk_box_gadget_distribute (GtkBoxGadget     *gadget,
147                            gint              for_size,
148                            gint              size,
149                            GtkRequestedSize *sizes)
150 {
151   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
152   guint i, n_expand;
153 
154   n_expand = 0;
155 
156   for (i = 0 ; i < priv->children->len; i++)
157     {
158       GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i);
159 
160       gtk_box_gadget_measure_child (child->object,
161                                     priv->orientation,
162                                     for_size,
163                                     &sizes[i].minimum_size, &sizes[i].natural_size,
164                                     NULL, NULL);
165       if (gtk_box_gadget_child_is_visible (child->object) &&
166           gtk_box_gadget_child_compute_expand (gadget, child))
167         n_expand++;
168       size -= sizes[i].minimum_size;
169     }
170 
171   if G_UNLIKELY (size < 0)
172     {
173       g_critical ("%s: assertion 'size >= 0' failed in %s", G_STRFUNC, G_OBJECT_TYPE_NAME (gtk_css_gadget_get_owner (GTK_CSS_GADGET (gadget))));
174       return;
175     }
176 
177   size = gtk_distribute_natural_allocation (size, priv->children->len, sizes);
178 
179   if (size <= 0 || n_expand == 0)
180     return;
181 
182   for (i = 0 ; i < priv->children->len; i++)
183     {
184       GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i);
185 
186       if (!gtk_box_gadget_child_is_visible (child->object) ||
187           !gtk_box_gadget_child_compute_expand (gadget, child))
188         continue;
189 
190       sizes[i].minimum_size += size / n_expand;
191       /* distribute all pixels, even if there's a remainder */
192       size -= size / n_expand;
193       n_expand--;
194     }
195 
196 }
197 
198 static void
gtk_box_gadget_measure_orientation(GtkCssGadget * gadget,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)199 gtk_box_gadget_measure_orientation (GtkCssGadget   *gadget,
200                                     GtkOrientation  orientation,
201                                     gint            for_size,
202                                     gint           *minimum,
203                                     gint           *natural,
204                                     gint           *minimum_baseline,
205                                     gint           *natural_baseline)
206 {
207   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
208   gint child_min, child_nat;
209   guint i;
210 
211   *minimum = 0;
212   *natural = 0;
213 
214   for (i = 0 ; i < priv->children->len; i++)
215     {
216       GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i);
217 
218       gtk_box_gadget_measure_child (child->object,
219                                     orientation,
220                                     for_size,
221                                     &child_min, &child_nat,
222                                     NULL, NULL);
223 
224       *minimum += child_min;
225       *natural += child_nat;
226     }
227 }
228 
229 static void
gtk_box_gadget_measure_opposite(GtkCssGadget * gadget,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)230 gtk_box_gadget_measure_opposite (GtkCssGadget   *gadget,
231                                  GtkOrientation  orientation,
232                                  gint            for_size,
233                                  gint           *minimum,
234                                  gint           *natural,
235                                  gint           *minimum_baseline,
236                                  gint           *natural_baseline)
237 {
238   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
239   int child_min, child_nat, child_min_baseline, child_nat_baseline;
240   int total_min, above_min, below_min, total_nat, above_nat, below_nat;
241   GtkRequestedSize *sizes;
242   guint i;
243 
244   if (for_size >= 0)
245     {
246       sizes = g_newa (GtkRequestedSize, priv->children->len);
247       gtk_box_gadget_distribute (GTK_BOX_GADGET (gadget), -1, for_size, sizes);
248     }
249 
250   above_min = below_min = above_nat = below_nat = -1;
251   total_min = total_nat = 0;
252 
253   for (i = 0 ; i < priv->children->len; i++)
254     {
255       GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i);
256 
257       gtk_box_gadget_measure_child (child->object,
258                                     orientation,
259                                     for_size >= 0 ? sizes[i].minimum_size : -1,
260                                     &child_min, &child_nat,
261                                     &child_min_baseline, &child_nat_baseline);
262 
263       if (child_min_baseline >= 0)
264         {
265           below_min = MAX (below_min, child_min - child_min_baseline);
266           above_min = MAX (above_min, child_min_baseline);
267           below_nat = MAX (below_nat, child_nat - child_nat_baseline);
268           above_nat = MAX (above_nat, child_nat_baseline);
269         }
270       else
271         {
272           total_min = MAX (total_min, child_min);
273           total_nat = MAX (total_nat, child_nat);
274         }
275     }
276 
277   if (above_min >= 0)
278     {
279       total_min = MAX (total_min, above_min + below_min);
280       total_nat = MAX (total_nat, above_nat + below_nat);
281       /* assume GTK_BASELINE_POSITION_CENTER for now */
282       if (minimum_baseline)
283         *minimum_baseline = above_min + (total_min - (above_min + below_min)) / 2;
284       if (natural_baseline)
285         *natural_baseline = above_nat + (total_nat - (above_nat + below_nat)) / 2;
286     }
287 
288   *minimum = total_min;
289   *natural = total_nat;
290 }
291 
292 static void
gtk_box_gadget_get_preferred_size(GtkCssGadget * gadget,GtkOrientation orientation,gint for_size,gint * minimum,gint * natural,gint * minimum_baseline,gint * natural_baseline)293 gtk_box_gadget_get_preferred_size (GtkCssGadget   *gadget,
294                                    GtkOrientation  orientation,
295                                    gint            for_size,
296                                    gint           *minimum,
297                                    gint           *natural,
298                                    gint           *minimum_baseline,
299                                    gint           *natural_baseline)
300 {
301   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
302 
303   if (priv->orientation == orientation)
304     gtk_box_gadget_measure_orientation (gadget, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
305   else
306     gtk_box_gadget_measure_opposite (gadget, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
307 }
308 
309 static void
gtk_box_gadget_allocate_child(GObject * child,GtkOrientation box_orientation,GtkAlign child_align,GtkAllocation * allocation,int baseline,GtkAllocation * out_clip)310 gtk_box_gadget_allocate_child (GObject        *child,
311                                GtkOrientation  box_orientation,
312                                GtkAlign        child_align,
313                                GtkAllocation  *allocation,
314                                int             baseline,
315                                GtkAllocation  *out_clip)
316 {
317   if (GTK_IS_WIDGET (child))
318     {
319       gtk_widget_size_allocate_with_baseline (GTK_WIDGET (child), allocation, baseline);
320       gtk_widget_get_clip (GTK_WIDGET (child), out_clip);
321     }
322   else
323     {
324       GtkAllocation child_allocation;
325       int minimum, natural;
326       int minimum_baseline, natural_baseline;
327 
328       if (box_orientation == GTK_ORIENTATION_HORIZONTAL)
329         {
330           child_allocation.width = allocation->width;
331           child_allocation.x = allocation->x;
332 
333           gtk_css_gadget_get_preferred_size (GTK_CSS_GADGET (child),
334                                              GTK_ORIENTATION_VERTICAL,
335                                              allocation->width,
336                                              &minimum, &natural,
337                                              &minimum_baseline, &natural_baseline);
338 
339           switch (child_align)
340             {
341             case GTK_ALIGN_FILL:
342               child_allocation.height = allocation->height;
343               child_allocation.y = allocation->y;
344               break;
345             case GTK_ALIGN_START:
346               child_allocation.height = MIN(natural, allocation->height);
347               child_allocation.y = allocation->y;
348               break;
349             case GTK_ALIGN_END:
350               child_allocation.height = MIN(natural, allocation->height);
351               child_allocation.y = allocation->y + allocation->height - child_allocation.height;
352               break;
353             case GTK_ALIGN_BASELINE:
354               if (minimum_baseline >= 0 && baseline >= 0)
355                 {
356                   child_allocation.height = MIN(natural, allocation->height);
357                   child_allocation.y = allocation->y + MAX(0, baseline - minimum_baseline);
358                   break;
359                 }
360             case GTK_ALIGN_CENTER:
361               child_allocation.height = MIN(natural, allocation->height);
362               child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2;
363               break;
364             default:
365               g_assert_not_reached ();
366             }
367         }
368       else
369         {
370           child_allocation.height = allocation->height;
371           child_allocation.y = allocation->y;
372 
373           gtk_css_gadget_get_preferred_size (GTK_CSS_GADGET (child),
374                                              GTK_ORIENTATION_HORIZONTAL,
375                                              allocation->height,
376                                              &minimum, &natural,
377                                              NULL, NULL);
378 
379           switch (child_align)
380             {
381             case GTK_ALIGN_FILL:
382               child_allocation.width = allocation->width;
383               child_allocation.x = allocation->x;
384               break;
385             case GTK_ALIGN_START:
386               child_allocation.width = MIN(natural, allocation->width);
387               child_allocation.x = allocation->x;
388               break;
389             case GTK_ALIGN_END:
390               child_allocation.width = MIN(natural, allocation->width);
391               child_allocation.x = allocation->x + allocation->width - child_allocation.width;
392               break;
393             case GTK_ALIGN_BASELINE:
394             case GTK_ALIGN_CENTER:
395               child_allocation.width = MIN(natural, allocation->width);
396               child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2;
397               break;
398             default:
399               g_assert_not_reached ();
400             }
401         }
402 
403       gtk_css_gadget_allocate (GTK_CSS_GADGET (child), &child_allocation, baseline, out_clip);
404     }
405 }
406 
407 static void
gtk_box_gadget_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip)408 gtk_box_gadget_allocate (GtkCssGadget        *gadget,
409                          const GtkAllocation *allocation,
410                          int                  baseline,
411                          GtkAllocation       *out_clip)
412 {
413   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
414   GtkRequestedSize *sizes;
415   GtkAllocation child_allocation, child_clip;
416   GtkAlign child_align;
417   guint i;
418 
419   child_allocation = *allocation;
420   sizes = g_newa (GtkRequestedSize, priv->children->len);
421 
422   if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
423     {
424       gtk_box_gadget_distribute (GTK_BOX_GADGET (gadget), allocation->height, allocation->width, sizes);
425 
426       if (priv->allocate_reverse)
427         child_allocation.x = allocation->x + allocation->width;
428 
429       for (i = 0; i < priv->children->len; i++)
430         {
431           guint idx = priv->allocate_reverse ? priv->children->len - 1 - i : i;
432           GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, idx);
433           child_allocation.width = sizes[idx].minimum_size;
434           child_allocation.height = allocation->height;
435           child_allocation.y = allocation->y;
436           if (priv->allocate_reverse)
437             child_allocation.x -= child_allocation.width;
438 
439           child_align = gtk_box_gadget_child_get_align (GTK_BOX_GADGET (gadget), child);
440           gtk_box_gadget_allocate_child (child->object,
441                                          priv->orientation,
442                                          effective_align (child_align, priv->align_reverse),
443                                          &child_allocation,
444                                          baseline,
445                                          &child_clip);
446 
447           if (i == 0)
448             *out_clip = child_clip;
449           else
450             gdk_rectangle_union (out_clip, &child_clip, out_clip);
451 
452           if (!priv->allocate_reverse)
453             child_allocation.x += sizes[idx].minimum_size;
454         }
455     }
456   else
457     {
458       gtk_box_gadget_distribute (GTK_BOX_GADGET (gadget), allocation->width, allocation->height, sizes);
459 
460       if (priv->allocate_reverse)
461         child_allocation.y = allocation->y + allocation->height;
462 
463       for (i = 0 ; i < priv->children->len; i++)
464         {
465           guint idx = priv->allocate_reverse ? priv->children->len - 1 - i : i;
466           GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, idx);
467           child_allocation.height = sizes[idx].minimum_size;
468           child_allocation.width = allocation->width;
469           child_allocation.x = allocation->x;
470           if (priv->allocate_reverse)
471             child_allocation.y -= child_allocation.height;
472 
473           child_align = gtk_box_gadget_child_get_align (GTK_BOX_GADGET (gadget), child);
474           gtk_box_gadget_allocate_child (child->object,
475                                          priv->orientation,
476                                          effective_align (child_align, priv->align_reverse),
477                                          &child_allocation,
478                                          -1,
479                                          &child_clip);
480 
481           if (i == 0)
482             *out_clip = child_clip;
483           else
484             gdk_rectangle_union (out_clip, &child_clip, out_clip);
485 
486           if (!priv->allocate_reverse)
487             child_allocation.y += sizes[idx].minimum_size;
488         }
489     }
490 }
491 
492 static gboolean
gtk_box_gadget_draw(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height)493 gtk_box_gadget_draw (GtkCssGadget *gadget,
494                      cairo_t      *cr,
495                      int           x,
496                      int           y,
497                      int           width,
498                      int           height)
499 {
500   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
501   GtkWidget *owner = gtk_css_gadget_get_owner (gadget);
502   guint i;
503 
504   for (i = 0; i < priv->children->len; i++)
505     {
506       guint draw_index = priv->draw_reverse ? priv->children->len - 1 - i : i;
507       GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, draw_index);
508 
509       if (GTK_IS_WIDGET (child->object))
510         gtk_container_propagate_draw (GTK_CONTAINER (owner), GTK_WIDGET (child->object), cr);
511       else
512         gtk_css_gadget_draw (GTK_CSS_GADGET (child->object), cr);
513     }
514 
515   if (priv->draw_focus && gtk_widget_has_visible_focus (owner))
516     return TRUE;
517 
518   return FALSE;
519 }
520 
521 static void
gtk_box_gadget_finalize(GObject * object)522 gtk_box_gadget_finalize (GObject *object)
523 {
524   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (object));
525 
526   g_array_free (priv->children, TRUE);
527 
528   G_OBJECT_CLASS (gtk_box_gadget_parent_class)->finalize (object);
529 }
530 
531 static void
gtk_box_gadget_class_init(GtkBoxGadgetClass * klass)532 gtk_box_gadget_class_init (GtkBoxGadgetClass *klass)
533 {
534   GtkCssGadgetClass *gadget_class = GTK_CSS_GADGET_CLASS (klass);
535   GObjectClass *object_class = G_OBJECT_CLASS (klass);
536 
537   object_class->finalize = gtk_box_gadget_finalize;
538 
539   gadget_class->get_preferred_size = gtk_box_gadget_get_preferred_size;
540   gadget_class->allocate = gtk_box_gadget_allocate;
541   gadget_class->draw = gtk_box_gadget_draw;
542 }
543 
544 static void
gtk_box_gadget_clear_child(gpointer data)545 gtk_box_gadget_clear_child (gpointer data)
546 {
547   GtkBoxGadgetChild *child = data;
548 
549   g_object_unref (child->object);
550 }
551 
552 static void
gtk_box_gadget_init(GtkBoxGadget * gadget)553 gtk_box_gadget_init (GtkBoxGadget *gadget)
554 {
555   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
556 
557   priv->children = g_array_new (FALSE, FALSE, sizeof (GtkBoxGadgetChild));
558   g_array_set_clear_func (priv->children, gtk_box_gadget_clear_child);
559 }
560 
561 GtkCssGadget *
gtk_box_gadget_new_for_node(GtkCssNode * node,GtkWidget * owner)562 gtk_box_gadget_new_for_node (GtkCssNode *node,
563                                GtkWidget  *owner)
564 {
565   return g_object_new (GTK_TYPE_BOX_GADGET,
566                        "node", node,
567                        "owner", owner,
568                        NULL);
569 }
570 
571 GtkCssGadget *
gtk_box_gadget_new(const char * name,GtkWidget * owner,GtkCssGadget * parent,GtkCssGadget * next_sibling)572 gtk_box_gadget_new (const char   *name,
573                     GtkWidget    *owner,
574                     GtkCssGadget *parent,
575                     GtkCssGadget *next_sibling)
576 {
577   GtkCssNode *node;
578   GtkCssGadget *result;
579 
580   node = gtk_css_node_new ();
581   gtk_css_node_set_name (node, g_intern_string (name));
582   if (parent)
583     gtk_css_node_insert_before (gtk_css_gadget_get_node (parent),
584                                 node,
585                                 next_sibling ? gtk_css_gadget_get_node (next_sibling) : NULL);
586 
587   result = gtk_box_gadget_new_for_node (node, owner);
588 
589   g_object_unref (node);
590 
591   return result;
592 }
593 
594 void
gtk_box_gadget_set_orientation(GtkBoxGadget * gadget,GtkOrientation orientation)595 gtk_box_gadget_set_orientation (GtkBoxGadget   *gadget,
596                                 GtkOrientation  orientation)
597 {
598   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
599 
600   priv->orientation = orientation;
601 }
602 
603 void
gtk_box_gadget_set_draw_focus(GtkBoxGadget * gadget,gboolean draw_focus)604 gtk_box_gadget_set_draw_focus (GtkBoxGadget *gadget,
605                                gboolean      draw_focus)
606 {
607   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
608 
609   priv->draw_focus = draw_focus;
610 }
611 
612 void
gtk_box_gadget_set_draw_reverse(GtkBoxGadget * gadget,gboolean draw_reverse)613 gtk_box_gadget_set_draw_reverse (GtkBoxGadget *gadget,
614                                  gboolean      draw_reverse)
615 {
616   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
617 
618   priv->draw_reverse = draw_reverse;
619 }
620 
621 void
gtk_box_gadget_set_allocate_reverse(GtkBoxGadget * gadget,gboolean allocate_reverse)622 gtk_box_gadget_set_allocate_reverse (GtkBoxGadget *gadget,
623                                      gboolean      allocate_reverse)
624 {
625   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
626 
627   priv->allocate_reverse = allocate_reverse;
628 }
629 
630 void
gtk_box_gadget_set_align_reverse(GtkBoxGadget * gadget,gboolean align_reverse)631 gtk_box_gadget_set_align_reverse (GtkBoxGadget *gadget,
632                                   gboolean      align_reverse)
633 {
634   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
635 
636   priv->align_reverse = align_reverse;
637 }
638 
639 static GtkCssNode *
get_css_node(GObject * child)640 get_css_node (GObject *child)
641 {
642   if (GTK_IS_WIDGET (child))
643     return gtk_widget_get_css_node (GTK_WIDGET (child));
644   else
645     return gtk_css_gadget_get_node (GTK_CSS_GADGET (child));
646 }
647 
648 static void
gtk_box_gadget_insert_object(GtkBoxGadget * gadget,int pos,GObject * object,gboolean expand,GtkAlign align)649 gtk_box_gadget_insert_object (GtkBoxGadget *gadget,
650                               int           pos,
651                               GObject      *object,
652                               gboolean      expand,
653                               GtkAlign      align)
654 {
655   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
656   GtkBoxGadgetChild child;
657 
658   child.object = g_object_ref (object);
659   child.expand = expand;
660   child.align = align;
661 
662   if (pos < 0 || pos >= priv->children->len)
663     {
664       g_array_append_val (priv->children, child);
665       gtk_css_node_insert_before (gtk_css_gadget_get_node (GTK_CSS_GADGET (gadget)),
666                                   get_css_node (object),
667                                   NULL);
668     }
669   else
670     {
671       g_array_insert_val (priv->children, pos, child);
672       gtk_css_node_insert_before (gtk_css_gadget_get_node (GTK_CSS_GADGET (gadget)),
673                                   get_css_node (object),
674                                   get_css_node (g_array_index (priv->children, GtkBoxGadgetChild, pos + 1).object));
675     }
676 }
677 
678 void
gtk_box_gadget_insert_widget(GtkBoxGadget * gadget,int pos,GtkWidget * widget)679 gtk_box_gadget_insert_widget (GtkBoxGadget *gadget,
680                               int           pos,
681                               GtkWidget    *widget)
682 {
683   gtk_box_gadget_insert_object (gadget, pos, G_OBJECT (widget), FALSE, GTK_ALIGN_FILL);
684 }
685 
686 static GtkBoxGadgetChild *
gtk_box_gadget_find_object(GtkBoxGadget * gadget,GObject * object,int * position)687 gtk_box_gadget_find_object (GtkBoxGadget *gadget,
688                             GObject      *object,
689                             int          *position)
690 {
691   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
692   guint i;
693 
694   for (i = 0; i < priv->children->len; i++)
695     {
696       GtkBoxGadgetChild *child = &g_array_index (priv->children, GtkBoxGadgetChild, i);
697 
698       if (child->object == object)
699         {
700           if (position)
701             *position = i;
702           return child;
703         }
704     }
705 
706   return NULL;
707 }
708 
709 static void
gtk_box_gadget_remove_object(GtkBoxGadget * gadget,GObject * object)710 gtk_box_gadget_remove_object (GtkBoxGadget *gadget,
711                               GObject      *object)
712 {
713   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (gadget);
714   GtkBoxGadgetChild *child;
715   int position;
716 
717   child = gtk_box_gadget_find_object (gadget, object, &position);
718   if (child)
719     {
720       gtk_css_node_set_parent (get_css_node (child->object), NULL);
721       g_array_remove_index (priv->children, position);
722     }
723 }
724 
725 void
gtk_box_gadget_remove_widget(GtkBoxGadget * gadget,GtkWidget * widget)726 gtk_box_gadget_remove_widget (GtkBoxGadget *gadget,
727                               GtkWidget    *widget)
728 {
729   gtk_box_gadget_remove_object (gadget, G_OBJECT (widget));
730 }
731 
732 void
gtk_box_gadget_insert_gadget_before(GtkBoxGadget * gadget,GtkCssGadget * sibling,GtkCssGadget * cssgadget,gboolean expand,GtkAlign align)733 gtk_box_gadget_insert_gadget_before (GtkBoxGadget *gadget,
734                                      GtkCssGadget *sibling,
735                                      GtkCssGadget *cssgadget,
736                                      gboolean      expand,
737                                      GtkAlign      align)
738 {
739   /* Insert at the end if no sibling specified */
740   int pos = -1;
741 
742   if (sibling)
743     gtk_box_gadget_find_object (gadget, G_OBJECT (sibling), &pos);
744 
745   gtk_box_gadget_insert_gadget (gadget, pos, cssgadget, expand, align);
746 }
747 
748 void
gtk_box_gadget_insert_gadget_after(GtkBoxGadget * gadget,GtkCssGadget * sibling,GtkCssGadget * cssgadget,gboolean expand,GtkAlign align)749 gtk_box_gadget_insert_gadget_after (GtkBoxGadget *gadget,
750                                     GtkCssGadget *sibling,
751                                     GtkCssGadget *cssgadget,
752                                     gboolean      expand,
753                                     GtkAlign      align)
754 {
755   /* Insert at the beginning if no sibling specified */
756   int pos = 0;
757 
758   if (sibling && gtk_box_gadget_find_object (gadget, G_OBJECT (sibling), &pos))
759     pos++;
760 
761   gtk_box_gadget_insert_gadget (gadget, pos, cssgadget, expand, align);
762 }
763 
764 void
gtk_box_gadget_insert_gadget(GtkBoxGadget * gadget,int pos,GtkCssGadget * cssgadget,gboolean expand,GtkAlign align)765 gtk_box_gadget_insert_gadget (GtkBoxGadget *gadget,
766                               int           pos,
767                               GtkCssGadget *cssgadget,
768                               gboolean      expand,
769                               GtkAlign      align)
770 {
771   gtk_box_gadget_insert_object (gadget, pos, G_OBJECT (cssgadget), expand, align);
772 }
773 
774 void
gtk_box_gadget_remove_gadget(GtkBoxGadget * gadget,GtkCssGadget * cssgadget)775 gtk_box_gadget_remove_gadget (GtkBoxGadget *gadget,
776                               GtkCssGadget *cssgadget)
777 {
778   gtk_box_gadget_remove_object (gadget, G_OBJECT (cssgadget));
779 }
780 
781 void
gtk_box_gadget_reverse_children(GtkBoxGadget * gadget)782 gtk_box_gadget_reverse_children (GtkBoxGadget *gadget)
783 {
784   GtkBoxGadgetPrivate *priv = gtk_box_gadget_get_instance_private (GTK_BOX_GADGET (gadget));
785   int i, j;
786 
787   gtk_css_node_reverse_children (gtk_css_gadget_get_node (GTK_CSS_GADGET (gadget)));
788 
789   for (i = 0, j = priv->children->len - 1; i < j; i++, j--)
790     {
791       GtkBoxGadgetChild *child1 = &g_array_index (priv->children, GtkBoxGadgetChild, i);
792       GtkBoxGadgetChild *child2 = &g_array_index (priv->children, GtkBoxGadgetChild, j);
793       GtkBoxGadgetChild tmp;
794 
795       tmp = *child1;
796       *child1 = *child2;
797       *child2 = tmp;
798     }
799 }
800 
801 void
gtk_box_gadget_set_gadget_expand(GtkBoxGadget * gadget,GObject * object,gboolean expand)802 gtk_box_gadget_set_gadget_expand (GtkBoxGadget *gadget,
803                                   GObject      *object,
804                                   gboolean      expand)
805 {
806   GtkBoxGadgetChild *child;
807 
808   child = gtk_box_gadget_find_object (gadget, object, NULL);
809 
810   if (!child)
811     return;
812 
813   if (child->expand == expand)
814     return;
815 
816   child->expand = expand;
817   gtk_css_gadget_queue_resize (GTK_CSS_GADGET (gadget));
818 }
819 
820 void
gtk_box_gadget_set_gadget_align(GtkBoxGadget * gadget,GObject * object,GtkAlign align)821 gtk_box_gadget_set_gadget_align (GtkBoxGadget *gadget,
822                                  GObject      *object,
823                                  GtkAlign      align)
824 {
825   GtkBoxGadgetChild *child;
826 
827   child = gtk_box_gadget_find_object (gadget, object, NULL);
828 
829   if (!child)
830     return;
831 
832   if (child->align == align)
833     return;
834 
835   child->align = align;
836   gtk_css_gadget_queue_resize (GTK_CSS_GADGET (gadget));
837 }
838