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; |
39016687 |
253 | GList *children, *next; |
d9b15094 |
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); |
39016687 |
263 | children = next) { |
264 | /* |
265 | * We can't wait until after the callback to assign |
266 | * `children = children->next', because the callback might |
267 | * be gtk_widget_destroy, which would remove the link |
268 | * `children' from the list! So instead we must get our |
269 | * hands on the value of the `next' pointer _before_ the |
270 | * callback. |
271 | */ |
272 | next = children->next; |
d9b15094 |
273 | if (child->widget) |
274 | callback(child->widget, callback_data); |
39016687 |
275 | } |
d9b15094 |
276 | } |
277 | |
278 | static GtkType columns_child_type(GtkContainer *container) |
279 | { |
280 | return GTK_TYPE_WIDGET; |
281 | } |
282 | |
283 | GtkWidget *columns_new(gint spacing) |
284 | { |
285 | Columns *cols; |
286 | |
287 | cols = gtk_type_new(columns_get_type()); |
288 | cols->spacing = spacing; |
289 | |
290 | return GTK_WIDGET(cols); |
291 | } |
292 | |
293 | void columns_set_cols(Columns *cols, gint ncols, const gint *percentages) |
294 | { |
295 | ColumnsChild *childdata; |
296 | gint i; |
297 | |
298 | g_return_if_fail(cols != NULL); |
299 | g_return_if_fail(IS_COLUMNS(cols)); |
300 | g_return_if_fail(ncols > 0); |
301 | g_return_if_fail(percentages != NULL); |
302 | |
303 | childdata = g_new(ColumnsChild, 1); |
304 | childdata->widget = NULL; |
305 | childdata->ncols = ncols; |
306 | childdata->percentages = g_new(gint, ncols); |
307 | childdata->force_left = FALSE; |
308 | for (i = 0; i < ncols; i++) |
309 | childdata->percentages[i] = percentages[i]; |
310 | |
311 | cols->children = g_list_append(cols->children, childdata); |
312 | } |
313 | |
314 | void columns_add(Columns *cols, GtkWidget *child, |
315 | gint colstart, gint colspan) |
316 | { |
317 | ColumnsChild *childdata; |
318 | |
319 | g_return_if_fail(cols != NULL); |
320 | g_return_if_fail(IS_COLUMNS(cols)); |
321 | g_return_if_fail(child != NULL); |
322 | g_return_if_fail(child->parent == NULL); |
323 | |
324 | childdata = g_new(ColumnsChild, 1); |
325 | childdata->widget = child; |
326 | childdata->colstart = colstart; |
327 | childdata->colspan = colspan; |
328 | childdata->force_left = FALSE; |
329 | |
330 | cols->children = g_list_append(cols->children, childdata); |
331 | cols->taborder = g_list_append(cols->taborder, child); |
332 | |
333 | gtk_widget_set_parent(child, GTK_WIDGET(cols)); |
334 | |
335 | if (GTK_WIDGET_REALIZED(cols)) |
336 | gtk_widget_realize(child); |
337 | |
338 | if (GTK_WIDGET_VISIBLE(cols) && GTK_WIDGET_VISIBLE(child)) { |
339 | if (GTK_WIDGET_MAPPED(cols)) |
340 | gtk_widget_map(child); |
341 | gtk_widget_queue_resize(child); |
342 | } |
343 | } |
344 | |
345 | void columns_force_left_align(Columns *cols, GtkWidget *widget) |
346 | { |
347 | ColumnsChild *child; |
348 | GList *children; |
349 | |
350 | g_return_if_fail(cols != NULL); |
351 | g_return_if_fail(IS_COLUMNS(cols)); |
352 | g_return_if_fail(widget != NULL); |
353 | |
354 | for (children = cols->children; |
355 | children && (child = children->data); |
356 | children = children->next) { |
357 | if (child->widget != widget) |
358 | continue; |
359 | |
360 | child->force_left = TRUE; |
361 | if (GTK_WIDGET_VISIBLE(widget)) |
362 | gtk_widget_queue_resize(GTK_WIDGET(cols)); |
363 | break; |
364 | } |
365 | } |
366 | |
367 | void columns_taborder_last(Columns *cols, GtkWidget *widget) |
368 | { |
369 | GtkWidget *childw; |
370 | GList *children; |
371 | |
372 | g_return_if_fail(cols != NULL); |
373 | g_return_if_fail(IS_COLUMNS(cols)); |
374 | g_return_if_fail(widget != NULL); |
375 | |
376 | for (children = cols->taborder; |
377 | children && (childw = children->data); |
378 | children = children->next) { |
379 | if (childw != widget) |
380 | continue; |
381 | |
382 | cols->taborder = g_list_remove_link(cols->taborder, children); |
383 | g_list_free(children); |
384 | cols->taborder = g_list_append(cols->taborder, widget); |
385 | break; |
386 | } |
387 | } |
388 | |
389 | /* |
390 | * Override GtkContainer's focus movement so the user can |
391 | * explicitly specify the tab order. |
392 | */ |
393 | static gint columns_focus(GtkContainer *container, GtkDirectionType dir) |
394 | { |
395 | Columns *cols; |
396 | GList *pos; |
397 | GtkWidget *focuschild; |
398 | |
399 | g_return_val_if_fail(container != NULL, FALSE); |
400 | g_return_val_if_fail(IS_COLUMNS(container), FALSE); |
401 | |
402 | cols = COLUMNS(container); |
403 | |
404 | if (!GTK_WIDGET_DRAWABLE(cols) || |
405 | !GTK_WIDGET_IS_SENSITIVE(cols)) |
406 | return FALSE; |
407 | |
408 | if (!GTK_WIDGET_CAN_FOCUS(container) && |
409 | (dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) { |
410 | |
411 | focuschild = container->focus_child; |
412 | gtk_container_set_focus_child(container, NULL); |
413 | |
414 | if (dir == GTK_DIR_TAB_FORWARD) |
415 | pos = cols->taborder; |
416 | else |
417 | pos = g_list_last(cols->taborder); |
418 | |
419 | while (pos) { |
420 | GtkWidget *child = pos->data; |
421 | |
422 | if (focuschild) { |
423 | if (focuschild == child) { |
424 | focuschild = NULL; /* now we can start looking in here */ |
425 | if (GTK_WIDGET_DRAWABLE(child) && |
426 | GTK_IS_CONTAINER(child) && |
427 | !GTK_WIDGET_HAS_FOCUS(child)) { |
428 | if (gtk_container_focus(GTK_CONTAINER(child), dir)) |
429 | return TRUE; |
430 | } |
431 | } |
432 | } else if (GTK_WIDGET_DRAWABLE(child)) { |
433 | if (GTK_IS_CONTAINER(child)) { |
434 | if (gtk_container_focus(GTK_CONTAINER(child), dir)) |
435 | return TRUE; |
436 | } else if (GTK_WIDGET_CAN_FOCUS(child)) { |
437 | gtk_widget_grab_focus(child); |
438 | return TRUE; |
439 | } |
440 | } |
441 | |
442 | if (dir == GTK_DIR_TAB_FORWARD) |
443 | pos = pos->next; |
444 | else |
445 | pos = pos->prev; |
446 | } |
447 | |
448 | return FALSE; |
449 | } else |
450 | return columns_inherited_focus(container, dir); |
451 | } |
452 | |
453 | /* |
454 | * Now here comes the interesting bit. The actual layout part is |
455 | * done in the following two functions: |
456 | * |
457 | * columns_size_request() examines the list of widgets held in the |
458 | * Columns, and returns a requisition stating the absolute minimum |
459 | * size it can bear to be. |
460 | * |
461 | * columns_size_allocate() is given an allocation telling it what |
462 | * size the whole container is going to be, and it calls |
463 | * gtk_widget_size_allocate() on all of its (visible) children to |
464 | * set their size and position relative to the top left of the |
465 | * container. |
466 | */ |
467 | |
468 | static void columns_size_request(GtkWidget *widget, GtkRequisition *req) |
469 | { |
470 | Columns *cols; |
471 | ColumnsChild *child; |
472 | GList *children; |
473 | gint i, ncols, colspan, *colypos; |
474 | const gint *percentages; |
475 | static const gint onecol[] = { 100 }; |
476 | |
477 | g_return_if_fail(widget != NULL); |
478 | g_return_if_fail(IS_COLUMNS(widget)); |
479 | g_return_if_fail(req != NULL); |
480 | |
481 | cols = COLUMNS(widget); |
482 | |
483 | req->width = 0; |
484 | req->height = cols->spacing; |
485 | |
486 | ncols = 1; |
487 | colypos = g_new(gint, 1); |
488 | colypos[0] = 0; |
489 | percentages = onecol; |
490 | |
491 | for (children = cols->children; |
492 | children && (child = children->data); |
493 | children = children->next) { |
494 | GtkRequisition creq; |
495 | |
496 | if (!child->widget) { |
497 | /* Column reconfiguration. */ |
498 | for (i = 1; i < ncols; i++) { |
499 | if (colypos[0] < colypos[i]) |
500 | colypos[0] = colypos[i]; |
501 | } |
502 | ncols = child->ncols; |
503 | percentages = child->percentages; |
504 | colypos = g_renew(gint, colypos, ncols); |
505 | for (i = 1; i < ncols; i++) |
506 | colypos[i] = colypos[0]; |
507 | continue; |
508 | } |
509 | |
510 | /* Only take visible widgets into account. */ |
511 | if (!GTK_WIDGET_VISIBLE(child->widget)) |
512 | continue; |
513 | |
514 | gtk_widget_size_request(child->widget, &creq); |
515 | colspan = child->colspan ? child->colspan : ncols-child->colstart; |
516 | |
517 | /* |
518 | * To compute width: we know that creq.width plus |
519 | * cols->spacing needs to equal a certain percentage of the |
520 | * full width of the container. So we work this value out, |
521 | * figure out how wide the container will need to be to |
522 | * make that percentage of it equal to that width, and |
523 | * ensure our returned width is at least that much. Very |
524 | * simple really. |
525 | */ |
526 | { |
527 | int percent, thiswid, fullwid; |
528 | |
529 | percent = 0; |
530 | for (i = 0; i < colspan; i++) |
531 | percent += percentages[child->colstart+i]; |
532 | |
533 | thiswid = creq.width + cols->spacing; |
534 | /* |
535 | * Since creq is the _minimum_ size the child needs, we |
536 | * must ensure that it gets _at least_ that size. |
537 | * Hence, when scaling thiswid up to fullwid, we must |
538 | * round up, which means adding percent-1 before |
539 | * dividing by percent. |
540 | */ |
541 | fullwid = (thiswid * 100 + percent - 1) / percent; |
542 | |
543 | /* |
544 | * The above calculation assumes every widget gets |
545 | * cols->spacing on the right. So we subtract |
546 | * cols->spacing here to account for the extra load of |
547 | * spacing on the right. |
548 | */ |
549 | if (req->width < fullwid - cols->spacing) |
550 | req->width = fullwid - cols->spacing; |
551 | } |
552 | |
553 | /* |
554 | * To compute height: the widget's top will be positioned |
555 | * at the largest y value so far reached in any of the |
556 | * columns it crosses. Then it will go down by creq.height |
557 | * plus padding; and the point it reaches at the bottom is |
558 | * the new y value in all those columns, and minus the |
559 | * padding it is also a lower bound on our own size |
560 | * request. |
561 | */ |
562 | { |
563 | int topy, boty; |
564 | |
565 | topy = 0; |
566 | for (i = 0; i < colspan; i++) { |
567 | if (topy < colypos[child->colstart+i]) |
568 | topy = colypos[child->colstart+i]; |
569 | } |
570 | boty = topy + creq.height + cols->spacing; |
571 | for (i = 0; i < colspan; i++) { |
572 | colypos[child->colstart+i] = boty; |
573 | } |
574 | |
575 | if (req->height < boty - cols->spacing) |
576 | req->height = boty - cols->spacing; |
577 | } |
578 | } |
579 | |
580 | req->width += 2*GTK_CONTAINER(cols)->border_width; |
581 | req->height += 2*GTK_CONTAINER(cols)->border_width; |
582 | |
583 | g_free(colypos); |
584 | } |
585 | |
586 | static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc) |
587 | { |
588 | Columns *cols; |
589 | ColumnsChild *child; |
590 | GList *children; |
591 | gint i, ncols, colspan, border, *colxpos, *colypos; |
592 | const gint *percentages; |
593 | static const gint onecol[] = { 100 }; |
594 | |
595 | g_return_if_fail(widget != NULL); |
596 | g_return_if_fail(IS_COLUMNS(widget)); |
597 | g_return_if_fail(alloc != NULL); |
598 | |
599 | cols = COLUMNS(widget); |
600 | widget->allocation = *alloc; |
601 | border = GTK_CONTAINER(cols)->border_width; |
602 | |
603 | ncols = 1; |
604 | percentages = onecol; |
605 | /* colxpos gives the starting x position of each column. |
606 | * We supply n+1 of them, so that we can find the RH edge easily. |
607 | * All ending x positions are expected to be adjusted afterwards by |
608 | * subtracting the spacing. */ |
609 | colxpos = g_new(gint, 2); |
610 | colxpos[0] = 0; |
611 | colxpos[1] = alloc->width - 2*border + cols->spacing; |
612 | /* As in size_request, colypos is the lowest y reached in each column. */ |
613 | colypos = g_new(gint, 1); |
614 | colypos[0] = 0; |
615 | |
616 | for (children = cols->children; |
617 | children && (child = children->data); |
618 | children = children->next) { |
619 | GtkRequisition creq; |
620 | GtkAllocation call; |
621 | |
622 | if (!child->widget) { |
623 | gint percent; |
624 | |
625 | /* Column reconfiguration. */ |
626 | for (i = 1; i < ncols; i++) { |
627 | if (colypos[0] < colypos[i]) |
628 | colypos[0] = colypos[i]; |
629 | } |
630 | ncols = child->ncols; |
631 | percentages = child->percentages; |
632 | colypos = g_renew(gint, colypos, ncols); |
633 | for (i = 1; i < ncols; i++) |
634 | colypos[i] = colypos[0]; |
635 | colxpos = g_renew(gint, colxpos, ncols + 1); |
636 | colxpos[0] = 0; |
637 | percent = 0; |
638 | for (i = 0; i < ncols; i++) { |
639 | percent += percentages[i]; |
640 | colxpos[i+1] = (((alloc->width - 2*border) + cols->spacing) |
641 | * percent / 100); |
642 | } |
643 | continue; |
644 | } |
645 | |
646 | /* Only take visible widgets into account. */ |
647 | if (!GTK_WIDGET_VISIBLE(child->widget)) |
648 | continue; |
649 | |
650 | gtk_widget_get_child_requisition(child->widget, &creq); |
651 | colspan = child->colspan ? child->colspan : ncols-child->colstart; |
652 | |
653 | /* |
654 | * Starting x position is cols[colstart]. |
655 | * Ending x position is cols[colstart+colspan] - spacing. |
656 | * |
657 | * Unless we're forcing left, in which case the width is |
658 | * exactly the requisition width. |
659 | */ |
660 | call.x = alloc->x + border + colxpos[child->colstart]; |
661 | if (child->force_left) |
662 | call.width = creq.width; |
663 | else |
664 | call.width = (colxpos[child->colstart+colspan] - |
665 | colxpos[child->colstart] - cols->spacing); |
666 | |
667 | /* |
668 | * To compute height: the widget's top will be positioned |
669 | * at the largest y value so far reached in any of the |
670 | * columns it crosses. Then it will go down by creq.height |
671 | * plus padding; and the point it reaches at the bottom is |
672 | * the new y value in all those columns. |
673 | */ |
674 | { |
675 | int topy, boty; |
676 | |
677 | topy = 0; |
678 | for (i = 0; i < colspan; i++) { |
679 | if (topy < colypos[child->colstart+i]) |
680 | topy = colypos[child->colstart+i]; |
681 | } |
682 | call.y = alloc->y + border + topy; |
683 | call.height = creq.height; |
684 | boty = topy + creq.height + cols->spacing; |
685 | for (i = 0; i < colspan; i++) { |
686 | colypos[child->colstart+i] = boty; |
687 | } |
688 | } |
689 | |
690 | gtk_widget_size_allocate(child->widget, &call); |
691 | } |
692 | |
693 | g_free(colxpos); |
694 | g_free(colypos); |
695 | } |