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