d9b15094 |
1 | /* |
2 | * gtkcols.c - implementation of the `Columns' GTK layout container. |
3 | */ |
4 | |
5 | #include "gtkcols.h" |
6 | |
7 | static void columns_init(Columns *cols); |
8 | static void columns_class_init(ColumnsClass *klass); |
9 | static void columns_map(GtkWidget *widget); |
10 | static void columns_unmap(GtkWidget *widget); |
11 | static void columns_draw(GtkWidget *widget, GdkRectangle *area); |
12 | static gint columns_expose(GtkWidget *widget, GdkEventExpose *event); |
13 | static void columns_base_add(GtkContainer *container, GtkWidget *widget); |
14 | static void columns_remove(GtkContainer *container, GtkWidget *widget); |
15 | static void columns_forall(GtkContainer *container, gboolean include_internals, |
16 | GtkCallback callback, gpointer callback_data); |
17 | static gint columns_focus(GtkContainer *container, GtkDirectionType dir); |
18 | static GtkType columns_child_type(GtkContainer *container); |
19 | static void columns_size_request(GtkWidget *widget, GtkRequisition *req); |
20 | static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc); |
21 | |
22 | static GtkContainerClass *parent_class = NULL; |
23 | |
24 | GtkType columns_get_type(void) |
25 | { |
26 | static GtkType columns_type = 0; |
27 | |
28 | if (!columns_type) { |
29 | static const GtkTypeInfo columns_info = { |
30 | "Columns", |
31 | sizeof(Columns), |
32 | sizeof(ColumnsClass), |
33 | (GtkClassInitFunc) columns_class_init, |
34 | (GtkObjectInitFunc) columns_init, |
35 | /* reserved_1 */ NULL, |
36 | /* reserved_2 */ NULL, |
37 | (GtkClassInitFunc) NULL, |
38 | }; |
39 | |
40 | columns_type = gtk_type_unique(GTK_TYPE_CONTAINER, &columns_info); |
41 | } |
42 | |
43 | return columns_type; |
44 | } |
45 | |
46 | static gint (*columns_inherited_focus)(GtkContainer *container, |
47 | GtkDirectionType direction); |
48 | |
49 | static void columns_class_init(ColumnsClass *klass) |
50 | { |
51 | GtkObjectClass *object_class; |
52 | GtkWidgetClass *widget_class; |
53 | GtkContainerClass *container_class; |
54 | |
55 | object_class = (GtkObjectClass *)klass; |
56 | widget_class = (GtkWidgetClass *)klass; |
57 | container_class = (GtkContainerClass *)klass; |
58 | |
59 | parent_class = gtk_type_class(GTK_TYPE_CONTAINER); |
60 | |
61 | /* |
62 | * FIXME: do we have to do all this faffing with set_arg, |
63 | * get_arg and child_arg_type? Ick. |
64 | */ |
65 | |
66 | widget_class->map = columns_map; |
67 | widget_class->unmap = columns_unmap; |
68 | widget_class->draw = columns_draw; |
69 | widget_class->expose_event = columns_expose; |
70 | widget_class->size_request = columns_size_request; |
71 | widget_class->size_allocate = columns_size_allocate; |
72 | |
73 | container_class->add = columns_base_add; |
74 | container_class->remove = columns_remove; |
75 | container_class->forall = columns_forall; |
76 | container_class->child_type = columns_child_type; |
77 | /* Save the previous value of this method. */ |
78 | if (!columns_inherited_focus) |
79 | columns_inherited_focus = container_class->focus; |
80 | container_class->focus = columns_focus; |
81 | } |
82 | |
83 | static void columns_init(Columns *cols) |
84 | { |
85 | GTK_WIDGET_SET_FLAGS(cols, GTK_NO_WINDOW); |
86 | |
87 | cols->children = NULL; |
88 | cols->spacing = 0; |
89 | } |
90 | |
91 | /* |
92 | * These appear to be thoroughly tedious functions; the only reason |
93 | * we have to reimplement them at all is because we defined our own |
94 | * format for our GList of children... |
95 | */ |
96 | static void columns_map(GtkWidget *widget) |
97 | { |
98 | Columns *cols; |
99 | ColumnsChild *child; |
100 | GList *children; |
101 | |
102 | g_return_if_fail(widget != NULL); |
103 | g_return_if_fail(IS_COLUMNS(widget)); |
104 | |
105 | cols = COLUMNS(widget); |
106 | GTK_WIDGET_SET_FLAGS(cols, GTK_MAPPED); |
107 | |
108 | for (children = cols->children; |
109 | children && (child = children->data); |
110 | children = children->next) { |
111 | if (child->widget && |
112 | GTK_WIDGET_VISIBLE(child->widget) && |
113 | !GTK_WIDGET_MAPPED(child->widget)) |
114 | gtk_widget_map(child->widget); |
115 | } |
116 | } |
117 | static void columns_unmap(GtkWidget *widget) |
118 | { |
119 | Columns *cols; |
120 | ColumnsChild *child; |
121 | GList *children; |
122 | |
123 | g_return_if_fail(widget != NULL); |
124 | g_return_if_fail(IS_COLUMNS(widget)); |
125 | |
126 | cols = COLUMNS(widget); |
127 | GTK_WIDGET_UNSET_FLAGS(cols, GTK_MAPPED); |
128 | |
129 | for (children = cols->children; |
130 | children && (child = children->data); |
131 | children = children->next) { |
132 | if (child->widget && |
133 | GTK_WIDGET_VISIBLE(child->widget) && |
134 | GTK_WIDGET_MAPPED(child->widget)) |
135 | gtk_widget_unmap(child->widget); |
136 | } |
137 | } |
138 | static void columns_draw(GtkWidget *widget, GdkRectangle *area) |
139 | { |
140 | Columns *cols; |
141 | ColumnsChild *child; |
142 | GList *children; |
143 | GdkRectangle child_area; |
144 | |
145 | g_return_if_fail(widget != NULL); |
146 | g_return_if_fail(IS_COLUMNS(widget)); |
147 | |
148 | if (GTK_WIDGET_DRAWABLE(widget)) { |
149 | cols = COLUMNS(widget); |
150 | |
151 | for (children = cols->children; |
152 | children && (child = children->data); |
153 | children = children->next) { |
154 | if (child->widget && |
155 | GTK_WIDGET_DRAWABLE(child->widget) && |
156 | gtk_widget_intersect(child->widget, area, &child_area)) |
157 | gtk_widget_draw(child->widget, &child_area); |
158 | } |
159 | } |
160 | } |
161 | static gint columns_expose(GtkWidget *widget, GdkEventExpose *event) |
162 | { |
163 | Columns *cols; |
164 | ColumnsChild *child; |
165 | GList *children; |
166 | GdkEventExpose child_event; |
167 | |
168 | g_return_val_if_fail(widget != NULL, FALSE); |
169 | g_return_val_if_fail(IS_COLUMNS(widget), FALSE); |
170 | g_return_val_if_fail(event != NULL, FALSE); |
171 | |
172 | if (GTK_WIDGET_DRAWABLE(widget)) { |
173 | cols = COLUMNS(widget); |
174 | child_event = *event; |
175 | |
176 | for (children = cols->children; |
177 | children && (child = children->data); |
178 | children = children->next) { |
179 | if (child->widget && |
180 | GTK_WIDGET_DRAWABLE(child->widget) && |
181 | GTK_WIDGET_NO_WINDOW(child->widget) && |
182 | gtk_widget_intersect(child->widget, &event->area, |
183 | &child_event.area)) |
184 | gtk_widget_event(child->widget, (GdkEvent *)&child_event); |
185 | } |
186 | } |
187 | return FALSE; |
188 | } |
189 | |
190 | static void columns_base_add(GtkContainer *container, GtkWidget *widget) |
191 | { |
192 | Columns *cols; |
193 | |
194 | g_return_if_fail(container != NULL); |
195 | g_return_if_fail(IS_COLUMNS(container)); |
196 | g_return_if_fail(widget != NULL); |
197 | |
198 | cols = COLUMNS(container); |
199 | |
200 | /* |
201 | * Default is to add a new widget spanning all columns. |
202 | */ |
203 | columns_add(cols, widget, 0, 0); /* 0 means ncols */ |
204 | } |
205 | |
206 | static void columns_remove(GtkContainer *container, GtkWidget *widget) |
207 | { |
208 | Columns *cols; |
209 | ColumnsChild *child; |
210 | GtkWidget *childw; |
211 | GList *children; |
212 | gboolean was_visible; |
213 | |
214 | g_return_if_fail(container != NULL); |
215 | g_return_if_fail(IS_COLUMNS(container)); |
216 | g_return_if_fail(widget != NULL); |
217 | |
218 | cols = COLUMNS(container); |
219 | |
220 | for (children = cols->children; |
221 | children && (child = children->data); |
222 | children = children->next) { |
223 | if (child->widget != widget) |
224 | continue; |
225 | |
226 | was_visible = GTK_WIDGET_VISIBLE(widget); |
227 | gtk_widget_unparent(widget); |
228 | cols->children = g_list_remove_link(cols->children, children); |
229 | g_list_free(children); |
230 | g_free(child); |
231 | if (was_visible) |
232 | gtk_widget_queue_resize(GTK_WIDGET(container)); |
233 | break; |
234 | } |
235 | |
236 | for (children = cols->taborder; |
237 | children && (childw = children->data); |
238 | children = children->next) { |
239 | if (childw != widget) |
240 | continue; |
241 | |
242 | cols->taborder = g_list_remove_link(cols->taborder, children); |
243 | g_list_free(children); |
244 | break; |
245 | } |
246 | } |
247 | |
248 | static void columns_forall(GtkContainer *container, gboolean include_internals, |
249 | GtkCallback callback, gpointer callback_data) |
250 | { |
251 | Columns *cols; |
252 | ColumnsChild *child; |
253 | GList *children; |
254 | |
255 | g_return_if_fail(container != NULL); |
256 | g_return_if_fail(IS_COLUMNS(container)); |
257 | g_return_if_fail(callback != NULL); |
258 | |
259 | cols = COLUMNS(container); |
260 | |
261 | for (children = cols->children; |
262 | children && (child = children->data); |
263 | children = children->next) |
264 | if (child->widget) |
265 | callback(child->widget, callback_data); |
266 | } |
267 | |
268 | static GtkType columns_child_type(GtkContainer *container) |
269 | { |
270 | return GTK_TYPE_WIDGET; |
271 | } |
272 | |
273 | GtkWidget *columns_new(gint spacing) |
274 | { |
275 | Columns *cols; |
276 | |
277 | cols = gtk_type_new(columns_get_type()); |
278 | cols->spacing = spacing; |
279 | |
280 | return GTK_WIDGET(cols); |
281 | } |
282 | |
283 | void columns_set_cols(Columns *cols, gint ncols, const gint *percentages) |
284 | { |
285 | ColumnsChild *childdata; |
286 | gint i; |
287 | |
288 | g_return_if_fail(cols != NULL); |
289 | g_return_if_fail(IS_COLUMNS(cols)); |
290 | g_return_if_fail(ncols > 0); |
291 | g_return_if_fail(percentages != NULL); |
292 | |
293 | childdata = g_new(ColumnsChild, 1); |
294 | childdata->widget = NULL; |
295 | childdata->ncols = ncols; |
296 | childdata->percentages = g_new(gint, ncols); |
297 | childdata->force_left = FALSE; |
298 | for (i = 0; i < ncols; i++) |
299 | childdata->percentages[i] = percentages[i]; |
300 | |
301 | cols->children = g_list_append(cols->children, childdata); |
302 | } |
303 | |
304 | void columns_add(Columns *cols, GtkWidget *child, |
305 | gint colstart, gint colspan) |
306 | { |
307 | ColumnsChild *childdata; |
308 | |
309 | g_return_if_fail(cols != NULL); |
310 | g_return_if_fail(IS_COLUMNS(cols)); |
311 | g_return_if_fail(child != NULL); |
312 | g_return_if_fail(child->parent == NULL); |
313 | |
314 | childdata = g_new(ColumnsChild, 1); |
315 | childdata->widget = child; |
316 | childdata->colstart = colstart; |
317 | childdata->colspan = colspan; |
318 | childdata->force_left = FALSE; |
319 | |
320 | cols->children = g_list_append(cols->children, childdata); |
321 | cols->taborder = g_list_append(cols->taborder, child); |
322 | |
323 | gtk_widget_set_parent(child, GTK_WIDGET(cols)); |
324 | |
325 | if (GTK_WIDGET_REALIZED(cols)) |
326 | gtk_widget_realize(child); |
327 | |
328 | if (GTK_WIDGET_VISIBLE(cols) && GTK_WIDGET_VISIBLE(child)) { |
329 | if (GTK_WIDGET_MAPPED(cols)) |
330 | gtk_widget_map(child); |
331 | gtk_widget_queue_resize(child); |
332 | } |
333 | } |
334 | |
335 | void columns_force_left_align(Columns *cols, GtkWidget *widget) |
336 | { |
337 | ColumnsChild *child; |
338 | GList *children; |
339 | |
340 | g_return_if_fail(cols != NULL); |
341 | g_return_if_fail(IS_COLUMNS(cols)); |
342 | g_return_if_fail(widget != NULL); |
343 | |
344 | for (children = cols->children; |
345 | children && (child = children->data); |
346 | children = children->next) { |
347 | if (child->widget != widget) |
348 | continue; |
349 | |
350 | child->force_left = TRUE; |
351 | if (GTK_WIDGET_VISIBLE(widget)) |
352 | gtk_widget_queue_resize(GTK_WIDGET(cols)); |
353 | break; |
354 | } |
355 | } |
356 | |
357 | void columns_taborder_last(Columns *cols, GtkWidget *widget) |
358 | { |
359 | GtkWidget *childw; |
360 | GList *children; |
361 | |
362 | g_return_if_fail(cols != NULL); |
363 | g_return_if_fail(IS_COLUMNS(cols)); |
364 | g_return_if_fail(widget != NULL); |
365 | |
366 | for (children = cols->taborder; |
367 | children && (childw = children->data); |
368 | children = children->next) { |
369 | if (childw != widget) |
370 | continue; |
371 | |
372 | cols->taborder = g_list_remove_link(cols->taborder, children); |
373 | g_list_free(children); |
374 | cols->taborder = g_list_append(cols->taborder, widget); |
375 | break; |
376 | } |
377 | } |
378 | |
379 | /* |
380 | * Override GtkContainer's focus movement so the user can |
381 | * explicitly specify the tab order. |
382 | */ |
383 | static gint columns_focus(GtkContainer *container, GtkDirectionType dir) |
384 | { |
385 | Columns *cols; |
386 | GList *pos; |
387 | GtkWidget *focuschild; |
388 | |
389 | g_return_val_if_fail(container != NULL, FALSE); |
390 | g_return_val_if_fail(IS_COLUMNS(container), FALSE); |
391 | |
392 | cols = COLUMNS(container); |
393 | |
394 | if (!GTK_WIDGET_DRAWABLE(cols) || |
395 | !GTK_WIDGET_IS_SENSITIVE(cols)) |
396 | return FALSE; |
397 | |
398 | if (!GTK_WIDGET_CAN_FOCUS(container) && |
399 | (dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) { |
400 | |
401 | focuschild = container->focus_child; |
402 | gtk_container_set_focus_child(container, NULL); |
403 | |
404 | if (dir == GTK_DIR_TAB_FORWARD) |
405 | pos = cols->taborder; |
406 | else |
407 | pos = g_list_last(cols->taborder); |
408 | |
409 | while (pos) { |
410 | GtkWidget *child = pos->data; |
411 | |
412 | if (focuschild) { |
413 | if (focuschild == child) { |
414 | focuschild = NULL; /* now we can start looking in here */ |
415 | if (GTK_WIDGET_DRAWABLE(child) && |
416 | GTK_IS_CONTAINER(child) && |
417 | !GTK_WIDGET_HAS_FOCUS(child)) { |
418 | if (gtk_container_focus(GTK_CONTAINER(child), dir)) |
419 | return TRUE; |
420 | } |
421 | } |
422 | } else if (GTK_WIDGET_DRAWABLE(child)) { |
423 | if (GTK_IS_CONTAINER(child)) { |
424 | if (gtk_container_focus(GTK_CONTAINER(child), dir)) |
425 | return TRUE; |
426 | } else if (GTK_WIDGET_CAN_FOCUS(child)) { |
427 | gtk_widget_grab_focus(child); |
428 | return TRUE; |
429 | } |
430 | } |
431 | |
432 | if (dir == GTK_DIR_TAB_FORWARD) |
433 | pos = pos->next; |
434 | else |
435 | pos = pos->prev; |
436 | } |
437 | |
438 | return FALSE; |
439 | } else |
440 | return columns_inherited_focus(container, dir); |
441 | } |
442 | |
443 | /* |
444 | * Now here comes the interesting bit. The actual layout part is |
445 | * done in the following two functions: |
446 | * |
447 | * columns_size_request() examines the list of widgets held in the |
448 | * Columns, and returns a requisition stating the absolute minimum |
449 | * size it can bear to be. |
450 | * |
451 | * columns_size_allocate() is given an allocation telling it what |
452 | * size the whole container is going to be, and it calls |
453 | * gtk_widget_size_allocate() on all of its (visible) children to |
454 | * set their size and position relative to the top left of the |
455 | * container. |
456 | */ |
457 | |
458 | static void columns_size_request(GtkWidget *widget, GtkRequisition *req) |
459 | { |
460 | Columns *cols; |
461 | ColumnsChild *child; |
462 | GList *children; |
463 | gint i, ncols, colspan, *colypos; |
464 | const gint *percentages; |
465 | static const gint onecol[] = { 100 }; |
466 | |
467 | g_return_if_fail(widget != NULL); |
468 | g_return_if_fail(IS_COLUMNS(widget)); |
469 | g_return_if_fail(req != NULL); |
470 | |
471 | cols = COLUMNS(widget); |
472 | |
473 | req->width = 0; |
474 | req->height = cols->spacing; |
475 | |
476 | ncols = 1; |
477 | colypos = g_new(gint, 1); |
478 | colypos[0] = 0; |
479 | percentages = onecol; |
480 | |
481 | for (children = cols->children; |
482 | children && (child = children->data); |
483 | children = children->next) { |
484 | GtkRequisition creq; |
485 | |
486 | if (!child->widget) { |
487 | /* Column reconfiguration. */ |
488 | for (i = 1; i < ncols; i++) { |
489 | if (colypos[0] < colypos[i]) |
490 | colypos[0] = colypos[i]; |
491 | } |
492 | ncols = child->ncols; |
493 | percentages = child->percentages; |
494 | colypos = g_renew(gint, colypos, ncols); |
495 | for (i = 1; i < ncols; i++) |
496 | colypos[i] = colypos[0]; |
497 | continue; |
498 | } |
499 | |
500 | /* Only take visible widgets into account. */ |
501 | if (!GTK_WIDGET_VISIBLE(child->widget)) |
502 | continue; |
503 | |
504 | gtk_widget_size_request(child->widget, &creq); |
505 | colspan = child->colspan ? child->colspan : ncols-child->colstart; |
506 | |
507 | /* |
508 | * To compute width: we know that creq.width plus |
509 | * cols->spacing needs to equal a certain percentage of the |
510 | * full width of the container. So we work this value out, |
511 | * figure out how wide the container will need to be to |
512 | * make that percentage of it equal to that width, and |
513 | * ensure our returned width is at least that much. Very |
514 | * simple really. |
515 | */ |
516 | { |
517 | int percent, thiswid, fullwid; |
518 | |
519 | percent = 0; |
520 | for (i = 0; i < colspan; i++) |
521 | percent += percentages[child->colstart+i]; |
522 | |
523 | thiswid = creq.width + cols->spacing; |
524 | /* |
525 | * Since creq is the _minimum_ size the child needs, we |
526 | * must ensure that it gets _at least_ that size. |
527 | * Hence, when scaling thiswid up to fullwid, we must |
528 | * round up, which means adding percent-1 before |
529 | * dividing by percent. |
530 | */ |
531 | fullwid = (thiswid * 100 + percent - 1) / percent; |
532 | |
533 | /* |
534 | * The above calculation assumes every widget gets |
535 | * cols->spacing on the right. So we subtract |
536 | * cols->spacing here to account for the extra load of |
537 | * spacing on the right. |
538 | */ |
539 | if (req->width < fullwid - cols->spacing) |
540 | req->width = fullwid - cols->spacing; |
541 | } |
542 | |
543 | /* |
544 | * To compute height: the widget's top will be positioned |
545 | * at the largest y value so far reached in any of the |
546 | * columns it crosses. Then it will go down by creq.height |
547 | * plus padding; and the point it reaches at the bottom is |
548 | * the new y value in all those columns, and minus the |
549 | * padding it is also a lower bound on our own size |
550 | * request. |
551 | */ |
552 | { |
553 | int topy, boty; |
554 | |
555 | topy = 0; |
556 | for (i = 0; i < colspan; i++) { |
557 | if (topy < colypos[child->colstart+i]) |
558 | topy = colypos[child->colstart+i]; |
559 | } |
560 | boty = topy + creq.height + cols->spacing; |
561 | for (i = 0; i < colspan; i++) { |
562 | colypos[child->colstart+i] = boty; |
563 | } |
564 | |
565 | if (req->height < boty - cols->spacing) |
566 | req->height = boty - cols->spacing; |
567 | } |
568 | } |
569 | |
570 | req->width += 2*GTK_CONTAINER(cols)->border_width; |
571 | req->height += 2*GTK_CONTAINER(cols)->border_width; |
572 | |
573 | g_free(colypos); |
574 | } |
575 | |
576 | static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc) |
577 | { |
578 | Columns *cols; |
579 | ColumnsChild *child; |
580 | GList *children; |
581 | gint i, ncols, colspan, border, *colxpos, *colypos; |
582 | const gint *percentages; |
583 | static const gint onecol[] = { 100 }; |
584 | |
585 | g_return_if_fail(widget != NULL); |
586 | g_return_if_fail(IS_COLUMNS(widget)); |
587 | g_return_if_fail(alloc != NULL); |
588 | |
589 | cols = COLUMNS(widget); |
590 | widget->allocation = *alloc; |
591 | border = GTK_CONTAINER(cols)->border_width; |
592 | |
593 | ncols = 1; |
594 | percentages = onecol; |
595 | /* colxpos gives the starting x position of each column. |
596 | * We supply n+1 of them, so that we can find the RH edge easily. |
597 | * All ending x positions are expected to be adjusted afterwards by |
598 | * subtracting the spacing. */ |
599 | colxpos = g_new(gint, 2); |
600 | colxpos[0] = 0; |
601 | colxpos[1] = alloc->width - 2*border + cols->spacing; |
602 | /* As in size_request, colypos is the lowest y reached in each column. */ |
603 | colypos = g_new(gint, 1); |
604 | colypos[0] = 0; |
605 | |
606 | for (children = cols->children; |
607 | children && (child = children->data); |
608 | children = children->next) { |
609 | GtkRequisition creq; |
610 | GtkAllocation call; |
611 | |
612 | if (!child->widget) { |
613 | gint percent; |
614 | |
615 | /* Column reconfiguration. */ |
616 | for (i = 1; i < ncols; i++) { |
617 | if (colypos[0] < colypos[i]) |
618 | colypos[0] = colypos[i]; |
619 | } |
620 | ncols = child->ncols; |
621 | percentages = child->percentages; |
622 | colypos = g_renew(gint, colypos, ncols); |
623 | for (i = 1; i < ncols; i++) |
624 | colypos[i] = colypos[0]; |
625 | colxpos = g_renew(gint, colxpos, ncols + 1); |
626 | colxpos[0] = 0; |
627 | percent = 0; |
628 | for (i = 0; i < ncols; i++) { |
629 | percent += percentages[i]; |
630 | colxpos[i+1] = (((alloc->width - 2*border) + cols->spacing) |
631 | * percent / 100); |
632 | } |
633 | continue; |
634 | } |
635 | |
636 | /* Only take visible widgets into account. */ |
637 | if (!GTK_WIDGET_VISIBLE(child->widget)) |
638 | continue; |
639 | |
640 | gtk_widget_get_child_requisition(child->widget, &creq); |
641 | colspan = child->colspan ? child->colspan : ncols-child->colstart; |
642 | |
643 | /* |
644 | * Starting x position is cols[colstart]. |
645 | * Ending x position is cols[colstart+colspan] - spacing. |
646 | * |
647 | * Unless we're forcing left, in which case the width is |
648 | * exactly the requisition width. |
649 | */ |
650 | call.x = alloc->x + border + colxpos[child->colstart]; |
651 | if (child->force_left) |
652 | call.width = creq.width; |
653 | else |
654 | call.width = (colxpos[child->colstart+colspan] - |
655 | colxpos[child->colstart] - cols->spacing); |
656 | |
657 | /* |
658 | * To compute height: the widget's top will be positioned |
659 | * at the largest y value so far reached in any of the |
660 | * columns it crosses. Then it will go down by creq.height |
661 | * plus padding; and the point it reaches at the bottom is |
662 | * the new y value in all those columns. |
663 | */ |
664 | { |
665 | int topy, boty; |
666 | |
667 | topy = 0; |
668 | for (i = 0; i < colspan; i++) { |
669 | if (topy < colypos[child->colstart+i]) |
670 | topy = colypos[child->colstart+i]; |
671 | } |
672 | call.y = alloc->y + border + topy; |
673 | call.height = creq.height; |
674 | boty = topy + creq.height + cols->spacing; |
675 | for (i = 0; i < colspan; i++) { |
676 | colypos[child->colstart+i] = boty; |
677 | } |
678 | } |
679 | |
680 | gtk_widget_size_allocate(child->widget, &call); |
681 | } |
682 | |
683 | g_free(colxpos); |
684 | g_free(colypos); |
685 | } |