1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156 |
- /*
- * gtkcols.c - implementation of the `Columns' GTK layout container.
- */
- #include <gtk/gtk.h>
- #include "gtkcompat.h"
- #include "gtkcols.h"
- static void columns_init(Columns *cols);
- static void columns_class_init(ColumnsClass *klass);
- static void columns_map(GtkWidget *widget);
- static void columns_unmap(GtkWidget *widget);
- #if !GTK_CHECK_VERSION(2,0,0)
- static void columns_draw(GtkWidget *widget, GdkRectangle *area);
- static gint columns_expose(GtkWidget *widget, GdkEventExpose *event);
- #endif
- static void columns_base_add(GtkContainer *container, GtkWidget *widget);
- static void columns_remove(GtkContainer *container, GtkWidget *widget);
- static void columns_forall(GtkContainer *container, gboolean include_internals,
- GtkCallback callback, gpointer callback_data);
- #if !GTK_CHECK_VERSION(2,0,0)
- static gint columns_focus(GtkContainer *container, GtkDirectionType dir);
- #endif
- static GType columns_child_type(GtkContainer *container);
- #if GTK_CHECK_VERSION(3,0,0)
- static void columns_get_preferred_width(GtkWidget *widget,
- gint *min, gint *nat);
- static void columns_get_preferred_height(GtkWidget *widget,
- gint *min, gint *nat);
- static void columns_get_preferred_width_for_height(GtkWidget *widget,
- gint height,
- gint *min, gint *nat);
- static void columns_get_preferred_height_for_width(GtkWidget *widget,
- gint width,
- gint *min, gint *nat);
- #else
- static void columns_size_request(GtkWidget *widget, GtkRequisition *req);
- #endif
- static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc);
- static GtkContainerClass *parent_class = NULL;
- #if !GTK_CHECK_VERSION(2,0,0)
- GType columns_get_type(void)
- {
- static GType columns_type = 0;
- if (!columns_type) {
- static const GtkTypeInfo columns_info = {
- "Columns",
- sizeof(Columns),
- sizeof(ColumnsClass),
- (GtkClassInitFunc) columns_class_init,
- (GtkObjectInitFunc) columns_init,
- /* reserved_1 */ NULL,
- /* reserved_2 */ NULL,
- (GtkClassInitFunc) NULL,
- };
- columns_type = gtk_type_unique(GTK_TYPE_CONTAINER, &columns_info);
- }
- return columns_type;
- }
- #else
- GType columns_get_type(void)
- {
- static GType columns_type = 0;
- if (!columns_type) {
- static const GTypeInfo columns_info = {
- sizeof(ColumnsClass),
- NULL,
- NULL,
- (GClassInitFunc) columns_class_init,
- NULL,
- NULL,
- sizeof(Columns),
- 0,
- (GInstanceInitFunc)columns_init,
- };
- columns_type = g_type_register_static(GTK_TYPE_CONTAINER, "Columns",
- &columns_info, 0);
- }
- return columns_type;
- }
- #endif
- #if !GTK_CHECK_VERSION(2,0,0)
- static gint (*columns_inherited_focus)(GtkContainer *container,
- GtkDirectionType direction);
- #endif
- static void columns_class_init(ColumnsClass *klass)
- {
- #if !GTK_CHECK_VERSION(2,0,0)
- /* GtkObjectClass *object_class = (GtkObjectClass *)klass; */
- GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
- GtkContainerClass *container_class = (GtkContainerClass *)klass;
- #else
- /* GObjectClass *object_class = G_OBJECT_CLASS(klass); */
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
- GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
- #endif
- #if !GTK_CHECK_VERSION(2,0,0)
- parent_class = gtk_type_class(GTK_TYPE_CONTAINER);
- #else
- parent_class = g_type_class_peek_parent(klass);
- #endif
- widget_class->map = columns_map;
- widget_class->unmap = columns_unmap;
- #if !GTK_CHECK_VERSION(2,0,0)
- widget_class->draw = columns_draw;
- widget_class->expose_event = columns_expose;
- #endif
- #if GTK_CHECK_VERSION(3,0,0)
- widget_class->get_preferred_width = columns_get_preferred_width;
- widget_class->get_preferred_height = columns_get_preferred_height;
- widget_class->get_preferred_width_for_height =
- columns_get_preferred_width_for_height;
- widget_class->get_preferred_height_for_width =
- columns_get_preferred_height_for_width;
- #else
- widget_class->size_request = columns_size_request;
- #endif
- widget_class->size_allocate = columns_size_allocate;
- container_class->add = columns_base_add;
- container_class->remove = columns_remove;
- container_class->forall = columns_forall;
- container_class->child_type = columns_child_type;
- #if !GTK_CHECK_VERSION(2,0,0)
- /* Save the previous value of this method. */
- if (!columns_inherited_focus)
- columns_inherited_focus = container_class->focus;
- container_class->focus = columns_focus;
- #endif
- }
- static void columns_init(Columns *cols)
- {
- gtk_widget_set_has_window(GTK_WIDGET(cols), FALSE);
- cols->children = NULL;
- cols->spacing = 0;
- }
- /*
- * These appear to be thoroughly tedious functions; the only reason
- * we have to reimplement them at all is because we defined our own
- * format for our GList of children...
- */
- static void columns_map(GtkWidget *widget)
- {
- Columns *cols;
- ColumnsChild *child;
- GList *children;
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- cols = COLUMNS(widget);
- gtk_widget_set_mapped(GTK_WIDGET(cols), TRUE);
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget &&
- gtk_widget_get_visible(child->widget) &&
- !gtk_widget_get_mapped(child->widget))
- gtk_widget_map(child->widget);
- }
- }
- static void columns_unmap(GtkWidget *widget)
- {
- Columns *cols;
- ColumnsChild *child;
- GList *children;
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- cols = COLUMNS(widget);
- gtk_widget_set_mapped(GTK_WIDGET(cols), FALSE);
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget &&
- gtk_widget_get_visible(child->widget) &&
- gtk_widget_get_mapped(child->widget))
- gtk_widget_unmap(child->widget);
- }
- }
- #if !GTK_CHECK_VERSION(2,0,0)
- static void columns_draw(GtkWidget *widget, GdkRectangle *area)
- {
- Columns *cols;
- ColumnsChild *child;
- GList *children;
- GdkRectangle child_area;
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- if (GTK_WIDGET_DRAWABLE(widget)) {
- cols = COLUMNS(widget);
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget &&
- GTK_WIDGET_DRAWABLE(child->widget) &&
- gtk_widget_intersect(child->widget, area, &child_area))
- gtk_widget_draw(child->widget, &child_area);
- }
- }
- }
- static gint columns_expose(GtkWidget *widget, GdkEventExpose *event)
- {
- Columns *cols;
- ColumnsChild *child;
- GList *children;
- GdkEventExpose child_event;
- g_return_val_if_fail(widget != NULL, FALSE);
- g_return_val_if_fail(IS_COLUMNS(widget), FALSE);
- g_return_val_if_fail(event != NULL, FALSE);
- if (GTK_WIDGET_DRAWABLE(widget)) {
- cols = COLUMNS(widget);
- child_event = *event;
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget &&
- GTK_WIDGET_DRAWABLE(child->widget) &&
- GTK_WIDGET_NO_WINDOW(child->widget) &&
- gtk_widget_intersect(child->widget, &event->area,
- &child_event.area))
- gtk_widget_event(child->widget, (GdkEvent *)&child_event);
- }
- }
- return FALSE;
- }
- #endif
- static void columns_base_add(GtkContainer *container, GtkWidget *widget)
- {
- Columns *cols;
- g_return_if_fail(container != NULL);
- g_return_if_fail(IS_COLUMNS(container));
- g_return_if_fail(widget != NULL);
- cols = COLUMNS(container);
- /*
- * Default is to add a new widget spanning all columns.
- */
- columns_add(cols, widget, 0, 0); /* 0 means ncols */
- }
- static void columns_remove(GtkContainer *container, GtkWidget *widget)
- {
- Columns *cols;
- ColumnsChild *child;
- GtkWidget *childw;
- GList *children;
- gboolean was_visible;
- g_return_if_fail(container != NULL);
- g_return_if_fail(IS_COLUMNS(container));
- g_return_if_fail(widget != NULL);
- cols = COLUMNS(container);
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget != widget)
- continue;
- was_visible = gtk_widget_get_visible(widget);
- gtk_widget_unparent(widget);
- cols->children = g_list_remove_link(cols->children, children);
- g_list_free(children);
- if (child->same_height_as) {
- g_return_if_fail(child->same_height_as->same_height_as == child);
- child->same_height_as->same_height_as = NULL;
- if (gtk_widget_get_visible(child->same_height_as->widget))
- gtk_widget_queue_resize(GTK_WIDGET(container));
- }
- g_free(child);
- if (was_visible)
- gtk_widget_queue_resize(GTK_WIDGET(container));
- break;
- }
- for (children = cols->taborder;
- children && (childw = children->data);
- children = children->next) {
- if (childw != widget)
- continue;
- cols->taborder = g_list_remove_link(cols->taborder, children);
- g_list_free(children);
- #if GTK_CHECK_VERSION(2,0,0)
- gtk_container_set_focus_chain(container, cols->taborder);
- #endif
- break;
- }
- }
- static void columns_forall(GtkContainer *container, gboolean include_internals,
- GtkCallback callback, gpointer callback_data)
- {
- Columns *cols;
- ColumnsChild *child;
- GList *children, *next;
- g_return_if_fail(container != NULL);
- g_return_if_fail(IS_COLUMNS(container));
- g_return_if_fail(callback != NULL);
- cols = COLUMNS(container);
- for (children = cols->children;
- children && (child = children->data);
- children = next) {
- /*
- * We can't wait until after the callback to assign
- * `children = children->next', because the callback might
- * be gtk_widget_destroy, which would remove the link
- * `children' from the list! So instead we must get our
- * hands on the value of the `next' pointer _before_ the
- * callback.
- */
- next = children->next;
- if (child->widget)
- callback(child->widget, callback_data);
- }
- }
- static GType columns_child_type(GtkContainer *container)
- {
- return GTK_TYPE_WIDGET;
- }
- GtkWidget *columns_new(gint spacing)
- {
- Columns *cols;
- #if !GTK_CHECK_VERSION(2,0,0)
- cols = gtk_type_new(columns_get_type());
- #else
- cols = g_object_new(TYPE_COLUMNS, NULL);
- #endif
- cols->spacing = spacing;
- return GTK_WIDGET(cols);
- }
- void columns_set_cols(Columns *cols, gint ncols, const gint *percentages)
- {
- ColumnsChild *childdata;
- gint i;
- g_return_if_fail(cols != NULL);
- g_return_if_fail(IS_COLUMNS(cols));
- g_return_if_fail(ncols > 0);
- g_return_if_fail(percentages != NULL);
- childdata = g_new(ColumnsChild, 1);
- childdata->widget = NULL;
- childdata->ncols = ncols;
- childdata->percentages = g_new(gint, ncols);
- childdata->force_left = FALSE;
- for (i = 0; i < ncols; i++)
- childdata->percentages[i] = percentages[i];
- cols->children = g_list_append(cols->children, childdata);
- }
- void columns_add(Columns *cols, GtkWidget *child,
- gint colstart, gint colspan)
- {
- ColumnsChild *childdata;
- g_return_if_fail(cols != NULL);
- g_return_if_fail(IS_COLUMNS(cols));
- g_return_if_fail(child != NULL);
- g_return_if_fail(gtk_widget_get_parent(child) == NULL);
- childdata = g_new(ColumnsChild, 1);
- childdata->widget = child;
- childdata->colstart = colstart;
- childdata->colspan = colspan;
- childdata->force_left = FALSE;
- childdata->same_height_as = NULL;
- cols->children = g_list_append(cols->children, childdata);
- cols->taborder = g_list_append(cols->taborder, child);
- gtk_widget_set_parent(child, GTK_WIDGET(cols));
- #if GTK_CHECK_VERSION(2,0,0)
- gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
- #endif
- if (gtk_widget_get_realized(GTK_WIDGET(cols)))
- gtk_widget_realize(child);
- if (gtk_widget_get_visible(GTK_WIDGET(cols)) &&
- gtk_widget_get_visible(child)) {
- if (gtk_widget_get_mapped(GTK_WIDGET(cols)))
- gtk_widget_map(child);
- gtk_widget_queue_resize(child);
- }
- }
- static ColumnsChild *columns_find_child(Columns *cols, GtkWidget *widget)
- {
- GList *children;
- ColumnsChild *child;
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget == widget)
- return child;
- }
- return NULL;
- }
- void columns_force_left_align(Columns *cols, GtkWidget *widget)
- {
- ColumnsChild *child;
- g_return_if_fail(cols != NULL);
- g_return_if_fail(IS_COLUMNS(cols));
- g_return_if_fail(widget != NULL);
- child = columns_find_child(cols, widget);
- g_return_if_fail(child != NULL);
- child->force_left = TRUE;
- if (gtk_widget_get_visible(widget))
- gtk_widget_queue_resize(GTK_WIDGET(cols));
- }
- void columns_force_same_height(Columns *cols, GtkWidget *cw1, GtkWidget *cw2)
- {
- ColumnsChild *child1, *child2;
- g_return_if_fail(cols != NULL);
- g_return_if_fail(IS_COLUMNS(cols));
- g_return_if_fail(cw1 != NULL);
- g_return_if_fail(cw2 != NULL);
- child1 = columns_find_child(cols, cw1);
- g_return_if_fail(child1 != NULL);
- child2 = columns_find_child(cols, cw2);
- g_return_if_fail(child2 != NULL);
- child1->same_height_as = child2;
- child2->same_height_as = child1;
- if (gtk_widget_get_visible(cw1) || gtk_widget_get_visible(cw2))
- gtk_widget_queue_resize(GTK_WIDGET(cols));
- }
- void columns_taborder_last(Columns *cols, GtkWidget *widget)
- {
- GtkWidget *childw;
- GList *children;
- g_return_if_fail(cols != NULL);
- g_return_if_fail(IS_COLUMNS(cols));
- g_return_if_fail(widget != NULL);
- for (children = cols->taborder;
- children && (childw = children->data);
- children = children->next) {
- if (childw != widget)
- continue;
- cols->taborder = g_list_remove_link(cols->taborder, children);
- g_list_free(children);
- cols->taborder = g_list_append(cols->taborder, widget);
- #if GTK_CHECK_VERSION(2,0,0)
- gtk_container_set_focus_chain(GTK_CONTAINER(cols), cols->taborder);
- #endif
- break;
- }
- }
- #if !GTK_CHECK_VERSION(2,0,0)
- /*
- * Override GtkContainer's focus movement so the user can
- * explicitly specify the tab order.
- */
- static gint columns_focus(GtkContainer *container, GtkDirectionType dir)
- {
- Columns *cols;
- GList *pos;
- GtkWidget *focuschild;
- g_return_val_if_fail(container != NULL, FALSE);
- g_return_val_if_fail(IS_COLUMNS(container), FALSE);
- cols = COLUMNS(container);
- if (!GTK_WIDGET_DRAWABLE(cols) ||
- !GTK_WIDGET_IS_SENSITIVE(cols))
- return FALSE;
- if (!GTK_WIDGET_CAN_FOCUS(container) &&
- (dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) {
- focuschild = container->focus_child;
- gtk_container_set_focus_child(container, NULL);
- if (dir == GTK_DIR_TAB_FORWARD)
- pos = cols->taborder;
- else
- pos = g_list_last(cols->taborder);
- while (pos) {
- GtkWidget *child = pos->data;
- if (focuschild) {
- if (focuschild == child) {
- focuschild = NULL; /* now we can start looking in here */
- if (GTK_WIDGET_DRAWABLE(child) &&
- GTK_IS_CONTAINER(child) &&
- !GTK_WIDGET_HAS_FOCUS(child)) {
- if (gtk_container_focus(GTK_CONTAINER(child), dir))
- return TRUE;
- }
- }
- } else if (GTK_WIDGET_DRAWABLE(child)) {
- if (GTK_IS_CONTAINER(child)) {
- if (gtk_container_focus(GTK_CONTAINER(child), dir))
- return TRUE;
- } else if (GTK_WIDGET_CAN_FOCUS(child)) {
- gtk_widget_grab_focus(child);
- return TRUE;
- }
- }
- if (dir == GTK_DIR_TAB_FORWARD)
- pos = pos->next;
- else
- pos = pos->prev;
- }
- return FALSE;
- } else
- return columns_inherited_focus(container, dir);
- }
- #endif
- /*
- * Underlying parts of the layout algorithm, to compute the Columns
- * container's width or height given the widths or heights of its
- * children. These will be called in various ways with different
- * notions of width and height in use, so we abstract them out and
- * pass them a 'get width' or 'get height' function pointer.
- */
- typedef gint (*widget_dim_fn_t)(ColumnsChild *child);
- static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
- {
- ColumnsChild *child;
- GList *children;
- gint i, ncols, colspan, retwidth, childwidth;
- const gint *percentages;
- static const gint onecol[] = { 100 };
- #ifdef COLUMNS_WIDTH_DIAGNOSTICS
- printf("compute_width(%p): start\n", cols);
- #endif
- retwidth = 0;
- ncols = 1;
- percentages = onecol;
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (!child->widget) {
- /* Column reconfiguration. */
- ncols = child->ncols;
- percentages = child->percentages;
- continue;
- }
- /* Only take visible widgets into account. */
- if (!gtk_widget_get_visible(child->widget))
- continue;
- childwidth = get_width(child);
- colspan = child->colspan ? child->colspan : ncols-child->colstart;
- #ifdef COLUMNS_WIDTH_DIAGNOSTICS
- printf("compute_width(%p): ", cols);
- if (GTK_IS_LABEL(child->widget))
- printf("label %p '%s' wrap=%s: ", child->widget,
- gtk_label_get_text(GTK_LABEL(child->widget)),
- (gtk_label_get_line_wrap(GTK_LABEL(child->widget))
- ? "TRUE" : "FALSE"));
- else
- printf("widget %p: ", child->widget);
- {
- gint min, nat;
- gtk_widget_get_preferred_width(child->widget, &min, &nat);
- printf("minwidth=%d natwidth=%d ", min, nat);
- }
- printf("thiswidth=%d span=%d\n", childwidth, colspan);
- #endif
- /*
- * To compute width: we know that childwidth + cols->spacing
- * needs to equal a certain percentage of the full width of
- * the container. So we work this value out, figure out how
- * wide the container will need to be to make that percentage
- * of it equal to that width, and ensure our returned width is
- * at least that much. Very simple really.
- */
- {
- int percent, thiswid, fullwid;
- percent = 0;
- for (i = 0; i < colspan; i++)
- percent += percentages[child->colstart+i];
- thiswid = childwidth + cols->spacing;
- /*
- * Since childwidth is (at least sometimes) the _minimum_
- * size the child needs, we must ensure that it gets _at
- * least_ that size. Hence, when scaling thiswid up to
- * fullwid, we must round up, which means adding percent-1
- * before dividing by percent.
- */
- fullwid = (thiswid * 100 + percent - 1) / percent;
- #ifdef COLUMNS_WIDTH_DIAGNOSTICS
- printf("compute_width(%p): after %p, thiswid=%d fullwid=%d\n",
- cols, child->widget, thiswid, fullwid);
- #endif
- /*
- * The above calculation assumes every widget gets
- * cols->spacing on the right. So we subtract
- * cols->spacing here to account for the extra load of
- * spacing on the right.
- */
- if (retwidth < fullwid - cols->spacing)
- retwidth = fullwid - cols->spacing;
- }
- }
- retwidth += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
- #ifdef COLUMNS_WIDTH_DIAGNOSTICS
- printf("compute_width(%p): done, returning %d\n", cols, retwidth);
- #endif
- return retwidth;
- }
- static void columns_alloc_horiz(Columns *cols, gint ourwidth,
- widget_dim_fn_t get_width)
- {
- ColumnsChild *child;
- GList *children;
- gint i, ncols, colspan, border, *colxpos, childwidth;
- const gint *percentages;
- static const gint onecol[] = { 100 };
- border = gtk_container_get_border_width(GTK_CONTAINER(cols));
- ncols = 1;
- percentages = onecol;
- /* colxpos gives the starting x position of each column.
- * We supply n+1 of them, so that we can find the RH edge easily.
- * All ending x positions are expected to be adjusted afterwards by
- * subtracting the spacing. */
- colxpos = g_new(gint, 2);
- colxpos[0] = 0;
- colxpos[1] = ourwidth - 2*border + cols->spacing;
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (!child->widget) {
- gint percent;
- /* Column reconfiguration. */
- ncols = child->ncols;
- percentages = child->percentages;
- colxpos = g_renew(gint, colxpos, ncols + 1);
- colxpos[0] = 0;
- percent = 0;
- for (i = 0; i < ncols; i++) {
- percent += percentages[i];
- colxpos[i+1] = (((ourwidth - 2*border) + cols->spacing)
- * percent / 100);
- }
- continue;
- }
- /* Only take visible widgets into account. */
- if (!gtk_widget_get_visible(child->widget))
- continue;
- childwidth = get_width(child);
- colspan = child->colspan ? child->colspan : ncols-child->colstart;
- /*
- * Starting x position is cols[colstart].
- * Ending x position is cols[colstart+colspan] - spacing.
- *
- * Unless we're forcing left, in which case the width is
- * exactly the requisition width.
- */
- child->x = colxpos[child->colstart];
- if (child->force_left)
- child->w = childwidth;
- else
- child->w = (colxpos[child->colstart+colspan] -
- colxpos[child->colstart] - cols->spacing);
- }
- g_free(colxpos);
- }
- static gint columns_compute_height(Columns *cols, widget_dim_fn_t get_height)
- {
- ColumnsChild *child;
- GList *children;
- gint i, ncols, colspan, *colypos, retheight, childheight;
- retheight = cols->spacing;
- ncols = 1;
- colypos = g_new(gint, 1);
- colypos[0] = 0;
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (!child->widget) {
- /* Column reconfiguration. */
- for (i = 1; i < ncols; i++) {
- if (colypos[0] < colypos[i])
- colypos[0] = colypos[i];
- }
- ncols = child->ncols;
- colypos = g_renew(gint, colypos, ncols);
- for (i = 1; i < ncols; i++)
- colypos[i] = colypos[0];
- continue;
- }
- /* Only take visible widgets into account. */
- if (!gtk_widget_get_visible(child->widget))
- continue;
- childheight = get_height(child);
- if (child->same_height_as) {
- gint childheight2 = get_height(child->same_height_as);
- if (childheight < childheight2)
- childheight = childheight2;
- }
- colspan = child->colspan ? child->colspan : ncols-child->colstart;
- /*
- * To compute height: the widget's top will be positioned at
- * the largest y value so far reached in any of the columns it
- * crosses. Then it will go down by childheight plus padding;
- * and the point it reaches at the bottom is the new y value
- * in all those columns, and minus the padding it is also a
- * lower bound on our own height.
- */
- {
- int topy, boty;
- topy = 0;
- for (i = 0; i < colspan; i++) {
- if (topy < colypos[child->colstart+i])
- topy = colypos[child->colstart+i];
- }
- boty = topy + childheight + cols->spacing;
- for (i = 0; i < colspan; i++) {
- colypos[child->colstart+i] = boty;
- }
- if (retheight < boty - cols->spacing)
- retheight = boty - cols->spacing;
- }
- }
- retheight += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
- g_free(colypos);
- return retheight;
- }
- static void columns_alloc_vert(Columns *cols, gint ourheight,
- widget_dim_fn_t get_height)
- {
- ColumnsChild *child;
- GList *children;
- gint i, ncols, colspan, *colypos, realheight, fakeheight;
- ncols = 1;
- /* As in size_request, colypos is the lowest y reached in each column. */
- colypos = g_new(gint, 1);
- colypos[0] = 0;
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (!child->widget) {
- /* Column reconfiguration. */
- for (i = 1; i < ncols; i++) {
- if (colypos[0] < colypos[i])
- colypos[0] = colypos[i];
- }
- ncols = child->ncols;
- colypos = g_renew(gint, colypos, ncols);
- for (i = 1; i < ncols; i++)
- colypos[i] = colypos[0];
- continue;
- }
- /* Only take visible widgets into account. */
- if (!gtk_widget_get_visible(child->widget))
- continue;
- realheight = fakeheight = get_height(child);
- if (child->same_height_as) {
- gint childheight2 = get_height(child->same_height_as);
- if (fakeheight < childheight2)
- fakeheight = childheight2;
- }
- colspan = child->colspan ? child->colspan : ncols-child->colstart;
- /*
- * To compute height: the widget's top will be positioned
- * at the largest y value so far reached in any of the
- * columns it crosses. Then it will go down by creq.height
- * plus padding; and the point it reaches at the bottom is
- * the new y value in all those columns.
- */
- {
- int topy, boty;
- topy = 0;
- for (i = 0; i < colspan; i++) {
- if (topy < colypos[child->colstart+i])
- topy = colypos[child->colstart+i];
- }
- child->y = topy + fakeheight/2 - realheight/2;
- child->h = realheight;
- boty = topy + fakeheight + cols->spacing;
- for (i = 0; i < colspan; i++) {
- colypos[child->colstart+i] = boty;
- }
- }
- }
- g_free(colypos);
- }
- /*
- * Now here comes the interesting bit. The actual layout part is
- * done in the following two functions:
- *
- * columns_size_request() examines the list of widgets held in the
- * Columns, and returns a requisition stating the absolute minimum
- * size it can bear to be.
- *
- * columns_size_allocate() is given an allocation telling it what
- * size the whole container is going to be, and it calls
- * gtk_widget_size_allocate() on all of its (visible) children to
- * set their size and position relative to the top left of the
- * container.
- */
- #if !GTK_CHECK_VERSION(3,0,0)
- static gint columns_gtk2_get_width(ColumnsChild *child)
- {
- GtkRequisition creq;
- gtk_widget_size_request(child->widget, &creq);
- return creq.width;
- }
- static gint columns_gtk2_get_height(ColumnsChild *child)
- {
- GtkRequisition creq;
- gtk_widget_size_request(child->widget, &creq);
- return creq.height;
- }
- static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
- {
- Columns *cols;
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- g_return_if_fail(req != NULL);
- cols = COLUMNS(widget);
- req->width = columns_compute_width(cols, columns_gtk2_get_width);
- req->height = columns_compute_height(cols, columns_gtk2_get_height);
- }
- static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
- {
- Columns *cols;
- ColumnsChild *child;
- GList *children;
- gint border;
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- g_return_if_fail(alloc != NULL);
- cols = COLUMNS(widget);
- gtk_widget_set_allocation(widget, alloc);
- border = gtk_container_get_border_width(GTK_CONTAINER(cols));
- columns_alloc_horiz(cols, alloc->width, columns_gtk2_get_width);
- columns_alloc_vert(cols, alloc->height, columns_gtk2_get_height);
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget && gtk_widget_get_visible(child->widget)) {
- GtkAllocation call;
- call.x = alloc->x + border + child->x;
- call.y = alloc->y + border + child->y;
- call.width = child->w;
- call.height = child->h;
- gtk_widget_size_allocate(child->widget, &call);
- }
- }
- }
- #else /* GTK_CHECK_VERSION(3,0,0) */
- static gint columns_gtk3_get_min_width(ColumnsChild *child)
- {
- gint ret;
- gtk_widget_get_preferred_width(child->widget, &ret, NULL);
- return ret;
- }
- static gint columns_gtk3_get_nat_width(ColumnsChild *child)
- {
- gint ret;
- if ((GTK_IS_LABEL(child->widget) &&
- gtk_label_get_line_wrap(GTK_LABEL(child->widget))) ||
- GTK_IS_ENTRY(child->widget)) {
- /*
- * We treat wrapping GtkLabels as a special case in this
- * layout class, because the whole point of those is that I
- * _don't_ want them to take up extra horizontal space for
- * long text, but instead to wrap it to whatever size is used
- * by the rest of the layout.
- *
- * GtkEntry gets similar treatment, because in OS X GTK I've
- * found that it requests a natural width regardless of the
- * output of gtk_entry_set_width_chars.
- */
- gtk_widget_get_preferred_width(child->widget, &ret, NULL);
- } else {
- gtk_widget_get_preferred_width(child->widget, NULL, &ret);
- }
- return ret;
- }
- static gint columns_gtk3_get_minfh_width(ColumnsChild *child)
- {
- gint ret;
- gtk_widget_get_preferred_width_for_height(child->widget, child->h,
- &ret, NULL);
- return ret;
- }
- static gint columns_gtk3_get_natfh_width(ColumnsChild *child)
- {
- gint ret;
- gtk_widget_get_preferred_width_for_height(child->widget, child->h,
- NULL, &ret);
- return ret;
- }
- static gint columns_gtk3_get_min_height(ColumnsChild *child)
- {
- gint ret;
- gtk_widget_get_preferred_height(child->widget, &ret, NULL);
- return ret;
- }
- static gint columns_gtk3_get_nat_height(ColumnsChild *child)
- {
- gint ret;
- gtk_widget_get_preferred_height(child->widget, NULL, &ret);
- return ret;
- }
- static gint columns_gtk3_get_minfw_height(ColumnsChild *child)
- {
- gint ret;
- gtk_widget_get_preferred_height_for_width(child->widget, child->w,
- &ret, NULL);
- return ret;
- }
- static gint columns_gtk3_get_natfw_height(ColumnsChild *child)
- {
- gint ret;
- gtk_widget_get_preferred_height_for_width(child->widget, child->w,
- NULL, &ret);
- return ret;
- }
- static void columns_get_preferred_width(GtkWidget *widget,
- gint *min, gint *nat)
- {
- Columns *cols;
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- cols = COLUMNS(widget);
- if (min)
- *min = columns_compute_width(cols, columns_gtk3_get_min_width);
- if (nat)
- *nat = columns_compute_width(cols, columns_gtk3_get_nat_width);
- }
- static void columns_get_preferred_height(GtkWidget *widget,
- gint *min, gint *nat)
- {
- Columns *cols;
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- cols = COLUMNS(widget);
- if (min)
- *min = columns_compute_height(cols, columns_gtk3_get_min_height);
- if (nat)
- *nat = columns_compute_height(cols, columns_gtk3_get_nat_height);
- }
- static void columns_get_preferred_width_for_height(GtkWidget *widget,
- gint height,
- gint *min, gint *nat)
- {
- Columns *cols;
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- cols = COLUMNS(widget);
- /* FIXME: which one should the get-height function here be? */
- columns_alloc_vert(cols, height, columns_gtk3_get_nat_height);
- if (min)
- *min = columns_compute_width(cols, columns_gtk3_get_minfh_width);
- if (nat)
- *nat = columns_compute_width(cols, columns_gtk3_get_natfh_width);
- }
- static void columns_get_preferred_height_for_width(GtkWidget *widget,
- gint width,
- gint *min, gint *nat)
- {
- Columns *cols;
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- cols = COLUMNS(widget);
- /* FIXME: which one should the get-height function here be? */
- columns_alloc_horiz(cols, width, columns_gtk3_get_nat_width);
- if (min)
- *min = columns_compute_height(cols, columns_gtk3_get_minfw_height);
- if (nat)
- *nat = columns_compute_height(cols, columns_gtk3_get_natfw_height);
- }
- static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
- {
- Columns *cols;
- ColumnsChild *child;
- GList *children;
- gint border;
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- g_return_if_fail(alloc != NULL);
- cols = COLUMNS(widget);
- gtk_widget_set_allocation(widget, alloc);
- border = gtk_container_get_border_width(GTK_CONTAINER(cols));
- columns_alloc_horiz(cols, alloc->width, columns_gtk3_get_min_width);
- columns_alloc_vert(cols, alloc->height, columns_gtk3_get_minfw_height);
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget && gtk_widget_get_visible(child->widget)) {
- GtkAllocation call;
- call.x = alloc->x + border + child->x;
- call.y = alloc->y + border + child->y;
- call.width = child->w;
- call.height = child->h;
- gtk_widget_size_allocate(child->widget, &call);
- }
- }
- }
- #endif
|