2 * gtkcols.c - implementation of the `Columns' GTK layout container.
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);
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);
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 static void columns_get_preferred_width_for_height(GtkWidget *widget,
32 gint *min, gint *nat);
33 static void columns_get_preferred_height_for_width(GtkWidget *widget,
35 gint *min, gint *nat);
37 static void columns_size_request(GtkWidget *widget, GtkRequisition *req);
39 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc);
41 static GtkContainerClass *parent_class = NULL;
43 #if !GTK_CHECK_VERSION(2,0,0)
44 GType columns_get_type(void)
46 static GType columns_type = 0;
49 static const GtkTypeInfo columns_info = {
53 (GtkClassInitFunc) columns_class_init,
54 (GtkObjectInitFunc) columns_init,
55 /* reserved_1 */ NULL,
56 /* reserved_2 */ NULL,
57 (GtkClassInitFunc) NULL,
60 columns_type = gtk_type_unique(GTK_TYPE_CONTAINER, &columns_info);
66 GType columns_get_type(void)
68 static GType columns_type = 0;
71 static const GTypeInfo columns_info = {
75 (GClassInitFunc) columns_class_init,
80 (GInstanceInitFunc)columns_init,
83 columns_type = g_type_register_static(GTK_TYPE_CONTAINER, "Columns",
91 #if !GTK_CHECK_VERSION(2,0,0)
92 static gint (*columns_inherited_focus)(GtkContainer *container,
93 GtkDirectionType direction);
96 static void columns_class_init(ColumnsClass *klass)
98 #if !GTK_CHECK_VERSION(2,0,0)
99 /* GtkObjectClass *object_class = (GtkObjectClass *)klass; */
100 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
101 GtkContainerClass *container_class = (GtkContainerClass *)klass;
103 /* GObjectClass *object_class = G_OBJECT_CLASS(klass); */
104 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
105 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
108 #if !GTK_CHECK_VERSION(2,0,0)
109 parent_class = gtk_type_class(GTK_TYPE_CONTAINER);
111 parent_class = g_type_class_peek_parent(klass);
114 widget_class->map = columns_map;
115 widget_class->unmap = columns_unmap;
116 #if !GTK_CHECK_VERSION(2,0,0)
117 widget_class->draw = columns_draw;
118 widget_class->expose_event = columns_expose;
120 #if GTK_CHECK_VERSION(3,0,0)
121 widget_class->get_preferred_width = columns_get_preferred_width;
122 widget_class->get_preferred_height = columns_get_preferred_height;
123 widget_class->get_preferred_width_for_height =
124 columns_get_preferred_width_for_height;
125 widget_class->get_preferred_height_for_width =
126 columns_get_preferred_height_for_width;
128 widget_class->size_request = columns_size_request;
130 widget_class->size_allocate = columns_size_allocate;
132 container_class->add = columns_base_add;
133 container_class->remove = columns_remove;
134 container_class->forall = columns_forall;
135 container_class->child_type = columns_child_type;
136 #if !GTK_CHECK_VERSION(2,0,0)
137 /* Save the previous value of this method. */
138 if (!columns_inherited_focus)
139 columns_inherited_focus = container_class->focus;
140 container_class->focus = columns_focus;
144 static void columns_init(Columns *cols)
146 gtk_widget_set_has_window(GTK_WIDGET(cols), FALSE);
148 cols->children = NULL;
153 * These appear to be thoroughly tedious functions; the only reason
154 * we have to reimplement them at all is because we defined our own
155 * format for our GList of children...
157 static void columns_map(GtkWidget *widget)
163 g_return_if_fail(widget != NULL);
164 g_return_if_fail(IS_COLUMNS(widget));
166 cols = COLUMNS(widget);
167 gtk_widget_set_mapped(GTK_WIDGET(cols), TRUE);
169 for (children = cols->children;
170 children && (child = children->data);
171 children = children->next) {
173 gtk_widget_get_visible(child->widget) &&
174 !gtk_widget_get_mapped(child->widget))
175 gtk_widget_map(child->widget);
178 static void columns_unmap(GtkWidget *widget)
184 g_return_if_fail(widget != NULL);
185 g_return_if_fail(IS_COLUMNS(widget));
187 cols = COLUMNS(widget);
188 gtk_widget_set_mapped(GTK_WIDGET(cols), FALSE);
190 for (children = cols->children;
191 children && (child = children->data);
192 children = children->next) {
194 gtk_widget_get_visible(child->widget) &&
195 gtk_widget_get_mapped(child->widget))
196 gtk_widget_unmap(child->widget);
199 #if !GTK_CHECK_VERSION(2,0,0)
200 static void columns_draw(GtkWidget *widget, GdkRectangle *area)
205 GdkRectangle child_area;
207 g_return_if_fail(widget != NULL);
208 g_return_if_fail(IS_COLUMNS(widget));
210 if (GTK_WIDGET_DRAWABLE(widget)) {
211 cols = COLUMNS(widget);
213 for (children = cols->children;
214 children && (child = children->data);
215 children = children->next) {
217 GTK_WIDGET_DRAWABLE(child->widget) &&
218 gtk_widget_intersect(child->widget, area, &child_area))
219 gtk_widget_draw(child->widget, &child_area);
223 static gint columns_expose(GtkWidget *widget, GdkEventExpose *event)
228 GdkEventExpose child_event;
230 g_return_val_if_fail(widget != NULL, FALSE);
231 g_return_val_if_fail(IS_COLUMNS(widget), FALSE);
232 g_return_val_if_fail(event != NULL, FALSE);
234 if (GTK_WIDGET_DRAWABLE(widget)) {
235 cols = COLUMNS(widget);
236 child_event = *event;
238 for (children = cols->children;
239 children && (child = children->data);
240 children = children->next) {
242 GTK_WIDGET_DRAWABLE(child->widget) &&
243 GTK_WIDGET_NO_WINDOW(child->widget) &&
244 gtk_widget_intersect(child->widget, &event->area,
246 gtk_widget_event(child->widget, (GdkEvent *)&child_event);
253 static void columns_base_add(GtkContainer *container, GtkWidget *widget)
257 g_return_if_fail(container != NULL);
258 g_return_if_fail(IS_COLUMNS(container));
259 g_return_if_fail(widget != NULL);
261 cols = COLUMNS(container);
264 * Default is to add a new widget spanning all columns.
266 columns_add(cols, widget, 0, 0); /* 0 means ncols */
269 static void columns_remove(GtkContainer *container, GtkWidget *widget)
275 gboolean was_visible;
277 g_return_if_fail(container != NULL);
278 g_return_if_fail(IS_COLUMNS(container));
279 g_return_if_fail(widget != NULL);
281 cols = COLUMNS(container);
283 for (children = cols->children;
284 children && (child = children->data);
285 children = children->next) {
286 if (child->widget != widget)
289 was_visible = gtk_widget_get_visible(widget);
290 gtk_widget_unparent(widget);
291 cols->children = g_list_remove_link(cols->children, children);
292 g_list_free(children);
294 if (child->same_height_as) {
295 g_return_if_fail(child->same_height_as->same_height_as == child);
296 child->same_height_as->same_height_as = NULL;
297 if (gtk_widget_get_visible(child->same_height_as->widget))
298 gtk_widget_queue_resize(GTK_WIDGET(container));
303 gtk_widget_queue_resize(GTK_WIDGET(container));
307 for (children = cols->taborder;
308 children && (childw = children->data);
309 children = children->next) {
310 if (childw != widget)
313 cols->taborder = g_list_remove_link(cols->taborder, children);
314 g_list_free(children);
315 #if GTK_CHECK_VERSION(2,0,0)
316 gtk_container_set_focus_chain(container, cols->taborder);
322 static void columns_forall(GtkContainer *container, gboolean include_internals,
323 GtkCallback callback, gpointer callback_data)
327 GList *children, *next;
329 g_return_if_fail(container != NULL);
330 g_return_if_fail(IS_COLUMNS(container));
331 g_return_if_fail(callback != NULL);
333 cols = COLUMNS(container);
335 for (children = cols->children;
336 children && (child = children->data);
339 * We can't wait until after the callback to assign
340 * `children = children->next', because the callback might
341 * be gtk_widget_destroy, which would remove the link
342 * `children' from the list! So instead we must get our
343 * hands on the value of the `next' pointer _before_ the
346 next = children->next;
348 callback(child->widget, callback_data);
352 static GType columns_child_type(GtkContainer *container)
354 return GTK_TYPE_WIDGET;
357 GtkWidget *columns_new(gint spacing)
361 #if !GTK_CHECK_VERSION(2,0,0)
362 cols = gtk_type_new(columns_get_type());
364 cols = g_object_new(TYPE_COLUMNS, NULL);
367 cols->spacing = spacing;
369 return GTK_WIDGET(cols);
372 void columns_set_cols(Columns *cols, gint ncols, const gint *percentages)
374 ColumnsChild *childdata;
377 g_return_if_fail(cols != NULL);
378 g_return_if_fail(IS_COLUMNS(cols));
379 g_return_if_fail(ncols > 0);
380 g_return_if_fail(percentages != NULL);
382 childdata = g_new(ColumnsChild, 1);
383 childdata->widget = NULL;
384 childdata->ncols = ncols;
385 childdata->percentages = g_new(gint, ncols);
386 childdata->force_left = FALSE;
387 for (i = 0; i < ncols; i++)
388 childdata->percentages[i] = percentages[i];
390 cols->children = g_list_append(cols->children, childdata);
393 void columns_add(Columns *cols, GtkWidget *child,
394 gint colstart, gint colspan)
396 ColumnsChild *childdata;
398 g_return_if_fail(cols != NULL);
399 g_return_if_fail(IS_COLUMNS(cols));
400 g_return_if_fail(child != NULL);
401 g_return_if_fail(gtk_widget_get_parent(child) == NULL);
403 childdata = g_new(ColumnsChild, 1);
404 childdata->widget = child;
405 childdata->colstart = colstart;
406 childdata->colspan = colspan;
407 childdata->force_left = FALSE;
408 childdata->same_height_as = NULL;
410 cols->children = g_list_append(cols->children, childdata);
411 cols->taborder = g_list_append(cols->taborder, child);
413 gtk_widget_set_parent(child, GTK_WIDGET(cols));
415 #if GTK_CHECK_VERSION(2,0,0)
416 gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
419 if (gtk_widget_get_realized(GTK_WIDGET(cols)))
420 gtk_widget_realize(child);
422 if (gtk_widget_get_visible(GTK_WIDGET(cols)) &&
423 gtk_widget_get_visible(child)) {
424 if (gtk_widget_get_mapped(GTK_WIDGET(cols)))
425 gtk_widget_map(child);
426 gtk_widget_queue_resize(child);
430 static ColumnsChild *columns_find_child(Columns *cols, GtkWidget *widget)
435 for (children = cols->children;
436 children && (child = children->data);
437 children = children->next) {
439 if (child->widget == widget)
446 void columns_force_left_align(Columns *cols, GtkWidget *widget)
450 g_return_if_fail(cols != NULL);
451 g_return_if_fail(IS_COLUMNS(cols));
452 g_return_if_fail(widget != NULL);
454 child = columns_find_child(cols, widget);
455 g_return_if_fail(child != NULL);
457 child->force_left = TRUE;
458 if (gtk_widget_get_visible(widget))
459 gtk_widget_queue_resize(GTK_WIDGET(cols));
462 void columns_force_same_height(Columns *cols, GtkWidget *cw1, GtkWidget *cw2)
464 ColumnsChild *child1, *child2;
466 g_return_if_fail(cols != NULL);
467 g_return_if_fail(IS_COLUMNS(cols));
468 g_return_if_fail(cw1 != NULL);
469 g_return_if_fail(cw2 != NULL);
471 child1 = columns_find_child(cols, cw1);
472 g_return_if_fail(child1 != NULL);
473 child2 = columns_find_child(cols, cw2);
474 g_return_if_fail(child2 != NULL);
476 child1->same_height_as = child2;
477 child2->same_height_as = child1;
478 if (gtk_widget_get_visible(cw1) || gtk_widget_get_visible(cw2))
479 gtk_widget_queue_resize(GTK_WIDGET(cols));
482 void columns_taborder_last(Columns *cols, GtkWidget *widget)
487 g_return_if_fail(cols != NULL);
488 g_return_if_fail(IS_COLUMNS(cols));
489 g_return_if_fail(widget != NULL);
491 for (children = cols->taborder;
492 children && (childw = children->data);
493 children = children->next) {
494 if (childw != widget)
497 cols->taborder = g_list_remove_link(cols->taborder, children);
498 g_list_free(children);
499 cols->taborder = g_list_append(cols->taborder, widget);
500 #if GTK_CHECK_VERSION(2,0,0)
501 gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
507 #if !GTK_CHECK_VERSION(2,0,0)
509 * Override GtkContainer's focus movement so the user can
510 * explicitly specify the tab order.
512 static gint columns_focus(GtkContainer *container, GtkDirectionType dir)
516 GtkWidget *focuschild;
518 g_return_val_if_fail(container != NULL, FALSE);
519 g_return_val_if_fail(IS_COLUMNS(container), FALSE);
521 cols = COLUMNS(container);
523 if (!GTK_WIDGET_DRAWABLE(cols) ||
524 !GTK_WIDGET_IS_SENSITIVE(cols))
527 if (!GTK_WIDGET_CAN_FOCUS(container) &&
528 (dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) {
530 focuschild = container->focus_child;
531 gtk_container_set_focus_child(container, NULL);
533 if (dir == GTK_DIR_TAB_FORWARD)
534 pos = cols->taborder;
536 pos = g_list_last(cols->taborder);
539 GtkWidget *child = pos->data;
542 if (focuschild == child) {
543 focuschild = NULL; /* now we can start looking in here */
544 if (GTK_WIDGET_DRAWABLE(child) &&
545 GTK_IS_CONTAINER(child) &&
546 !GTK_WIDGET_HAS_FOCUS(child)) {
547 if (gtk_container_focus(GTK_CONTAINER(child), dir))
551 } else if (GTK_WIDGET_DRAWABLE(child)) {
552 if (GTK_IS_CONTAINER(child)) {
553 if (gtk_container_focus(GTK_CONTAINER(child), dir))
555 } else if (GTK_WIDGET_CAN_FOCUS(child)) {
556 gtk_widget_grab_focus(child);
561 if (dir == GTK_DIR_TAB_FORWARD)
569 return columns_inherited_focus(container, dir);
574 * Underlying parts of the layout algorithm, to compute the Columns
575 * container's width or height given the widths or heights of its
576 * children. These will be called in various ways with different
577 * notions of width and height in use, so we abstract them out and
578 * pass them a 'get width' or 'get height' function pointer.
581 typedef gint (*widget_dim_fn_t)(ColumnsChild *child);
583 static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
587 gint i, ncols, colspan, retwidth, childwidth;
588 const gint *percentages;
589 static const gint onecol[] = { 100 };
591 #ifdef COLUMNS_WIDTH_DIAGNOSTICS
592 printf("compute_width(%p): start\n", cols);
598 percentages = onecol;
600 for (children = cols->children;
601 children && (child = children->data);
602 children = children->next) {
604 if (!child->widget) {
605 /* Column reconfiguration. */
606 ncols = child->ncols;
607 percentages = child->percentages;
611 /* Only take visible widgets into account. */
612 if (!gtk_widget_get_visible(child->widget))
615 childwidth = get_width(child);
616 colspan = child->colspan ? child->colspan : ncols-child->colstart;
618 #ifdef COLUMNS_WIDTH_DIAGNOSTICS
619 printf("compute_width(%p): ", cols);
620 if (GTK_IS_LABEL(child->widget))
621 printf("label %p '%s' wrap=%s: ", child->widget,
622 gtk_label_get_text(GTK_LABEL(child->widget)),
623 (gtk_label_get_line_wrap(GTK_LABEL(child->widget))
624 ? "TRUE" : "FALSE"));
626 printf("widget %p: ", child->widget);
629 gtk_widget_get_preferred_width(child->widget, &min, &nat);
630 printf("minwidth=%d natwidth=%d ", min, nat);
632 printf("thiswidth=%d span=%d\n", childwidth, colspan);
636 * To compute width: we know that childwidth + cols->spacing
637 * needs to equal a certain percentage of the full width of
638 * the container. So we work this value out, figure out how
639 * wide the container will need to be to make that percentage
640 * of it equal to that width, and ensure our returned width is
641 * at least that much. Very simple really.
644 int percent, thiswid, fullwid;
647 for (i = 0; i < colspan; i++)
648 percent += percentages[child->colstart+i];
650 thiswid = childwidth + cols->spacing;
652 * Since childwidth is (at least sometimes) the _minimum_
653 * size the child needs, we must ensure that it gets _at
654 * least_ that size. Hence, when scaling thiswid up to
655 * fullwid, we must round up, which means adding percent-1
656 * before dividing by percent.
658 fullwid = (thiswid * 100 + percent - 1) / percent;
659 #ifdef COLUMNS_WIDTH_DIAGNOSTICS
660 printf("compute_width(%p): after %p, thiswid=%d fullwid=%d\n",
661 cols, child->widget, thiswid, fullwid);
665 * The above calculation assumes every widget gets
666 * cols->spacing on the right. So we subtract
667 * cols->spacing here to account for the extra load of
668 * spacing on the right.
670 if (retwidth < fullwid - cols->spacing)
671 retwidth = fullwid - cols->spacing;
675 retwidth += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
677 #ifdef COLUMNS_WIDTH_DIAGNOSTICS
678 printf("compute_width(%p): done, returning %d\n", cols, retwidth);
684 static void columns_alloc_horiz(Columns *cols, gint ourwidth,
685 widget_dim_fn_t get_width)
689 gint i, ncols, colspan, border, *colxpos, childwidth;
690 const gint *percentages;
691 static const gint onecol[] = { 100 };
693 border = gtk_container_get_border_width(GTK_CONTAINER(cols));
696 percentages = onecol;
697 /* colxpos gives the starting x position of each column.
698 * We supply n+1 of them, so that we can find the RH edge easily.
699 * All ending x positions are expected to be adjusted afterwards by
700 * subtracting the spacing. */
701 colxpos = g_new(gint, 2);
703 colxpos[1] = ourwidth - 2*border + cols->spacing;
705 for (children = cols->children;
706 children && (child = children->data);
707 children = children->next) {
709 if (!child->widget) {
712 /* Column reconfiguration. */
713 ncols = child->ncols;
714 percentages = child->percentages;
715 colxpos = g_renew(gint, colxpos, ncols + 1);
718 for (i = 0; i < ncols; i++) {
719 percent += percentages[i];
720 colxpos[i+1] = (((ourwidth - 2*border) + cols->spacing)
726 /* Only take visible widgets into account. */
727 if (!gtk_widget_get_visible(child->widget))
730 childwidth = get_width(child);
731 colspan = child->colspan ? child->colspan : ncols-child->colstart;
734 * Starting x position is cols[colstart].
735 * Ending x position is cols[colstart+colspan] - spacing.
737 * Unless we're forcing left, in which case the width is
738 * exactly the requisition width.
740 child->x = colxpos[child->colstart];
741 if (child->force_left)
742 child->w = childwidth;
744 child->w = (colxpos[child->colstart+colspan] -
745 colxpos[child->colstart] - cols->spacing);
751 static gint columns_compute_height(Columns *cols, widget_dim_fn_t get_height)
755 gint i, ncols, colspan, *colypos, retheight, childheight;
757 retheight = cols->spacing;
760 colypos = g_new(gint, 1);
763 for (children = cols->children;
764 children && (child = children->data);
765 children = children->next) {
767 if (!child->widget) {
768 /* Column reconfiguration. */
769 for (i = 1; i < ncols; i++) {
770 if (colypos[0] < colypos[i])
771 colypos[0] = colypos[i];
773 ncols = child->ncols;
774 colypos = g_renew(gint, colypos, ncols);
775 for (i = 1; i < ncols; i++)
776 colypos[i] = colypos[0];
780 /* Only take visible widgets into account. */
781 if (!gtk_widget_get_visible(child->widget))
784 childheight = get_height(child);
785 if (child->same_height_as) {
786 gint childheight2 = get_height(child->same_height_as);
787 if (childheight < childheight2)
788 childheight = childheight2;
790 colspan = child->colspan ? child->colspan : ncols-child->colstart;
793 * To compute height: the widget's top will be positioned at
794 * the largest y value so far reached in any of the columns it
795 * crosses. Then it will go down by childheight plus padding;
796 * and the point it reaches at the bottom is the new y value
797 * in all those columns, and minus the padding it is also a
798 * lower bound on our own height.
804 for (i = 0; i < colspan; i++) {
805 if (topy < colypos[child->colstart+i])
806 topy = colypos[child->colstart+i];
808 boty = topy + childheight + cols->spacing;
809 for (i = 0; i < colspan; i++) {
810 colypos[child->colstart+i] = boty;
813 if (retheight < boty - cols->spacing)
814 retheight = boty - cols->spacing;
818 retheight += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
825 static void columns_alloc_vert(Columns *cols, gint ourheight,
826 widget_dim_fn_t get_height)
830 gint i, ncols, colspan, *colypos, realheight, fakeheight;
833 /* As in size_request, colypos is the lowest y reached in each column. */
834 colypos = g_new(gint, 1);
837 for (children = cols->children;
838 children && (child = children->data);
839 children = children->next) {
840 if (!child->widget) {
841 /* Column reconfiguration. */
842 for (i = 1; i < ncols; i++) {
843 if (colypos[0] < colypos[i])
844 colypos[0] = colypos[i];
846 ncols = child->ncols;
847 colypos = g_renew(gint, colypos, ncols);
848 for (i = 1; i < ncols; i++)
849 colypos[i] = colypos[0];
853 /* Only take visible widgets into account. */
854 if (!gtk_widget_get_visible(child->widget))
857 realheight = fakeheight = get_height(child);
858 if (child->same_height_as) {
859 gint childheight2 = get_height(child->same_height_as);
860 if (fakeheight < childheight2)
861 fakeheight = childheight2;
863 colspan = child->colspan ? child->colspan : ncols-child->colstart;
866 * To compute height: the widget's top will be positioned
867 * at the largest y value so far reached in any of the
868 * columns it crosses. Then it will go down by creq.height
869 * plus padding; and the point it reaches at the bottom is
870 * the new y value in all those columns.
876 for (i = 0; i < colspan; i++) {
877 if (topy < colypos[child->colstart+i])
878 topy = colypos[child->colstart+i];
880 child->y = topy + fakeheight/2 - realheight/2;
881 child->h = realheight;
882 boty = topy + fakeheight + cols->spacing;
883 for (i = 0; i < colspan; i++) {
884 colypos[child->colstart+i] = boty;
893 * Now here comes the interesting bit. The actual layout part is
894 * done in the following two functions:
896 * columns_size_request() examines the list of widgets held in the
897 * Columns, and returns a requisition stating the absolute minimum
898 * size it can bear to be.
900 * columns_size_allocate() is given an allocation telling it what
901 * size the whole container is going to be, and it calls
902 * gtk_widget_size_allocate() on all of its (visible) children to
903 * set their size and position relative to the top left of the
907 #if !GTK_CHECK_VERSION(3,0,0)
909 static gint columns_gtk2_get_width(ColumnsChild *child)
912 gtk_widget_size_request(child->widget, &creq);
916 static gint columns_gtk2_get_height(ColumnsChild *child)
919 gtk_widget_size_request(child->widget, &creq);
923 static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
927 g_return_if_fail(widget != NULL);
928 g_return_if_fail(IS_COLUMNS(widget));
929 g_return_if_fail(req != NULL);
931 cols = COLUMNS(widget);
933 req->width = columns_compute_width(cols, columns_gtk2_get_width);
934 req->height = columns_compute_height(cols, columns_gtk2_get_height);
937 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
944 g_return_if_fail(widget != NULL);
945 g_return_if_fail(IS_COLUMNS(widget));
946 g_return_if_fail(alloc != NULL);
948 cols = COLUMNS(widget);
949 gtk_widget_set_allocation(widget, alloc);
951 border = gtk_container_get_border_width(GTK_CONTAINER(cols));
953 columns_alloc_horiz(cols, alloc->width, columns_gtk2_get_width);
954 columns_alloc_vert(cols, alloc->height, columns_gtk2_get_height);
956 for (children = cols->children;
957 children && (child = children->data);
958 children = children->next) {
959 if (child->widget && gtk_widget_get_visible(child->widget)) {
961 call.x = alloc->x + border + child->x;
962 call.y = alloc->y + border + child->y;
963 call.width = child->w;
964 call.height = child->h;
965 gtk_widget_size_allocate(child->widget, &call);
970 #else /* GTK_CHECK_VERSION(3,0,0) */
972 static gint columns_gtk3_get_min_width(ColumnsChild *child)
975 gtk_widget_get_preferred_width(child->widget, &ret, NULL);
979 static gint columns_gtk3_get_nat_width(ColumnsChild *child)
983 if ((GTK_IS_LABEL(child->widget) &&
984 gtk_label_get_line_wrap(GTK_LABEL(child->widget))) ||
985 GTK_IS_ENTRY(child->widget)) {
987 * We treat wrapping GtkLabels as a special case in this
988 * layout class, because the whole point of those is that I
989 * _don't_ want them to take up extra horizontal space for
990 * long text, but instead to wrap it to whatever size is used
991 * by the rest of the layout.
993 * GtkEntry gets similar treatment, because in OS X GTK I've
994 * found that it requests a natural width regardless of the
995 * output of gtk_entry_set_width_chars.
997 gtk_widget_get_preferred_width(child->widget, &ret, NULL);
999 gtk_widget_get_preferred_width(child->widget, NULL, &ret);
1004 static gint columns_gtk3_get_minfh_width(ColumnsChild *child)
1007 gtk_widget_get_preferred_width_for_height(child->widget, child->h,
1012 static gint columns_gtk3_get_natfh_width(ColumnsChild *child)
1015 gtk_widget_get_preferred_width_for_height(child->widget, child->h,
1020 static gint columns_gtk3_get_min_height(ColumnsChild *child)
1023 gtk_widget_get_preferred_height(child->widget, &ret, NULL);
1027 static gint columns_gtk3_get_nat_height(ColumnsChild *child)
1030 gtk_widget_get_preferred_height(child->widget, NULL, &ret);
1034 static gint columns_gtk3_get_minfw_height(ColumnsChild *child)
1037 gtk_widget_get_preferred_height_for_width(child->widget, child->w,
1042 static gint columns_gtk3_get_natfw_height(ColumnsChild *child)
1045 gtk_widget_get_preferred_height_for_width(child->widget, child->w,
1050 static void columns_get_preferred_width(GtkWidget *widget,
1051 gint *min, gint *nat)
1055 g_return_if_fail(widget != NULL);
1056 g_return_if_fail(IS_COLUMNS(widget));
1058 cols = COLUMNS(widget);
1061 *min = columns_compute_width(cols, columns_gtk3_get_min_width);
1063 *nat = columns_compute_width(cols, columns_gtk3_get_nat_width);
1066 static void columns_get_preferred_height(GtkWidget *widget,
1067 gint *min, gint *nat)
1071 g_return_if_fail(widget != NULL);
1072 g_return_if_fail(IS_COLUMNS(widget));
1074 cols = COLUMNS(widget);
1077 *min = columns_compute_height(cols, columns_gtk3_get_min_height);
1079 *nat = columns_compute_height(cols, columns_gtk3_get_nat_height);
1082 static void columns_get_preferred_width_for_height(GtkWidget *widget,
1084 gint *min, gint *nat)
1088 g_return_if_fail(widget != NULL);
1089 g_return_if_fail(IS_COLUMNS(widget));
1091 cols = COLUMNS(widget);
1093 /* FIXME: which one should the get-height function here be? */
1094 columns_alloc_vert(cols, height, columns_gtk3_get_nat_height);
1097 *min = columns_compute_width(cols, columns_gtk3_get_minfh_width);
1099 *nat = columns_compute_width(cols, columns_gtk3_get_natfh_width);
1102 static void columns_get_preferred_height_for_width(GtkWidget *widget,
1104 gint *min, gint *nat)
1108 g_return_if_fail(widget != NULL);
1109 g_return_if_fail(IS_COLUMNS(widget));
1111 cols = COLUMNS(widget);
1113 /* FIXME: which one should the get-height function here be? */
1114 columns_alloc_horiz(cols, width, columns_gtk3_get_nat_width);
1117 *min = columns_compute_height(cols, columns_gtk3_get_minfw_height);
1119 *nat = columns_compute_height(cols, columns_gtk3_get_natfw_height);
1122 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
1125 ColumnsChild *child;
1129 g_return_if_fail(widget != NULL);
1130 g_return_if_fail(IS_COLUMNS(widget));
1131 g_return_if_fail(alloc != NULL);
1133 cols = COLUMNS(widget);
1134 gtk_widget_set_allocation(widget, alloc);
1136 border = gtk_container_get_border_width(GTK_CONTAINER(cols));
1138 columns_alloc_horiz(cols, alloc->width, columns_gtk3_get_min_width);
1139 columns_alloc_vert(cols, alloc->height, columns_gtk3_get_minfw_height);
1141 for (children = cols->children;
1142 children && (child = children->data);
1143 children = children->next) {
1144 if (child->widget && gtk_widget_get_visible(child->widget)) {
1146 call.x = alloc->x + border + child->x;
1147 call.y = alloc->y + border + child->y;
1148 call.width = child->w;
1149 call.height = child->h;
1150 gtk_widget_size_allocate(child->widget, &call);