]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/gtkcols.c
GTK3 prep: refactor the layout calculation in Columns.
[PuTTY.git] / unix / gtkcols.c
1 /*
2  * gtkcols.c - implementation of the `Columns' GTK layout container.
3  */
4
5 #include <gtk/gtk.h>
6 #include "gtkcompat.h"
7 #include "gtkcols.h"
8
9 static void columns_init(Columns *cols);
10 static void columns_class_init(ColumnsClass *klass);
11 static void columns_map(GtkWidget *widget);
12 static void columns_unmap(GtkWidget *widget);
13 #if !GTK_CHECK_VERSION(2,0,0)
14 static void columns_draw(GtkWidget *widget, GdkRectangle *area);
15 static gint columns_expose(GtkWidget *widget, GdkEventExpose *event);
16 #endif
17 static void columns_base_add(GtkContainer *container, GtkWidget *widget);
18 static void columns_remove(GtkContainer *container, GtkWidget *widget);
19 static void columns_forall(GtkContainer *container, gboolean include_internals,
20                            GtkCallback callback, gpointer callback_data);
21 #if !GTK_CHECK_VERSION(2,0,0)
22 static gint columns_focus(GtkContainer *container, GtkDirectionType dir);
23 #endif
24 static GType columns_child_type(GtkContainer *container);
25 #if GTK_CHECK_VERSION(3,0,0)
26 static void columns_get_preferred_width(GtkWidget *widget,
27                                         gint *min, gint *nat);
28 static void columns_get_preferred_height(GtkWidget *widget,
29                                          gint *min, gint *nat);
30 #endif
31 static void columns_size_request(GtkWidget *widget, GtkRequisition *req);
32 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc);
33
34 static GtkContainerClass *parent_class = NULL;
35
36 #if !GTK_CHECK_VERSION(2,0,0)
37 GType columns_get_type(void)
38 {
39     static GType columns_type = 0;
40
41     if (!columns_type) {
42         static const GtkTypeInfo columns_info = {
43             "Columns",
44             sizeof(Columns),
45             sizeof(ColumnsClass),
46             (GtkClassInitFunc) columns_class_init,
47             (GtkObjectInitFunc) columns_init,
48             /* reserved_1 */ NULL,
49             /* reserved_2 */ NULL,
50             (GtkClassInitFunc) NULL,
51         };
52
53         columns_type = gtk_type_unique(GTK_TYPE_CONTAINER, &columns_info);
54     }
55
56     return columns_type;
57 }
58 #else
59 GType columns_get_type(void)
60 {
61     static GType columns_type = 0;
62
63     if (!columns_type) {
64         static const GTypeInfo columns_info = {
65             sizeof(ColumnsClass),
66             NULL,
67             NULL,
68             (GClassInitFunc) columns_class_init,
69             NULL,
70             NULL,
71             sizeof(Columns),
72             0,
73             (GInstanceInitFunc)columns_init,
74         };
75
76         columns_type = g_type_register_static(GTK_TYPE_CONTAINER, "Columns",
77                                               &columns_info, 0);
78     }
79
80     return columns_type;
81 }
82 #endif
83
84 #if !GTK_CHECK_VERSION(2,0,0)
85 static gint (*columns_inherited_focus)(GtkContainer *container,
86                                        GtkDirectionType direction);
87 #endif
88
89 static void columns_class_init(ColumnsClass *klass)
90 {
91 #if !GTK_CHECK_VERSION(2,0,0)
92     /* GtkObjectClass *object_class = (GtkObjectClass *)klass; */
93     GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
94     GtkContainerClass *container_class = (GtkContainerClass *)klass;
95 #else
96     /* GObjectClass *object_class = G_OBJECT_CLASS(klass); */
97     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
98     GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
99 #endif
100
101 #if !GTK_CHECK_VERSION(2,0,0)
102     parent_class = gtk_type_class(GTK_TYPE_CONTAINER);
103 #else
104     parent_class = g_type_class_peek_parent(klass);
105 #endif
106
107     widget_class->map = columns_map;
108     widget_class->unmap = columns_unmap;
109 #if !GTK_CHECK_VERSION(2,0,0)
110     widget_class->draw = columns_draw;
111     widget_class->expose_event = columns_expose;
112 #endif
113 #if GTK_CHECK_VERSION(3,0,0)
114     widget_class->get_preferred_width = columns_get_preferred_width;
115     widget_class->get_preferred_height = columns_get_preferred_height;
116 #else
117     widget_class->size_request = columns_size_request;
118 #endif
119     widget_class->size_allocate = columns_size_allocate;
120
121     container_class->add = columns_base_add;
122     container_class->remove = columns_remove;
123     container_class->forall = columns_forall;
124     container_class->child_type = columns_child_type;
125 #if !GTK_CHECK_VERSION(2,0,0)
126     /* Save the previous value of this method. */
127     if (!columns_inherited_focus)
128         columns_inherited_focus = container_class->focus;
129     container_class->focus = columns_focus;
130 #endif
131 }
132
133 static void columns_init(Columns *cols)
134 {
135     gtk_widget_set_has_window(GTK_WIDGET(cols), FALSE);
136
137     cols->children = NULL;
138     cols->spacing = 0;
139 }
140
141 /*
142  * These appear to be thoroughly tedious functions; the only reason
143  * we have to reimplement them at all is because we defined our own
144  * format for our GList of children...
145  */
146 static void columns_map(GtkWidget *widget)
147 {
148     Columns *cols;
149     ColumnsChild *child;
150     GList *children;
151
152     g_return_if_fail(widget != NULL);
153     g_return_if_fail(IS_COLUMNS(widget));
154
155     cols = COLUMNS(widget);
156     gtk_widget_set_mapped(GTK_WIDGET(cols), TRUE);
157
158     for (children = cols->children;
159          children && (child = children->data);
160          children = children->next) {
161         if (child->widget &&
162             gtk_widget_get_visible(child->widget) &&
163             !gtk_widget_get_mapped(child->widget))
164             gtk_widget_map(child->widget);
165     }
166 }
167 static void columns_unmap(GtkWidget *widget)
168 {
169     Columns *cols;
170     ColumnsChild *child;
171     GList *children;
172
173     g_return_if_fail(widget != NULL);
174     g_return_if_fail(IS_COLUMNS(widget));
175
176     cols = COLUMNS(widget);
177     gtk_widget_set_mapped(GTK_WIDGET(cols), FALSE);
178
179     for (children = cols->children;
180          children && (child = children->data);
181          children = children->next) {
182         if (child->widget &&
183             gtk_widget_get_visible(child->widget) &&
184             gtk_widget_get_mapped(child->widget))
185             gtk_widget_unmap(child->widget);
186     }
187 }
188 #if !GTK_CHECK_VERSION(2,0,0)
189 static void columns_draw(GtkWidget *widget, GdkRectangle *area)
190 {
191     Columns *cols;
192     ColumnsChild *child;
193     GList *children;
194     GdkRectangle child_area;
195
196     g_return_if_fail(widget != NULL);
197     g_return_if_fail(IS_COLUMNS(widget));
198
199     if (GTK_WIDGET_DRAWABLE(widget)) {
200         cols = COLUMNS(widget);
201
202         for (children = cols->children;
203              children && (child = children->data);
204              children = children->next) {
205             if (child->widget &&
206                 GTK_WIDGET_DRAWABLE(child->widget) &&
207                 gtk_widget_intersect(child->widget, area, &child_area))
208                 gtk_widget_draw(child->widget, &child_area);
209         }
210     }
211 }
212 static gint columns_expose(GtkWidget *widget, GdkEventExpose *event)
213 {
214     Columns *cols;
215     ColumnsChild *child;
216     GList *children;
217     GdkEventExpose child_event;
218
219     g_return_val_if_fail(widget != NULL, FALSE);
220     g_return_val_if_fail(IS_COLUMNS(widget), FALSE);
221     g_return_val_if_fail(event != NULL, FALSE);
222
223     if (GTK_WIDGET_DRAWABLE(widget)) {
224         cols = COLUMNS(widget);
225         child_event = *event;
226
227         for (children = cols->children;
228              children && (child = children->data);
229              children = children->next) {
230             if (child->widget &&
231                 GTK_WIDGET_DRAWABLE(child->widget) &&
232                 GTK_WIDGET_NO_WINDOW(child->widget) &&
233                 gtk_widget_intersect(child->widget, &event->area,
234                                      &child_event.area))
235                 gtk_widget_event(child->widget, (GdkEvent *)&child_event);
236         }
237     }
238     return FALSE;
239 }
240 #endif
241
242 static void columns_base_add(GtkContainer *container, GtkWidget *widget)
243 {
244     Columns *cols;
245
246     g_return_if_fail(container != NULL);
247     g_return_if_fail(IS_COLUMNS(container));
248     g_return_if_fail(widget != NULL);
249
250     cols = COLUMNS(container);
251
252     /*
253      * Default is to add a new widget spanning all columns.
254      */
255     columns_add(cols, widget, 0, 0);   /* 0 means ncols */
256 }
257
258 static void columns_remove(GtkContainer *container, GtkWidget *widget)
259 {
260     Columns *cols;
261     ColumnsChild *child;
262     GtkWidget *childw;
263     GList *children;
264     gboolean was_visible;
265
266     g_return_if_fail(container != NULL);
267     g_return_if_fail(IS_COLUMNS(container));
268     g_return_if_fail(widget != NULL);
269
270     cols = COLUMNS(container);
271
272     for (children = cols->children;
273          children && (child = children->data);
274          children = children->next) {
275         if (child->widget != widget)
276             continue;
277
278         was_visible = gtk_widget_get_visible(widget);
279         gtk_widget_unparent(widget);
280         cols->children = g_list_remove_link(cols->children, children);
281         g_list_free(children);
282         g_free(child);
283         if (was_visible)
284             gtk_widget_queue_resize(GTK_WIDGET(container));
285         break;
286     }
287
288     for (children = cols->taborder;
289          children && (childw = children->data);
290          children = children->next) {
291         if (childw != widget)
292             continue;
293
294         cols->taborder = g_list_remove_link(cols->taborder, children);
295         g_list_free(children);
296 #if GTK_CHECK_VERSION(2,0,0)
297         gtk_container_set_focus_chain(container, cols->taborder);
298 #endif
299         break;
300     }
301 }
302
303 static void columns_forall(GtkContainer *container, gboolean include_internals,
304                            GtkCallback callback, gpointer callback_data)
305 {
306     Columns *cols;
307     ColumnsChild *child;
308     GList *children, *next;
309
310     g_return_if_fail(container != NULL);
311     g_return_if_fail(IS_COLUMNS(container));
312     g_return_if_fail(callback != NULL);
313
314     cols = COLUMNS(container);
315
316     for (children = cols->children;
317          children && (child = children->data);
318          children = next) {
319         /*
320          * We can't wait until after the callback to assign
321          * `children = children->next', because the callback might
322          * be gtk_widget_destroy, which would remove the link
323          * `children' from the list! So instead we must get our
324          * hands on the value of the `next' pointer _before_ the
325          * callback.
326          */
327         next = children->next;
328         if (child->widget)
329             callback(child->widget, callback_data);
330     }
331 }
332
333 static GType columns_child_type(GtkContainer *container)
334 {
335     return GTK_TYPE_WIDGET;
336 }
337
338 GtkWidget *columns_new(gint spacing)
339 {
340     Columns *cols;
341
342 #if !GTK_CHECK_VERSION(2,0,0)
343     cols = gtk_type_new(columns_get_type());
344 #else
345     cols = g_object_new(TYPE_COLUMNS, NULL);
346 #endif
347
348     cols->spacing = spacing;
349
350     return GTK_WIDGET(cols);
351 }
352
353 void columns_set_cols(Columns *cols, gint ncols, const gint *percentages)
354 {
355     ColumnsChild *childdata;
356     gint i;
357
358     g_return_if_fail(cols != NULL);
359     g_return_if_fail(IS_COLUMNS(cols));
360     g_return_if_fail(ncols > 0);
361     g_return_if_fail(percentages != NULL);
362
363     childdata = g_new(ColumnsChild, 1);
364     childdata->widget = NULL;
365     childdata->ncols = ncols;
366     childdata->percentages = g_new(gint, ncols);
367     childdata->force_left = FALSE;
368     for (i = 0; i < ncols; i++)
369         childdata->percentages[i] = percentages[i];
370
371     cols->children = g_list_append(cols->children, childdata);
372 }
373
374 void columns_add(Columns *cols, GtkWidget *child,
375                  gint colstart, gint colspan)
376 {
377     ColumnsChild *childdata;
378
379     g_return_if_fail(cols != NULL);
380     g_return_if_fail(IS_COLUMNS(cols));
381     g_return_if_fail(child != NULL);
382     g_return_if_fail(gtk_widget_get_parent(child) == NULL);
383
384     childdata = g_new(ColumnsChild, 1);
385     childdata->widget = child;
386     childdata->colstart = colstart;
387     childdata->colspan = colspan;
388     childdata->force_left = FALSE;
389
390     cols->children = g_list_append(cols->children, childdata);
391     cols->taborder = g_list_append(cols->taborder, child);
392
393     gtk_widget_set_parent(child, GTK_WIDGET(cols));
394
395 #if GTK_CHECK_VERSION(2,0,0)
396     gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
397 #endif
398
399     if (gtk_widget_get_realized(GTK_WIDGET(cols)))
400         gtk_widget_realize(child);
401
402     if (gtk_widget_get_visible(GTK_WIDGET(cols)) &&
403         gtk_widget_get_visible(child)) {
404         if (gtk_widget_get_mapped(GTK_WIDGET(cols)))
405             gtk_widget_map(child);
406         gtk_widget_queue_resize(child);
407     }
408 }
409
410 void columns_force_left_align(Columns *cols, GtkWidget *widget)
411 {
412     ColumnsChild *child;
413     GList *children;
414
415     g_return_if_fail(cols != NULL);
416     g_return_if_fail(IS_COLUMNS(cols));
417     g_return_if_fail(widget != NULL);
418
419     for (children = cols->children;
420          children && (child = children->data);
421          children = children->next) {
422         if (child->widget != widget)
423             continue;
424
425         child->force_left = TRUE;
426         if (gtk_widget_get_visible(widget))
427             gtk_widget_queue_resize(GTK_WIDGET(cols));
428         break;
429     }
430 }
431
432 void columns_taborder_last(Columns *cols, GtkWidget *widget)
433 {
434     GtkWidget *childw;
435     GList *children;
436
437     g_return_if_fail(cols != NULL);
438     g_return_if_fail(IS_COLUMNS(cols));
439     g_return_if_fail(widget != NULL);
440
441     for (children = cols->taborder;
442          children && (childw = children->data);
443          children = children->next) {
444         if (childw != widget)
445             continue;
446
447         cols->taborder = g_list_remove_link(cols->taborder, children);
448         g_list_free(children);
449         cols->taborder = g_list_append(cols->taborder, widget);
450 #if GTK_CHECK_VERSION(2,0,0)
451         gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
452 #endif
453         break;
454     }
455 }
456
457 #if !GTK_CHECK_VERSION(2,0,0)
458 /*
459  * Override GtkContainer's focus movement so the user can
460  * explicitly specify the tab order.
461  */
462 static gint columns_focus(GtkContainer *container, GtkDirectionType dir)
463 {
464     Columns *cols;
465     GList *pos;
466     GtkWidget *focuschild;
467
468     g_return_val_if_fail(container != NULL, FALSE);
469     g_return_val_if_fail(IS_COLUMNS(container), FALSE);
470
471     cols = COLUMNS(container);
472
473     if (!GTK_WIDGET_DRAWABLE(cols) ||
474         !GTK_WIDGET_IS_SENSITIVE(cols))
475         return FALSE;
476
477     if (!GTK_WIDGET_CAN_FOCUS(container) &&
478         (dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) {
479
480         focuschild = container->focus_child;
481         gtk_container_set_focus_child(container, NULL);
482
483         if (dir == GTK_DIR_TAB_FORWARD)
484             pos = cols->taborder;
485         else
486             pos = g_list_last(cols->taborder);
487
488         while (pos) {
489             GtkWidget *child = pos->data;
490
491             if (focuschild) {
492                 if (focuschild == child) {
493                     focuschild = NULL; /* now we can start looking in here */
494                     if (GTK_WIDGET_DRAWABLE(child) &&
495                         GTK_IS_CONTAINER(child) &&
496                         !GTK_WIDGET_HAS_FOCUS(child)) {
497                         if (gtk_container_focus(GTK_CONTAINER(child), dir))
498                             return TRUE;
499                     }
500                 }
501             } else if (GTK_WIDGET_DRAWABLE(child)) {
502                 if (GTK_IS_CONTAINER(child)) {
503                     if (gtk_container_focus(GTK_CONTAINER(child), dir))
504                         return TRUE;
505                 } else if (GTK_WIDGET_CAN_FOCUS(child)) {
506                     gtk_widget_grab_focus(child);
507                     return TRUE;
508                 }
509             }
510
511             if (dir == GTK_DIR_TAB_FORWARD)
512                 pos = pos->next;
513             else
514                 pos = pos->prev;
515         }
516
517         return FALSE;
518     } else
519         return columns_inherited_focus(container, dir);
520 }
521 #endif
522
523 /*
524  * Underlying parts of the layout algorithm, to compute the Columns
525  * container's width or height given the widths or heights of its
526  * children. These will be called in various ways with different
527  * notions of width and height in use, so we abstract them out and
528  * pass them a 'get width' or 'get height' function pointer.
529  */
530
531 typedef gint (*widget_dim_fn_t)(ColumnsChild *child);
532
533 static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
534 {
535     ColumnsChild *child;
536     GList *children;
537     gint i, ncols, colspan, retwidth, childwidth;
538     const gint *percentages;
539     static const gint onecol[] = { 100 };
540
541     retwidth = 0;
542
543     ncols = 1;
544     percentages = onecol;
545
546     for (children = cols->children;
547          children && (child = children->data);
548          children = children->next) {
549
550         if (!child->widget) {
551             /* Column reconfiguration. */
552             ncols = child->ncols;
553             percentages = child->percentages;
554             continue;
555         }
556
557         /* Only take visible widgets into account. */
558         if (!gtk_widget_get_visible(child->widget))
559             continue;
560
561         childwidth = get_width(child);
562         colspan = child->colspan ? child->colspan : ncols-child->colstart;
563
564         /*
565          * To compute width: we know that childwidth + cols->spacing
566          * needs to equal a certain percentage of the full width of
567          * the container. So we work this value out, figure out how
568          * wide the container will need to be to make that percentage
569          * of it equal to that width, and ensure our returned width is
570          * at least that much. Very simple really.
571          */
572         {
573             int percent, thiswid, fullwid;
574
575             percent = 0;
576             for (i = 0; i < colspan; i++)
577                 percent += percentages[child->colstart+i];
578
579             thiswid = childwidth + cols->spacing;
580             /*
581              * Since childwidth is (at least sometimes) the _minimum_
582              * size the child needs, we must ensure that it gets _at
583              * least_ that size. Hence, when scaling thiswid up to
584              * fullwid, we must round up, which means adding percent-1
585              * before dividing by percent.
586              */
587             fullwid = (thiswid * 100 + percent - 1) / percent;
588
589             /*
590              * The above calculation assumes every widget gets
591              * cols->spacing on the right. So we subtract
592              * cols->spacing here to account for the extra load of
593              * spacing on the right.
594              */
595             if (retwidth < fullwid - cols->spacing)
596                 retwidth = fullwid - cols->spacing;
597         }
598     }
599
600     retwidth += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
601
602     return retwidth;
603 }
604
605 static void columns_alloc_horiz(Columns *cols, gint ourwidth,
606                                 widget_dim_fn_t get_width)
607 {
608     ColumnsChild *child;
609     GList *children;
610     gint i, ncols, colspan, border, *colxpos, childwidth;
611     const gint *percentages;
612     static const gint onecol[] = { 100 };
613
614     border = gtk_container_get_border_width(GTK_CONTAINER(cols));
615
616     ncols = 1;
617     percentages = onecol;
618     /* colxpos gives the starting x position of each column.
619      * We supply n+1 of them, so that we can find the RH edge easily.
620      * All ending x positions are expected to be adjusted afterwards by
621      * subtracting the spacing. */
622     colxpos = g_new(gint, 2);
623     colxpos[0] = 0;
624     colxpos[1] = ourwidth - 2*border + cols->spacing;
625
626     for (children = cols->children;
627          children && (child = children->data);
628          children = children->next) {
629
630         if (!child->widget) {
631             gint percent;
632
633             /* Column reconfiguration. */
634             ncols = child->ncols;
635             percentages = child->percentages;
636             colxpos = g_renew(gint, colxpos, ncols + 1);
637             colxpos[0] = 0;
638             percent = 0;
639             for (i = 0; i < ncols; i++) {
640                 percent += percentages[i];
641                 colxpos[i+1] = (((ourwidth - 2*border) + cols->spacing)
642                                 * percent / 100);
643             }
644             continue;
645         }
646
647         /* Only take visible widgets into account. */
648         if (!gtk_widget_get_visible(child->widget))
649             continue;
650
651         childwidth = get_width(child);
652         colspan = child->colspan ? child->colspan : ncols-child->colstart;
653
654         /*
655          * Starting x position is cols[colstart].
656          * Ending x position is cols[colstart+colspan] - spacing.
657          * 
658          * Unless we're forcing left, in which case the width is
659          * exactly the requisition width.
660          */
661         child->x = colxpos[child->colstart];
662         if (child->force_left)
663             child->w = childwidth;
664         else
665             child->w = (colxpos[child->colstart+colspan] -
666                         colxpos[child->colstart] - cols->spacing);
667     }
668
669     g_free(colxpos);
670 }
671
672 static gint columns_compute_height(Columns *cols, widget_dim_fn_t get_height)
673 {
674     ColumnsChild *child;
675     GList *children;
676     gint i, ncols, colspan, *colypos, retheight, childheight;
677
678     retheight = cols->spacing;
679
680     ncols = 1;
681     colypos = g_new(gint, 1);
682     colypos[0] = 0;
683
684     for (children = cols->children;
685          children && (child = children->data);
686          children = children->next) {
687
688         if (!child->widget) {
689             /* Column reconfiguration. */
690             for (i = 1; i < ncols; i++) {
691                 if (colypos[0] < colypos[i])
692                     colypos[0] = colypos[i];
693             }
694             ncols = child->ncols;
695             colypos = g_renew(gint, colypos, ncols);
696             for (i = 1; i < ncols; i++)
697                 colypos[i] = colypos[0];
698             continue;
699         }
700
701         /* Only take visible widgets into account. */
702         if (!gtk_widget_get_visible(child->widget))
703             continue;
704
705         childheight = get_height(child);
706         colspan = child->colspan ? child->colspan : ncols-child->colstart;
707
708         /*
709          * To compute height: the widget's top will be positioned at
710          * the largest y value so far reached in any of the columns it
711          * crosses. Then it will go down by childheight plus padding;
712          * and the point it reaches at the bottom is the new y value
713          * in all those columns, and minus the padding it is also a
714          * lower bound on our own height.
715          */
716         {
717             int topy, boty;
718
719             topy = 0;
720             for (i = 0; i < colspan; i++) {
721                 if (topy < colypos[child->colstart+i])
722                     topy = colypos[child->colstart+i];
723             }
724             boty = topy + childheight + cols->spacing;
725             for (i = 0; i < colspan; i++) {
726                 colypos[child->colstart+i] = boty;
727             }
728
729             if (retheight < boty - cols->spacing)
730                 retheight = boty - cols->spacing;
731         }
732     }
733
734     retheight += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
735
736     g_free(colypos);
737
738     return retheight;
739 }
740
741 static void columns_alloc_vert(Columns *cols, gint ourheight,
742                                widget_dim_fn_t get_height)
743 {
744     ColumnsChild *child;
745     GList *children;
746     gint i, ncols, colspan, *colypos, childheight;
747
748     ncols = 1;
749     /* As in size_request, colypos is the lowest y reached in each column. */
750     colypos = g_new(gint, 1);
751     colypos[0] = 0;
752
753     for (children = cols->children;
754          children && (child = children->data);
755          children = children->next) {
756         if (!child->widget) {
757             /* Column reconfiguration. */
758             for (i = 1; i < ncols; i++) {
759                 if (colypos[0] < colypos[i])
760                     colypos[0] = colypos[i];
761             }
762             ncols = child->ncols;
763             colypos = g_renew(gint, colypos, ncols);
764             for (i = 1; i < ncols; i++)
765                 colypos[i] = colypos[0];
766             continue;
767         }
768
769         /* Only take visible widgets into account. */
770         if (!gtk_widget_get_visible(child->widget))
771             continue;
772
773         childheight = get_height(child);
774         colspan = child->colspan ? child->colspan : ncols-child->colstart;
775
776         /*
777          * To compute height: the widget's top will be positioned
778          * at the largest y value so far reached in any of the
779          * columns it crosses. Then it will go down by creq.height
780          * plus padding; and the point it reaches at the bottom is
781          * the new y value in all those columns.
782          */
783         {
784             int topy, boty;
785
786             topy = 0;
787             for (i = 0; i < colspan; i++) {
788                 if (topy < colypos[child->colstart+i])
789                     topy = colypos[child->colstart+i];
790             }
791             child->y = topy;
792             child->h = childheight;
793             boty = topy + childheight + cols->spacing;
794             for (i = 0; i < colspan; i++) {
795                 colypos[child->colstart+i] = boty;
796             }
797         }
798     }
799
800     g_free(colypos);    
801 }
802
803 /*
804  * Now here comes the interesting bit. The actual layout part is
805  * done in the following two functions:
806  *
807  * columns_size_request() examines the list of widgets held in the
808  * Columns, and returns a requisition stating the absolute minimum
809  * size it can bear to be.
810  *
811  * columns_size_allocate() is given an allocation telling it what
812  * size the whole container is going to be, and it calls
813  * gtk_widget_size_allocate() on all of its (visible) children to
814  * set their size and position relative to the top left of the
815  * container.
816  */
817
818 static gint columns_gtk2_get_width(ColumnsChild *child)
819 {
820     GtkRequisition creq;
821     gtk_widget_size_request(child->widget, &creq);
822     return creq.width;
823 }
824
825 static gint columns_gtk2_get_height(ColumnsChild *child)
826 {
827     GtkRequisition creq;
828     gtk_widget_size_request(child->widget, &creq);
829     return creq.height;
830 }
831
832 static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
833 {
834     Columns *cols;
835
836     g_return_if_fail(widget != NULL);
837     g_return_if_fail(IS_COLUMNS(widget));
838     g_return_if_fail(req != NULL);
839
840     cols = COLUMNS(widget);
841
842     req->width = columns_compute_width(cols, columns_gtk2_get_width);
843     req->height = columns_compute_height(cols, columns_gtk2_get_height);
844 }
845
846 #if GTK_CHECK_VERSION(3,0,0)
847 static void columns_get_preferred_width(GtkWidget *widget,
848                                         gint *min, gint *nat)
849 {
850     GtkRequisition req;
851     columns_size_request(widget, &req);
852     *min = *nat = req.width;
853 }
854
855 static void columns_get_preferred_height(GtkWidget *widget,
856                                         gint *min, gint *nat)
857 {
858     GtkRequisition req;
859     columns_size_request(widget, &req);
860     *min = *nat = req.height;
861 }
862 #endif
863
864 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
865 {
866     Columns *cols;
867     ColumnsChild *child;
868     GList *children;
869     gint border;
870
871     g_return_if_fail(widget != NULL);
872     g_return_if_fail(IS_COLUMNS(widget));
873     g_return_if_fail(alloc != NULL);
874
875     cols = COLUMNS(widget);
876     gtk_widget_set_allocation(widget, alloc);
877
878     border = gtk_container_get_border_width(GTK_CONTAINER(cols));
879
880     columns_alloc_horiz(cols, alloc->width, columns_gtk2_get_width);
881     columns_alloc_vert(cols, alloc->height, columns_gtk2_get_height);
882
883     for (children = cols->children;
884          children && (child = children->data);
885          children = children->next) {
886         if (child->widget && gtk_widget_get_visible(child->widget)) {
887             GtkAllocation call;
888             call.x = alloc->x + border + child->x;
889             call.y = alloc->y + border + child->y;
890             call.width = child->w;
891             call.height = child->h;
892             gtk_widget_size_allocate(child->widget, &call);
893         }
894     }
895 }