1 /* Fo
2 * fo-area-spanning-table-cell.c: Area object for spanning-table-cell formatting objects
3 *
4 * Copyright (C) 2001 Sun Microsystems
5 * Copyright (C) 2007-2010 Menteith Consulting Ltd
6 *
7 * See COPYING for the status of this software.
8 */
9
10 #include "fo-utils.h"
11 #include "fo-area.h"
12 #include "fo-area-private.h"
13 #include "fo-area-reference.h"
14 #include "fo-area-reference-private.h"
15 #include "fo-area-table-cell.h"
16 #include "fo-area-spanning-table-cell.h"
17 #include "fo-area-spanning-table-cell-private.h"
18 #include "fo-area-table-cell-proxy.h"
19 #include "fo/fo-table-cell.h"
20 #include "fo/fo-block-fo.h"
21 #include "property/fo-property-block-progression-dimension.h"
22 #include "property/fo-property-inline-progression-dimension.h"
23 #include "property/fo-property-keep-with-next.h"
24 #include "property/fo-property-keep-with-previous.h"
25 #include "property/fo-property-keep-with-next-within-column.h"
26 #include "property/fo-property-keep-with-next-within-page.h"
27 #include "property/fo-property-keep-with-previous-within-column.h"
28 #include "property/fo-property-keep-with-previous-within-page.h"
29
30 static void _class_init (FoAreaSpanningTableCellClass *klass);
31 static void _finalize (GObject *object);
32
33 static void _debug_dump_properties (FoArea *area,
34 gint depth);
35 static FoArea* _size_request (FoArea *child);
36 static FoArea* _split_before_height (FoArea *area,
37 gdouble max_height);
38 static gboolean _split_before_height_check (FoArea *area,
39 gdouble max_height);
40
41 static gpointer parent_class;
42
43 /**
44 * fo_area_spanning_table_cell_get_type:
45 * @void:
46 *
47 * Register the FoAreaSpanningTableCell object type.
48 *
49 * Return value: GType value of the FoAreaSpanningTableCell object type.
50 **/
51 GType
fo_area_spanning_table_cell_get_type(void)52 fo_area_spanning_table_cell_get_type (void)
53 {
54 static GType object_type = 0;
55
56 if (!object_type)
57 {
58 static const GTypeInfo object_info =
59 {
60 sizeof (FoAreaSpanningTableCellClass),
61 (GBaseInitFunc) NULL,
62 (GBaseFinalizeFunc) NULL,
63 (GClassInitFunc) _class_init,
64 NULL, /* class_finalize */
65 NULL, /* class_data */
66 sizeof (FoAreaSpanningTableCell),
67 0, /* n_preallocs */
68 NULL, /* instance_init */
69 NULL
70 };
71
72 object_type = g_type_register_static (FO_TYPE_AREA_REFERENCE,
73 "FoAreaSpanningTableCell",
74 &object_info, 0);
75 }
76
77 return object_type;
78 }
79
80 /**
81 * _class_init:
82 * @klass: #FoAreaSpanningTableCellClass object to initialise.
83 *
84 * Implements #GClassInitFunc for #FoAreaSpanningTableCellClass.
85 **/
86 static void
_class_init(FoAreaSpanningTableCellClass * klass)87 _class_init (FoAreaSpanningTableCellClass *klass)
88 {
89 GObjectClass *object_class = G_OBJECT_CLASS (klass);
90
91 parent_class = g_type_class_peek_parent (klass);
92
93 object_class->finalize = _finalize;
94
95 FO_AREA_CLASS (klass)->debug_dump_properties = _debug_dump_properties;
96 FO_AREA_CLASS (klass)->size_request = _size_request;
97 FO_AREA_CLASS (klass)->split_before_height = _split_before_height;
98 FO_AREA_CLASS (klass)->split_before_height_check = _split_before_height_check;
99 }
100
101 /**
102 * _finalize:
103 * @object: #FoAreaSpanningTableCell object to finalize.
104 *
105 * Implements #GObjectFinalizeFunc for #FoAreaSpanningTableCell.
106 **/
107 static void
_finalize(GObject * object)108 _finalize (GObject *object)
109 {
110 FoAreaSpanningTableCell *fo_area_spanning_table_cell;
111
112 fo_area_spanning_table_cell = FO_AREA_SPANNING_TABLE_CELL (object);
113
114 G_OBJECT_CLASS (parent_class)->finalize (object);
115 }
116
117
118 /**
119 * fo_area_spanning_table_cell_new:
120 *
121 * Creates a new #FoAreaSpanningTableCell initialized to default value.
122 *
123 * Return value: the new #FoAreaSpanningTableCell
124 **/
125 FoArea*
fo_area_spanning_table_cell_new(void)126 fo_area_spanning_table_cell_new (void)
127 {
128 return FO_AREA (g_object_new (fo_area_spanning_table_cell_get_type (), NULL));
129 }
130
131
132 static void
fo_area_spanning_table_cell_accumulate_proxy_available_height(gpointer value,gpointer data)133 fo_area_spanning_table_cell_accumulate_proxy_available_height (gpointer value,
134 gpointer data)
135 {
136 FoArea *proxy = FO_AREA (value);
137 gdouble *total = (gdouble *) data;
138
139 *total += fo_area_get_available_height (proxy);
140 }
141
142 #if 0
143 static void
144 fo_area_spanning_table_cell_debug_dump_proxy_height (gpointer value,
145 gpointer data)
146 {
147 FoArea *proxy = FO_AREA (value);
148
149 g_message ("proxy: %s; available_height: %f; height: %f",
150 fo_object_debug_sprintf (proxy),
151 fo_area_get_available_height (proxy),
152 fo_area_area_get_height (proxy));
153 }
154
155 static void
156 fo_area_spanning_table_cell_debug_dump_heights (FoArea *fo_area)
157 {
158 g_message ("spanning_table_cell:: area: %s; available_height: %g; height: %g; real_height: %g; real_available_height: %g",
159 fo_object_debug_sprintf (fo_area),
160 fo_area_get_available_height (fo_area),
161 fo_area_area_get_height (fo_area),
162 FO_AREA_SPANNING_TABLE_CELL (fo_area)->real_height,
163 FO_AREA_SPANNING_TABLE_CELL (fo_area)->real_available_height);
164 g_list_foreach (FO_AREA_SPANNING_TABLE_CELL (fo_area)->proxies,
165 fo_area_spanning_table_cell_debug_dump_proxy_height,
166 0);
167 }
168 #endif
169
170 /**
171 * fo_area_spanning_table_cell_signal_test:
172 * @signallee:
173 * @param:
174 * @signaller:
175 *
176 * Function to test how property value change signals work.
177 **/
178 static void
fo_area_spanning_table_cell_signal_test(FoArea * signallee,GParamSpec * param G_GNUC_UNUSED,FoArea * signaller G_GNUC_UNUSED)179 fo_area_spanning_table_cell_signal_test (FoArea *signallee,
180 GParamSpec *param G_GNUC_UNUSED,
181 FoArea *signaller G_GNUC_UNUSED)
182 {
183 gdouble old_real_available_height;
184 gdouble new_real_available_height;
185 gdouble real_height;
186
187 old_real_available_height = FO_AREA_SPANNING_TABLE_CELL (signallee)->real_available_height;
188 real_height = FO_AREA_SPANNING_TABLE_CELL (signallee)->real_height;
189
190 new_real_available_height = fo_area_get_available_height (signallee);
191 g_list_foreach (FO_AREA_SPANNING_TABLE_CELL (signallee)->proxies,
192 fo_area_spanning_table_cell_accumulate_proxy_available_height,
193 &new_real_available_height);
194
195 FO_AREA_SPANNING_TABLE_CELL (signallee)->real_available_height = new_real_available_height;
196 fo_area_set_child_available_bpdim (signallee,
197 new_real_available_height -
198 fo_area_area_get_border_before (signallee) -
199 fo_area_area_get_padding_before (signallee) -
200 fo_area_area_get_padding_after (signallee) -
201 fo_area_area_get_border_after (signallee));
202 #if defined(LIBFO_DEBUG) && 1
203 g_message ("signal_test:: real_height: %g; old real_available_height: %g; new real_available_height: %g; child-available-bpdim: %g",
204 real_height,
205 old_real_available_height,
206 new_real_available_height,
207 fo_area_get_child_available_bpdim (signallee));
208 fo_area_spanning_table_cell_debug_dump_heights (signallee);
209 #endif
210 /* The first child is placed just inside the borders and padding of
211 this table cell. */
212 fo_area_set_next_x (signallee,
213 fo_area_area_get_border_start (signallee) +
214 fo_area_area_get_padding_start (signallee));
215
216 gdouble total_child_height = 0.0;
217 fo_area_children_foreach (signallee,
218 G_TRAVERSE_ALL,
219 &fo_area_accumulate_height,
220 &total_child_height);
221 /* Adjust initial 'next_y' depending on 'display-align' property
222 value. */
223 FoDatatype *fo_cell_display_align =
224 fo_property_get_value (fo_table_cell_get_display_align (signallee->generated_by));
225
226 FoEnumEnum display_align =
227 fo_enum_get_value (fo_cell_display_align);
228
229 gdouble next_y;
230 switch (display_align)
231 {
232 case FO_ENUM_ENUM_AUTO:
233 /* Treat as 'before' while 'relative-align' is
234 unimplemented. */
235 case FO_ENUM_ENUM_BEFORE:
236 next_y =
237 - (fo_area_area_get_border_before (signallee) +
238 fo_area_area_get_padding_before (signallee));
239 break;
240 case FO_ENUM_ENUM_CENTER:
241 next_y =
242 - (fo_area_area_get_border_before (signallee) +
243 fo_area_area_get_padding_before (signallee) +
244 (fo_area_get_child_available_bpdim (signallee) -
245 total_child_height) / 2.0);
246 break;
247 case FO_ENUM_ENUM_AFTER:
248 next_y =
249 - (fo_area_area_get_border_before (signallee) +
250 fo_area_area_get_padding_before (signallee) +
251 (fo_area_get_child_available_bpdim (signallee) -
252 total_child_height));
253 break;
254 default:
255 g_assert_not_reached ();
256 }
257 fo_area_set_next_y (signallee,
258 next_y);
259 /* Since the children all fit within the allowed height, adjust
260 their sizes and positions in sequence. */
261 fo_area_children_foreach (signallee,
262 G_TRAVERSE_ALL,
263 &fo_area_size_adjust,
264 NULL);
265
266 }
267
268 /**
269 * fo_area_spanning_table_cell_new_with_rows_spanned:
270 * @rows_spanned: Number of rows spanned by the new #FoAreaSpanningTableCell
271 *
272 * Creates a new #FoAreaSpanningTableCell initialized to span @rows_spanned rows.
273 *
274 * Return value: the new #FoAreaSpanningTableCell
275 **/
276 FoArea*
fo_area_spanning_table_cell_new_with_rows_spanned(gint rows_spanned)277 fo_area_spanning_table_cell_new_with_rows_spanned (gint rows_spanned)
278 {
279 gint current_row;
280 FoAreaSpanningTableCell *spanning_table_cell =
281 FO_AREA_SPANNING_TABLE_CELL (fo_area_spanning_table_cell_new ());
282
283 g_object_connect (spanning_table_cell,
284 "swapped_signal::notify::available-height",
285 fo_area_spanning_table_cell_signal_test,
286 spanning_table_cell,
287 NULL);
288
289 spanning_table_cell->rows_spanned = rows_spanned;
290 spanning_table_cell->row_info =
291 g_new0 (FoAreaSpanningTableCellRowInfo, rows_spanned);
292
293 for (current_row = 2; current_row <= rows_spanned; current_row++)
294 {
295 FoArea *proxy = fo_area_table_cell_proxy_new ();
296
297 g_object_connect (proxy,
298 "swapped_signal::notify::available-height",
299 fo_area_spanning_table_cell_signal_test,
300 spanning_table_cell,
301 NULL);
302 spanning_table_cell->proxies = g_list_append (spanning_table_cell->proxies,
303 proxy);
304 fo_area_table_cell_proxy_set_row_number (proxy,
305 current_row);
306 fo_area_table_cell_proxy_set_table_cell (proxy,
307 FO_AREA (spanning_table_cell));
308 }
309
310 return FO_AREA (spanning_table_cell);
311 }
312
313 /**
314 * fo_area_spanning_table_cell_get_nth_row_proxy:
315 * @fo_area: The #FoAreaSpanningTableCell object
316 * @row_number: The row number of proxy area to get
317 *
318 * Gets the #FoSpanningTableCellProxy object at the @row_number row number.
319 *
320 * @row number should be in the range 2...rows-spanned, where
321 * 'rows-spanned' is the number of rows spanned by @fo_area.
322 *
323 * When @fo_area spans only one row, this function always returns
324 * NULL.
325 *
326 * Return value: The #FoSpanningTableCellProxy object, or NULL if no proxy
327 **/
328 FoArea*
fo_area_spanning_table_cell_get_nth_row_proxy(FoArea * fo_area,gint row_number)329 fo_area_spanning_table_cell_get_nth_row_proxy (FoArea *fo_area,
330 gint row_number)
331 {
332 g_return_val_if_fail (fo_area != NULL, NULL);
333 g_return_val_if_fail (FO_IS_AREA_SPANNING_TABLE_CELL (fo_area), NULL);
334
335 /* Subtract 2 from row_number since proxies' row numbers start at 2
336 and g_list indexes start at 0 */
337 return g_list_nth_data (FO_AREA_SPANNING_TABLE_CELL (fo_area)->proxies,
338 row_number - 2);
339 }
340
341 /**
342 * fo_area_spanning_table_cell_debug_dump_proxy:
343 * @value: Pointer to the proxy
344 * @data: Indent level
345 *
346 * Logs debug dump info for one proxy of an #FoAreaSpanningTableCell.
347 **/
348 static void
fo_area_spanning_table_cell_debug_dump_proxy(gpointer value,gpointer data)349 fo_area_spanning_table_cell_debug_dump_proxy (gpointer value, gpointer data)
350 {
351 gchar *indent = g_strnfill (GPOINTER_TO_INT (data) * 2, ' ');
352 gchar *string = fo_object_debug_sprintf (value);
353
354 g_log (G_LOG_DOMAIN,
355 G_LOG_LEVEL_DEBUG,
356 "%s%s",
357 indent,
358 string);
359
360 g_free (string);
361 g_free (indent);
362 }
363
364 /**
365 * _debug_dump_properties:
366 * @area: The #FoArea object
367 * @depth: Indent level to add to the output
368 *
369 * Logs the value of each significant property of @area then calls
370 * debug_dump_properties method of parent class.
371 **/
372 void
_debug_dump_properties(FoArea * area,gint depth)373 _debug_dump_properties (FoArea *area,
374 gint depth)
375 {
376 FoAreaSpanningTableCell *spanning_table_cell;
377 gchar *indent = g_strnfill (depth * 2, ' ');
378
379 g_return_if_fail (area != NULL);
380 g_return_if_fail (FO_IS_AREA_SPANNING_TABLE_CELL (area));
381
382 spanning_table_cell = FO_AREA_SPANNING_TABLE_CELL (area);
383
384 g_log (G_LOG_DOMAIN,
385 G_LOG_LEVEL_DEBUG,
386 "%sproxies:",
387 indent);
388 g_list_foreach (spanning_table_cell->proxies,
389 fo_area_spanning_table_cell_debug_dump_proxy,
390 GINT_TO_POINTER (depth + 1));
391
392 g_log (G_LOG_DOMAIN,
393 G_LOG_LEVEL_DEBUG,
394 "%srows-spanned: %d",
395 indent,
396 spanning_table_cell->rows_spanned);
397
398 g_log (G_LOG_DOMAIN,
399 G_LOG_LEVEL_DEBUG,
400 "%sreal-height: %fpt",
401 indent,
402 spanning_table_cell->real_height);
403
404 g_log (G_LOG_DOMAIN,
405 G_LOG_LEVEL_DEBUG,
406 "%sreal-available-height: %fpt",
407 indent,
408 spanning_table_cell->real_available_height);
409
410 g_free (indent);
411 FO_AREA_CLASS (parent_class)->debug_dump_properties (area, depth + 1);
412 }
413
414 /**
415 * fo_area_spanning_table_cell_get_real_height:
416 * @fo_area: The #FoAreaSpanningTableCell object
417 *
418 * Gets the #real-height property of @fo_area
419 *
420 * Return value: The "real_height" property value
421 **/
422 gdouble
fo_area_spanning_table_cell_get_real_height(FoArea * fo_area)423 fo_area_spanning_table_cell_get_real_height (FoArea *fo_area)
424 {
425 g_return_val_if_fail (fo_area != NULL, 0);
426 g_return_val_if_fail (FO_IS_AREA_SPANNING_TABLE_CELL (fo_area), 0);
427
428 return FO_AREA_SPANNING_TABLE_CELL (fo_area)->real_height;
429 }
430
431 /**
432 * fo_area_spanning_table_cell_get_real_available_height:
433 * @fo_area: The #FoAreaSpanningTableCell object
434 *
435 * Gets the "real-available-height" property of @fo_area
436 *
437 * Return value: The "real-available-height" property value
438 **/
439 gdouble
fo_area_spanning_table_cell_get_real_available_height(FoArea * fo_area)440 fo_area_spanning_table_cell_get_real_available_height (FoArea *fo_area)
441 {
442 g_return_val_if_fail (fo_area != NULL, 0);
443 g_return_val_if_fail (FO_IS_AREA_SPANNING_TABLE_CELL (fo_area), 0);
444
445 return FO_AREA_SPANNING_TABLE_CELL (fo_area)->real_available_height;
446 }
447
448 /**
449 * _size_request:
450 * @child: Child area
451 *
452 * Check that the parent area of @child has sufficient space for
453 * @child. If not enough space, request that the parent has
454 * sufficient space allocated for it, then adjust @child and its
455 * siblings as necessary to fit into the resized parent area.
456 *
457 * Return value: Pointer to the last area generated from @child after
458 * any reallocation and resizing
459 **/
460 static FoArea *
_size_request(FoArea * child)461 _size_request (FoArea *child)
462 {
463 g_return_val_if_fail (child != NULL, NULL);
464 g_return_val_if_fail (FO_IS_AREA_AREA (child), NULL);
465 g_return_val_if_fail (!FO_AREA_IS_ROOT (child), NULL);
466 g_return_val_if_fail (fo_area_parent (child) != NULL, NULL);
467 g_return_val_if_fail (FO_IS_AREA_SPANNING_TABLE_CELL (fo_area_parent (child)), NULL);
468
469 /* The area that is the return value of this procedure. */
470 FoArea *return_child;
471 /* Used when determining the return value. */
472 FoArea *child_original_next_part = child->next_part;
473
474 /* The table cell containing the area that issued this request. */
475 FoArea *parent = fo_area_parent (child);
476
477 /* Find the total height of all children. */
478 gdouble total_child_height = 0.0;
479 fo_area_children_foreach (parent,
480 G_TRAVERSE_ALL,
481 &fo_area_accumulate_height,
482 &total_child_height);
483
484 /* Ideally, this table cell would be just big enough for its borders
485 and padding plus the height needed for its children. */
486 gdouble parent_target_height = total_child_height +
487 fo_area_area_get_border_before (parent) +
488 fo_area_area_get_padding_before (parent) +
489 fo_area_area_get_padding_after (parent) +
490 fo_area_area_get_border_after (parent);
491
492 FO_AREA_SPANNING_TABLE_CELL (parent)->real_height = parent_target_height;
493
494 /* The FoFo that generated this table cell may have set the allowed
495 height. */
496 FoDatatype *fo_cell_bpdim =
497 fo_property_get_value (fo_table_cell_get_block_progression_dimension (parent->generated_by));
498
499 gdouble parent_use_height = 0.0;
500 if (FO_IS_LENGTH_RANGE (fo_cell_bpdim))
501 {
502 FoDatatype *min_datatype =
503 fo_length_range_get_minimum (fo_cell_bpdim);
504 FoDatatype *opt_datatype =
505 fo_length_range_get_optimum (fo_cell_bpdim);
506 FoDatatype *max_datatype =
507 fo_length_range_get_maximum (fo_cell_bpdim);
508
509 if (FO_IS_LENGTH (min_datatype) &&
510 parent_target_height <= fo_length_get_value (min_datatype))
511 {
512 parent_use_height = fo_length_get_value (min_datatype);
513
514 #if defined(LIBFO_DEBUG) && 0
515 g_message ("spanning_table_cell_size_request:: target: %g; min: %g",
516 parent_target_height,
517 fo_length_get_value (min_datatype));
518 #endif
519 }
520 else if (FO_IS_LENGTH (opt_datatype) &&
521 parent_target_height < fo_length_get_value (opt_datatype))
522 {
523 parent_use_height = fo_length_get_value (opt_datatype);
524
525 #if defined(LIBFO_DEBUG) && 0
526 g_message ("spanning_table_cell_size_request:: target: %g; opt: %g",
527 parent_target_height,
528 fo_length_get_value (opt_datatype));
529 #endif
530 }
531 else if (FO_IS_LENGTH (max_datatype) &&
532 fo_length_get_value (max_datatype) < parent_target_height)
533 {
534 parent_use_height = fo_length_get_value (max_datatype);
535
536 #if defined(LIBFO_DEBUG) && 0
537 g_message ("spanning_table_cell_size_request:: target: %g; max: %g",
538 parent_target_height,
539 fo_length_get_value (max_datatype));
540 #endif
541 }
542 else
543 {
544 parent_use_height = parent_target_height;
545 }
546 }
547 else
548 {
549 /* The 'block_progression_dimension' property should only ever
550 be a length-range. */
551 g_assert_not_reached ();
552 }
553
554 gdouble parent_real_available_height =
555 fo_area_get_available_height (parent);
556
557 g_list_foreach (FO_AREA_SPANNING_TABLE_CELL (parent)->proxies,
558 fo_area_spanning_table_cell_accumulate_proxy_available_height,
559 &parent_real_available_height);
560 gint rows_spanned = FO_AREA_SPANNING_TABLE_CELL (parent)->rows_spanned;
561
562 #if defined(LIBFO_DEBUG) && 1
563 fo_area_spanning_table_cell_debug_dump_heights (parent);
564 g_message ("spanning_table_cell_size_request:: parent_available: %g; parent_use: %g",
565 parent_available_height,
566 parent_use_height);
567 #endif
568
569 /* The available height may be less than what is required (or what
570 is allowed by the FoFo). */
571 if (parent_real_available_height < parent_use_height)
572 {
573 gdouble remaining_height = parent_use_height;
574 gint row_number;
575
576 fo_area_area_set_height (parent,
577 fo_area_get_available_height (parent));
578
579 remaining_height -= fo_area_get_available_height (parent);
580
581 for (row_number = 2;
582 (row_number <= rows_spanned - 1) && remaining_height > 0;
583 row_number++)
584 {
585 FoArea *proxy =
586 fo_area_spanning_table_cell_get_nth_row_proxy (parent,
587 row_number);
588
589 fo_area_area_set_height (proxy,
590 fo_area_get_available_height (proxy));
591
592 remaining_height -= fo_area_get_available_height (proxy);
593 }
594
595 if (remaining_height > 0)
596 {
597 FoArea *last_proxy =
598 fo_area_spanning_table_cell_get_nth_row_proxy (parent,
599 rows_spanned);
600
601 fo_area_area_set_height (last_proxy, remaining_height);
602 last_proxy = fo_area_size_request (last_proxy);
603 }
604 }
605
606 /* Work out the total child height again because this area's parent
607 area may have adjusted the children. */
608 total_child_height = 0;
609 fo_area_children_foreach (parent,
610 G_TRAVERSE_ALL,
611 &fo_area_accumulate_height,
612 &total_child_height);
613
614 /* Work out the new target height. */
615 parent_target_height =
616 total_child_height +
617 fo_area_area_get_border_before (parent) +
618 fo_area_area_get_padding_before (parent) +
619 fo_area_area_get_padding_after (parent) +
620 fo_area_area_get_border_after (parent);
621
622 /* In the absence of 'display-align', the first child is placed just
623 inside the borders and padding of this table cell. */
624 fo_area_set_next_x (parent,
625 fo_area_area_get_border_start (parent) +
626 fo_area_area_get_padding_start (parent));
627 fo_area_set_next_y (parent,
628 - (fo_area_area_get_border_before (parent) +
629 fo_area_area_get_padding_before (parent)));
630
631 if (parent_target_height <= parent_real_available_height)
632 {
633 /* Adjust initial 'next_y' depending on 'display-align' property
634 value. */
635 FoDatatype *fo_cell_display_align =
636 fo_property_get_value (fo_table_cell_get_display_align (parent->generated_by));
637
638 FoEnumEnum display_align =
639 fo_enum_get_value (fo_cell_display_align);
640
641 gdouble next_y;
642 switch (display_align)
643 {
644 case FO_ENUM_ENUM_AUTO:
645 /* Treat as 'before' while 'relative-align' is
646 unimplemented. */
647 case FO_ENUM_ENUM_BEFORE:
648 next_y =
649 - (fo_area_area_get_border_before (parent) +
650 fo_area_area_get_padding_before (parent));
651 break;
652 case FO_ENUM_ENUM_CENTER:
653 next_y =
654 - (fo_area_area_get_border_before (parent) +
655 fo_area_area_get_padding_before (parent) +
656 (parent_real_available_height -
657 parent_target_height) / 2.0);
658 break;
659 case FO_ENUM_ENUM_AFTER:
660 next_y =
661 - (fo_area_area_get_border_before (parent) +
662 fo_area_area_get_padding_before (parent) +
663 (parent_real_available_height -
664 parent_target_height));
665 break;
666 default:
667 g_assert_not_reached ();
668 }
669 fo_area_set_next_y (parent,
670 next_y);
671 /* Since the children all fit within the allowed height, adjust
672 their sizes and positions in sequence. */
673 fo_area_children_foreach (parent,
674 G_TRAVERSE_ALL,
675 &fo_area_size_adjust,
676 NULL);
677 }
678 else
679 {
680 /* Since the children don't all fit, place the ones that fit in
681 the available height, and split at or before the allowed
682 height. */
683 fo_area_children_foreach (parent,
684 G_TRAVERSE_ALL,
685 &fo_area_set_or_split,
686 NULL);
687 #if defined(LIBFO_DEBUG) && 0
688 g_message ("spanning_table_cell_size_request (%p):: total > available:: return:: parent->last: %s; generated by: %s",
689 child,
690 fo_object_debug_sprintf (fo_area_last_child (parent)),
691 fo_object_debug_sprintf (fo_area_last_child (parent)->generated_by));
692 #endif
693 }
694
695 /* The result will be the current child unless the child has been
696 split, in which case the result is the portion after the last
697 split. */
698 return_child = child;
699
700 while ((return_child->next_part != NULL) &&
701 (return_child->next_part != child_original_next_part))
702 {
703 return_child = return_child->next_part;
704 }
705
706 return return_child;
707 }
708
709 /* return the new area containing what comes after the split */
710 /* leave @area as area remaining after split */
711 FoArea*
_split_before_height(FoArea * area,gdouble max_height)712 _split_before_height (FoArea *area,
713 gdouble max_height)
714 {
715 FoArea *use_child_area;
716 gdouble minus_child_y = 0.0;
717 gdouble child_height = 0.0;
718
719 g_return_val_if_fail (FO_IS_AREA_SPANNING_TABLE_CELL (area), NULL);
720 g_return_val_if_fail (fo_area_n_children (area) > 0, NULL);
721 g_return_val_if_fail (max_height > 0, NULL);
722
723 /* if the current area is less than max height, then no new area */
724 if (fo_area_area_get_height (area) < max_height)
725 return NULL;
726
727 use_child_area = fo_area_first_child (area);
728
729 while (use_child_area)
730 {
731 minus_child_y = -fo_area_area_get_y (use_child_area);
732 child_height = fo_area_area_get_height (use_child_area);
733
734 if (minus_child_y + child_height >= max_height)
735 break;
736 else
737 use_child_area = fo_area_next_sibling (use_child_area);
738 }
739
740 #if defined(LIBFO_DEBUG) && 1
741 g_message ("spanning_table_cell_split_before_height:: splitting: area: %s; generated by: %s; y: %f; height: %f",
742 fo_object_debug_sprintf (use_child_area),
743 fo_object_debug_sprintf (fo_area_get_generated_by (use_child_area)),
744 fo_area_area_get_y (use_child_area),
745 fo_area_area_get_height (use_child_area));
746 #endif
747
748 if (use_child_area == NULL)
749 return NULL;
750
751 if (minus_child_y >= max_height)
752 {
753 /* max_height falls before use_child_area, i.e. in space between areas */
754
755 if (use_child_area == fo_area_first_child (area))
756 {
757 return NULL;
758 }
759 else
760 {
761 FoFo *child_fo =
762 fo_area_get_generated_by (use_child_area);
763 FoDatatype *child_kwpwp_datatype =
764 fo_keep_get_within_page (fo_property_get_value (fo_block_fo_get_keep_with_previous (child_fo)));
765 FoDatatype *child_kwpwc_datatype =
766 fo_keep_get_within_column (fo_property_get_value (fo_block_fo_get_keep_with_previous (child_fo)));
767
768 FoFo *prev_child_fo =
769 fo_area_get_generated_by (fo_area_prev_sibling (use_child_area));
770 FoDatatype *prev_child_kwnwp_datatype =
771 fo_keep_get_within_page (fo_property_get_value (fo_block_fo_get_keep_with_next (prev_child_fo)));
772 FoDatatype *prev_child_kwnwc_datatype =
773 fo_keep_get_within_column (fo_property_get_value (fo_block_fo_get_keep_with_next (prev_child_fo)));
774
775 /* FIXME: Need to handle integer keeps */
776 if ((FO_IS_ENUM (prev_child_kwnwp_datatype) &&
777 fo_enum_get_value (prev_child_kwnwp_datatype) == FO_ENUM_ENUM_AUTO) &&
778 (FO_IS_ENUM (prev_child_kwnwc_datatype) &&
779 fo_enum_get_value (prev_child_kwnwc_datatype) == FO_ENUM_ENUM_AUTO) &&
780 (FO_IS_ENUM (child_kwpwp_datatype) &&
781 fo_enum_get_value (child_kwpwp_datatype) == FO_ENUM_ENUM_AUTO) &&
782 (FO_IS_ENUM (child_kwpwc_datatype) &&
783 fo_enum_get_value (child_kwpwc_datatype) == FO_ENUM_ENUM_AUTO))
784 {
785 /* If got to here, all relevant keeps are 'auto' */
786 FoArea *clone = g_object_ref_sink (fo_area_clone (area));
787
788 fo_area_unlink_with_next_siblings (use_child_area);
789 fo_area_insert_with_next_siblings (clone, 0, use_child_area);
790
791 return clone;
792 }
793 else
794 {
795 gdouble minus_prev_y =
796 fo_area_area_get_y (fo_area_prev_sibling (use_child_area));
797 gdouble prev_height =
798 fo_area_area_get_height (fo_area_prev_sibling (use_child_area));
799 /* If can't split between use_child_area and previous, maybe
800 can split at lower height */
801 return _split_before_height (area,
802 minus_prev_y +
803 prev_height);
804 }
805 }
806 }
807 else
808 {
809 /* max_height falls within use_child_area */
810 gboolean child_can_split =
811 fo_area_split_before_height_check (use_child_area,
812 max_height -
813 minus_child_y);
814
815 if (child_can_split)
816 {
817 FoArea *clone = g_object_ref_sink (fo_area_clone (area));
818 FoArea *split_child = fo_area_split_before_height (use_child_area,
819 max_height -
820 minus_child_y);
821 fo_area_unlink_with_next_siblings (split_child);
822 fo_area_insert_with_next_siblings (clone, 0, split_child);
823
824 return clone;
825 }
826 else
827 {
828 /* If can't split use_child_area, maybe
829 can split at lower height */
830 return _split_before_height (area,
831 minus_child_y);
832 }
833 }
834 }
835
836 /* return the new area containing what comes after the split */
837 /* leave @area as area remaining after split */
838 gboolean
_split_before_height_check(FoArea * area,gdouble max_height)839 _split_before_height_check (FoArea *area,
840 gdouble max_height)
841 {
842 FoArea *use_child_area;
843 gdouble minus_child_y = 0.0;
844 gdouble child_height = 0.0;
845
846 g_return_val_if_fail (FO_IS_AREA_SPANNING_TABLE_CELL (area), FALSE);
847 g_return_val_if_fail (fo_area_n_children (area) > 0, FALSE);
848 g_return_val_if_fail (max_height > 0, FALSE);
849
850 /* if the current area is less than max height, then no new area */
851 if (fo_area_area_get_height (area) < max_height)
852 return FALSE;
853
854 use_child_area = fo_area_first_child (area);
855
856 while (use_child_area)
857 {
858 minus_child_y = -fo_area_area_get_y (use_child_area);
859 child_height = fo_area_area_get_height (use_child_area);
860
861 if (minus_child_y + child_height >= max_height)
862 break;
863 else
864 use_child_area = fo_area_next_sibling (use_child_area);
865 }
866
867 #if defined(LIBFO_DEBUG) && 1
868 g_message ("spanning_table_cell_split_before_height_check:: splitting: area: %s; generated by: %s; y: %f; height: %f",
869 fo_object_debug_sprintf (use_child_area),
870 fo_object_debug_sprintf (fo_area_get_generated_by (use_child_area)),
871 fo_area_area_get_y (use_child_area),
872 fo_area_area_get_height (use_child_area));
873 #endif
874
875 if (use_child_area == NULL)
876 return FALSE;
877
878 if (minus_child_y >= max_height)
879 {
880 /* max_height falls before use_child_area, i.e. in space between areas */
881
882 if (use_child_area == fo_area_first_child (area))
883 {
884 return FALSE;
885 }
886 else
887 {
888 FoFo *child_fo =
889 fo_area_get_generated_by (use_child_area);
890 FoDatatype *child_kwpwp_datatype =
891 fo_keep_get_within_page (fo_property_get_value (fo_block_fo_get_keep_with_previous (child_fo)));
892 FoDatatype *child_kwpwc_datatype =
893 fo_keep_get_within_column (fo_property_get_value (fo_block_fo_get_keep_with_previous (child_fo)));
894
895 FoFo *prev_child_fo =
896 fo_area_get_generated_by (fo_area_prev_sibling (use_child_area));
897 FoDatatype *prev_child_kwnwp_datatype =
898 fo_keep_get_within_page (fo_property_get_value (fo_block_fo_get_keep_with_next (prev_child_fo)));
899 FoDatatype *prev_child_kwnwc_datatype =
900 fo_keep_get_within_column (fo_property_get_value (fo_block_fo_get_keep_with_next (prev_child_fo)));
901
902 /* FIXME: Need to handle integer keeps */
903 if ((FO_IS_ENUM (prev_child_kwnwp_datatype) &&
904 fo_enum_get_value (prev_child_kwnwp_datatype) == FO_ENUM_ENUM_AUTO) &&
905 (FO_IS_ENUM (prev_child_kwnwc_datatype) &&
906 fo_enum_get_value (prev_child_kwnwc_datatype) == FO_ENUM_ENUM_AUTO) &&
907 (FO_IS_ENUM (child_kwpwp_datatype) &&
908 fo_enum_get_value (child_kwpwp_datatype) == FO_ENUM_ENUM_AUTO) &&
909 (FO_IS_ENUM (child_kwpwc_datatype) &&
910 fo_enum_get_value (child_kwpwc_datatype) == FO_ENUM_ENUM_AUTO))
911 {
912 /* If got to here, all relevant keeps are 'auto' */
913 return TRUE;
914 }
915 else
916 {
917 gdouble minus_prev_y =
918 fo_area_area_get_y (fo_area_prev_sibling (use_child_area));
919 gdouble prev_height =
920 fo_area_area_get_height (fo_area_prev_sibling (use_child_area));
921 /* If can't split between use_child_area and previous, maybe
922 can split at lower height */
923 return _split_before_height_check (area,
924 minus_prev_y +
925 prev_height);
926 }
927 }
928 }
929 else
930 {
931 /* max_height falls within use_child_area */
932 gboolean child_can_split = fo_area_split_before_height_check (use_child_area,
933 max_height -
934 minus_child_y);
935
936 if (child_can_split)
937 {
938 return TRUE;
939 }
940 else
941 {
942 /* If can't split use_child_area, maybe
943 can split at lower height */
944 return _split_before_height_check (area,
945 minus_child_y);
946 }
947 }
948 }
949