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