123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696 |
- /*
- * gtkcols.c - implementation of the `Columns' GTK layout container.
- */
- #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);
- static void columns_draw(GtkWidget *widget, GdkRectangle *area);
- static gint columns_expose(GtkWidget *widget, GdkEventExpose *event);
- 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);
- static gint columns_focus(GtkContainer *container, GtkDirectionType dir);
- static GtkType columns_child_type(GtkContainer *container);
- static void columns_size_request(GtkWidget *widget, GtkRequisition *req);
- static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc);
- static GtkContainerClass *parent_class = NULL;
- GtkType columns_get_type(void)
- {
- static GtkType 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;
- }
- static gint (*columns_inherited_focus)(GtkContainer *container,
- GtkDirectionType direction);
- static void columns_class_init(ColumnsClass *klass)
- {
- GtkObjectClass *object_class;
- GtkWidgetClass *widget_class;
- GtkContainerClass *container_class;
- object_class = (GtkObjectClass *)klass;
- widget_class = (GtkWidgetClass *)klass;
- container_class = (GtkContainerClass *)klass;
- parent_class = gtk_type_class(GTK_TYPE_CONTAINER);
- /*
- * FIXME: do we have to do all this faffing with set_arg,
- * get_arg and child_arg_type? Ick.
- */
- widget_class->map = columns_map;
- widget_class->unmap = columns_unmap;
- widget_class->draw = columns_draw;
- widget_class->expose_event = columns_expose;
- widget_class->size_request = columns_size_request;
- 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;
- /* Save the previous value of this method. */
- if (!columns_inherited_focus)
- columns_inherited_focus = container_class->focus;
- container_class->focus = columns_focus;
- }
- static void columns_init(Columns *cols)
- {
- GTK_WIDGET_SET_FLAGS(cols, GTK_NO_WINDOW);
- 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_FLAGS(cols, GTK_MAPPED);
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget &&
- GTK_WIDGET_VISIBLE(child->widget) &&
- !GTK_WIDGET_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_UNSET_FLAGS(cols, GTK_MAPPED);
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget &&
- GTK_WIDGET_VISIBLE(child->widget) &&
- GTK_WIDGET_MAPPED(child->widget))
- gtk_widget_unmap(child->widget);
- }
- }
- 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;
- }
- 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_VISIBLE(widget);
- gtk_widget_unparent(widget);
- cols->children = g_list_remove_link(cols->children, children);
- g_list_free(children);
- 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);
- 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 GtkType columns_child_type(GtkContainer *container)
- {
- return GTK_TYPE_WIDGET;
- }
- GtkWidget *columns_new(gint spacing)
- {
- Columns *cols;
- cols = gtk_type_new(columns_get_type());
- 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(child->parent == NULL);
- childdata = g_new(ColumnsChild, 1);
- childdata->widget = child;
- childdata->colstart = colstart;
- childdata->colspan = colspan;
- childdata->force_left = FALSE;
- 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_WIDGET_REALIZED(cols))
- gtk_widget_realize(child);
- if (GTK_WIDGET_VISIBLE(cols) && GTK_WIDGET_VISIBLE(child)) {
- if (GTK_WIDGET_MAPPED(cols))
- gtk_widget_map(child);
- gtk_widget_queue_resize(child);
- }
- }
- void columns_force_left_align(Columns *cols, GtkWidget *widget)
- {
- ColumnsChild *child;
- GList *children;
- g_return_if_fail(cols != NULL);
- g_return_if_fail(IS_COLUMNS(cols));
- g_return_if_fail(widget != NULL);
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- if (child->widget != widget)
- continue;
- child->force_left = TRUE;
- if (GTK_WIDGET_VISIBLE(widget))
- gtk_widget_queue_resize(GTK_WIDGET(cols));
- break;
- }
- }
- 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);
- break;
- }
- }
- /*
- * 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);
- }
- /*
- * 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.
- */
- static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
- {
- Columns *cols;
- ColumnsChild *child;
- GList *children;
- gint i, ncols, colspan, *colypos;
- const gint *percentages;
- static const gint onecol[] = { 100 };
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- g_return_if_fail(req != NULL);
- cols = COLUMNS(widget);
- req->width = 0;
- req->height = cols->spacing;
- ncols = 1;
- colypos = g_new(gint, 1);
- colypos[0] = 0;
- percentages = onecol;
- for (children = cols->children;
- children && (child = children->data);
- children = children->next) {
- GtkRequisition creq;
- if (!child->widget) {
- /* Column reconfiguration. */
- for (i = 1; i < ncols; i++) {
- if (colypos[0] < colypos[i])
- colypos[0] = colypos[i];
- }
- ncols = child->ncols;
- percentages = child->percentages;
- 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_VISIBLE(child->widget))
- continue;
- gtk_widget_size_request(child->widget, &creq);
- colspan = child->colspan ? child->colspan : ncols-child->colstart;
- /*
- * To compute width: we know that creq.width plus
- * 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 = creq.width + cols->spacing;
- /*
- * Since creq is 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;
- /*
- * 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 (req->width < fullwid - cols->spacing)
- req->width = fullwid - cols->spacing;
- }
- /*
- * 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, and minus the
- * padding it is also a lower bound on our own size
- * request.
- */
- {
- int topy, boty;
- topy = 0;
- for (i = 0; i < colspan; i++) {
- if (topy < colypos[child->colstart+i])
- topy = colypos[child->colstart+i];
- }
- boty = topy + creq.height + cols->spacing;
- for (i = 0; i < colspan; i++) {
- colypos[child->colstart+i] = boty;
- }
- if (req->height < boty - cols->spacing)
- req->height = boty - cols->spacing;
- }
- }
- req->width += 2*GTK_CONTAINER(cols)->border_width;
- req->height += 2*GTK_CONTAINER(cols)->border_width;
- g_free(colypos);
- }
- static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
- {
- Columns *cols;
- ColumnsChild *child;
- GList *children;
- gint i, ncols, colspan, border, *colxpos, *colypos;
- const gint *percentages;
- static const gint onecol[] = { 100 };
- g_return_if_fail(widget != NULL);
- g_return_if_fail(IS_COLUMNS(widget));
- g_return_if_fail(alloc != NULL);
- cols = COLUMNS(widget);
- widget->allocation = *alloc;
- border = GTK_CONTAINER(cols)->border_width;
- 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] = alloc->width - 2*border + cols->spacing;
- /* 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) {
- GtkRequisition creq;
- GtkAllocation call;
- if (!child->widget) {
- gint percent;
- /* Column reconfiguration. */
- for (i = 1; i < ncols; i++) {
- if (colypos[0] < colypos[i])
- colypos[0] = colypos[i];
- }
- ncols = child->ncols;
- percentages = child->percentages;
- colypos = g_renew(gint, colypos, ncols);
- for (i = 1; i < ncols; i++)
- colypos[i] = colypos[0];
- colxpos = g_renew(gint, colxpos, ncols + 1);
- colxpos[0] = 0;
- percent = 0;
- for (i = 0; i < ncols; i++) {
- percent += percentages[i];
- colxpos[i+1] = (((alloc->width - 2*border) + cols->spacing)
- * percent / 100);
- }
- continue;
- }
- /* Only take visible widgets into account. */
- if (!GTK_WIDGET_VISIBLE(child->widget))
- continue;
- gtk_widget_get_child_requisition(child->widget, &creq);
- 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.
- */
- call.x = alloc->x + border + colxpos[child->colstart];
- if (child->force_left)
- call.width = creq.width;
- else
- call.width = (colxpos[child->colstart+colspan] -
- colxpos[child->colstart] - cols->spacing);
- /*
- * 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];
- }
- call.y = alloc->y + border + topy;
- call.height = creq.height;
- boty = topy + creq.height + cols->spacing;
- for (i = 0; i < colspan; i++) {
- colypos[child->colstart+i] = boty;
- }
- }
- gtk_widget_size_allocate(child->widget, &call);
- }
- g_free(colxpos);
- g_free(colypos);
- }
|