1 /* Fo
2  * fo-area-table-cell.c: Area object for 'table-cell' formatting objects
3  *
4  * Copyright (C) 2001 Sun Microsystems
5  * Copyright (C) 2007-2009 Menteith Consulting Ltd
6  *
7  * See COPYING for the status of this software.
8  */
9 
10 #include "fo-utils.h"
11 #include "fo-area-table-cell-private.h"
12 #include "fo-area-table-cell-proxy.h"
13 #include "fo/fo-block-fo.h"
14 #include "fo/fo-table-cell.h"
15 #include "property/fo-property-block-progression-dimension.h"
16 #include "property/fo-property-inline-progression-dimension.h"
17 #include "property/fo-property-keep-with-next.h"
18 #include "property/fo-property-keep-with-previous.h"
19 #include "property/fo-property-keep-with-next-within-column.h"
20 #include "property/fo-property-keep-with-next-within-page.h"
21 #include "property/fo-property-keep-with-previous-within-column.h"
22 #include "property/fo-property-keep-with-previous-within-page.h"
23 
24 static void fo_area_table_cell_class_init  (FoAreaTableCellClass *klass);
25 static void fo_area_table_cell_finalize    (GObject           *object);
26 
27 static void fo_area_table_cell_debug_dump_properties (FoArea *area,
28 							      gint depth);
29 static FoArea* fo_area_table_cell_size_request (FoArea *child);
30 static FoArea* fo_area_table_cell_split_before_height (FoArea *area,
31 						      gdouble max_height);
32 static gboolean fo_area_table_cell_split_before_height_check (FoArea *area,
33 							     gdouble max_height);
34 static void fo_area_table_cell_size_adjust (FoArea *area,
35 					    gpointer data);
36 
37 static void fo_area_table_cell_set_or_split (FoArea *area,
38 					     gpointer data);
39 static void fo_area_table_cell_update_after_clone (FoArea *clone,
40 						   FoArea *original);
41 
42 static gpointer parent_class;
43 
44 /**
45  * fo_area_table_cell_get_type:
46  *
47  * Register the #FoAreaTableCell object type.
48  *
49  * Return value: #GType value of the #FoAreaTableCell object type.
50  **/
51 GType
fo_area_table_cell_get_type(void)52 fo_area_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 (FoAreaTableCellClass),
61 	  (GBaseInitFunc) NULL,
62 	  (GBaseFinalizeFunc) NULL,
63 	  (GClassInitFunc) fo_area_table_cell_class_init,
64 	  NULL,           /* class_finalize */
65 	  NULL,           /* class_data */
66 	  sizeof (FoAreaTableCell),
67 	  0,              /* n_preallocs */
68 	  NULL,		/* instance_init */
69 	  NULL		/* value_table */
70 	};
71 
72       object_type = g_type_register_static (FO_TYPE_AREA_REFERENCE,
73                                             "FoAreaTableCell",
74                                             &object_info, 0);
75     }
76 
77   return object_type;
78 }
79 
80 static void
fo_area_table_cell_class_init(FoAreaTableCellClass * klass)81 fo_area_table_cell_class_init (FoAreaTableCellClass *klass)
82 {
83   GObjectClass *object_class = G_OBJECT_CLASS (klass);
84 
85   parent_class = g_type_class_peek_parent (klass);
86 
87   object_class->finalize = fo_area_table_cell_finalize;
88 
89   FO_AREA_CLASS (klass)->debug_dump_properties = fo_area_table_cell_debug_dump_properties;
90   FO_AREA_CLASS (klass)->size_request = fo_area_table_cell_size_request;
91   FO_AREA_CLASS (klass)->split_before_height = fo_area_table_cell_split_before_height;
92   FO_AREA_CLASS (klass)->split_before_height_check = fo_area_table_cell_split_before_height_check;
93   FO_AREA_CLASS (klass)->update_after_clone =
94     fo_area_table_cell_update_after_clone;
95 }
96 
97 static void
fo_area_table_cell_finalize(GObject * object)98 fo_area_table_cell_finalize (GObject *object)
99 {
100   FoAreaTableCell *fo_area_table_cell;
101 
102   fo_area_table_cell = FO_AREA_TABLE_CELL (object);
103 
104   G_OBJECT_CLASS (parent_class)->finalize (object);
105 }
106 
107 
108 /**
109  * fo_area_table_cell_new:
110  *
111  * Creates a new #FoAreaTableCell initialized to default value.
112  *
113  * Return value: the new #FoAreaTableCell
114  **/
115 FoArea*
fo_area_table_cell_new(void)116 fo_area_table_cell_new (void)
117 {
118   return FO_AREA (g_object_new (fo_area_table_cell_get_type (), NULL));
119 }
120 
121 
122 /**
123  * fo_area_table_cell_debug_dump_properties:
124  * @area:  The #FoArea object
125  * @depth: Indent level to add to the output
126  *
127  * Logs the value of each significant property of @area then calls
128  * debug_dump_properties method of parent class.
129  **/
130 void
fo_area_table_cell_debug_dump_properties(FoArea * area,gint depth)131 fo_area_table_cell_debug_dump_properties (FoArea *area,
132 					  gint depth)
133 {
134   FoAreaTableCell *table_cell;
135   gchar *indent = g_strnfill (depth * 2, ' ');
136 
137   g_return_if_fail (area != NULL);
138   g_return_if_fail (FO_IS_AREA_TABLE_CELL (area));
139 
140   table_cell = FO_AREA_TABLE_CELL (area);
141 
142   g_free (indent);
143   FO_AREA_CLASS (parent_class)->debug_dump_properties (area, depth + 1);
144 }
145 
146 /**
147  * fo_area_table_cell_size_adjust:
148  * @area: #FoArea node to be placed within parent
149  * @data: Not used
150  *
151  * Place @area within its parent and adjust the parent's next-x and
152  * next-y properties accordingly.
153  **/
154 void
fo_area_table_cell_size_adjust(FoArea * area,gpointer data G_GNUC_UNUSED)155 fo_area_table_cell_size_adjust (FoArea  *area,
156 				gpointer data G_GNUC_UNUSED)
157 {
158   g_return_if_fail (FO_IS_AREA (area));
159   g_return_if_fail (FO_IS_AREA_TABLE_CELL (fo_area_parent (area)));
160 
161   FoArea *table_cell = fo_area_parent (area);
162 
163   fo_area_area_set_x (area,
164 		      fo_area_get_next_x (table_cell) +
165 		      fo_area_area_get_start_indent (area));
166   fo_area_area_set_y (area,
167 		      fo_area_get_next_y (table_cell) -
168 		      fo_area_area_get_space_before (area));
169   fo_area_set_next_x (table_cell,
170 		      fo_area_area_get_border_before (table_cell) +
171 		      fo_area_area_get_padding_before (table_cell));
172   fo_area_set_next_y (table_cell,
173 		      fo_area_get_next_y (table_cell) -
174 		      fo_area_area_get_height (area));
175   fo_area_set_available_height (area,
176 				fo_area_area_get_height (area));
177   fo_area_set_available_width (area,
178 			       fo_area_get_child_available_ipdim (table_cell));
179 }
180 
181 /**
182  * fo_area_table_cell_set_or_split:
183  * @area: #FoArea to be either placed within the parent area or split
184  *        into two areas
185  * @data: Not used
186  *
187  *
188  **/
189 void
fo_area_table_cell_set_or_split(FoArea * area,gpointer data G_GNUC_UNUSED)190 fo_area_table_cell_set_or_split (FoArea  *area,
191 				 gpointer data G_GNUC_UNUSED)
192 {
193   FoArea *table_cell;
194   gdouble table_cell_child_available_bpdim;
195 
196   g_return_if_fail (FO_IS_AREA (area));
197   g_return_if_fail (FO_IS_AREA_TABLE_CELL (fo_area_parent (area)));
198 
199   table_cell = fo_area_parent (area);
200   table_cell_child_available_bpdim = fo_area_get_child_available_bpdim (table_cell);
201 
202   if ((table_cell_child_available_bpdim -
203        (fo_area_get_next_y (table_cell) -
204 	fo_area_area_get_height (area))) >= 0)
205     {
206       fo_area_area_set_x (area,
207 			  fo_area_get_next_x (table_cell) +
208 			  fo_area_area_get_start_indent (area));
209       fo_area_area_set_y (area,
210 			  fo_area_get_next_y (table_cell));
211       fo_area_set_next_x (table_cell,
212 			  fo_area_area_get_border_before (table_cell) +
213 			  fo_area_area_get_padding_before (table_cell));
214       fo_area_set_next_y (table_cell,
215 			  fo_area_get_next_y (table_cell) -
216 			  fo_area_area_get_height (area));
217       fo_area_set_available_height (area,
218 				    fo_area_area_get_height (area));
219       fo_area_set_available_width (area,
220 				   fo_area_get_child_available_ipdim (table_cell));
221     }
222   else
223     {
224 #if defined(LIBFO_DEBUG) && 1
225       g_message ("table_cell_set_or_split:: splitting:: child: %s; generated by: %s",
226 		 fo_object_debug_sprintf (area) ,
227 		 fo_object_debug_sprintf (area->generated_by));
228 #endif
229       area = fo_area_split_before_height (area,
230 					  table_cell_child_available_bpdim -
231 					  fo_area_area_get_height (table_cell));
232       table_cell = fo_area_parent (area);
233       table_cell_child_available_bpdim = fo_area_get_child_available_bpdim (table_cell);
234     }
235 }
236 
237 /**
238  * fo_area_table_cell_size_request:
239  * @child: Child area
240  *
241  * Check that the parent area of @child has sufficient space for
242  * @child.  If not enough space, request that the parent has
243  * sufficient space allocated for it, then adjust @child and its
244  * siblings as necessary to fit into the resized parent area.
245  *
246  * Return value: Pointer to the last area generated from @child after
247  * any reallocation and resizing
248  **/
249 FoArea*
fo_area_table_cell_size_request(FoArea * child)250 fo_area_table_cell_size_request (FoArea *child)
251 {
252   g_return_val_if_fail (child != NULL, NULL);
253   g_return_val_if_fail (FO_IS_AREA_AREA (child), NULL);
254   g_return_val_if_fail (!FO_AREA_IS_ROOT (child), NULL);
255   g_return_val_if_fail (fo_area_parent (child) != NULL, NULL);
256   g_return_val_if_fail (FO_IS_AREA_TABLE_CELL (fo_area_parent (child)), NULL);
257 
258   /* The area that is the return value of this procedure. */
259   FoArea *return_child;
260   /* Used when determining the return value. */
261   FoArea *child_original_next_part = child->next_part;
262 
263   /* The table cell containing the area that issued this request. */
264   FoArea *table_cell = fo_area_parent (child);
265   /* The length available for all children of this table cell. */
266   gdouble table_cell_child_available_bpdim =
267     fo_area_get_child_available_bpdim (table_cell);
268 
269   /* Find the total height of all children. */
270   gdouble total_child_height = 0.0;
271   fo_area_children_foreach (table_cell,
272 			    G_TRAVERSE_ALL,
273 			    &fo_area_accumulate_height,
274 			    &total_child_height);
275 
276   /* Ideally, this table cell would be just big enough for its borders
277      and padding plus the height needed for its children. */
278   gdouble table_cell_target_height =
279     total_child_height +
280     fo_area_area_get_border_before (table_cell) +
281     fo_area_area_get_padding_before (table_cell) +
282     fo_area_area_get_padding_after (table_cell) +
283     fo_area_area_get_border_after (table_cell);
284 
285   /* The FoFo that generated this table cell may have set the allowed
286      height. */
287   FoDatatype *fo_cell_bpdim =
288     fo_property_get_value (fo_table_cell_get_block_progression_dimension (table_cell->generated_by));
289 
290   gdouble table_cell_use_height = 0.0;
291   if (FO_IS_LENGTH_RANGE (fo_cell_bpdim))
292     {
293       FoDatatype *min_datatype =
294 	fo_length_range_get_minimum (fo_cell_bpdim);
295       FoDatatype *opt_datatype =
296 	fo_length_range_get_optimum (fo_cell_bpdim);
297       FoDatatype *max_datatype =
298 	fo_length_range_get_maximum (fo_cell_bpdim);
299 
300       if (FO_IS_LENGTH (min_datatype) &&
301 	  table_cell_target_height <= fo_length_get_value (min_datatype))
302 	{
303 	  table_cell_use_height = fo_length_get_value (min_datatype);
304 
305 #if defined(LIBFO_DEBUG) && 0
306 	  g_message ("table_cell_size_request:: target: %g; min: %g",
307 		     table_cell_target_height,
308 		     fo_length_get_value (min_datatype));
309 #endif
310 	}
311       else if (FO_IS_LENGTH (opt_datatype) &&
312 	       table_cell_target_height < fo_length_get_value (opt_datatype))
313 	{
314 	  table_cell_use_height = fo_length_get_value (opt_datatype);
315 
316 #if defined(LIBFO_DEBUG) && 0
317 	  g_message ("table_cell_size_request:: target: %g; opt: %g",
318 		     table_cell_target_height,
319 		     fo_length_get_value (opt_datatype));
320 #endif
321 	}
322       else if (FO_IS_LENGTH (max_datatype) &&
323 	       fo_length_get_value (max_datatype) < table_cell_target_height)
324 	{
325 	  table_cell_use_height = fo_length_get_value (max_datatype);
326 
327 #if defined(LIBFO_DEBUG) && 0
328 	  g_message ("table_cell_size_request:: target: %g; max: %g",
329 		     table_cell_target_height,
330 		     fo_length_get_value (max_datatype));
331 #endif
332 	}
333       else
334 	{
335 	  table_cell_use_height = table_cell_target_height;
336 	}
337     }
338   else
339     {
340       /* The 'block_progression_dimension' property should only ever
341 	 be a length-range. */
342       g_assert_not_reached ();
343     }
344 
345   /* The available height may be less than what is required (or what
346      is allowed by the FoFo). */
347   if (table_cell_child_available_bpdim < table_cell_use_height)
348     {
349       /* Set the height to what we want, and then let the parent area
350 	 work out what the available height should be. */
351       fo_area_area_set_height (table_cell,
352 			       table_cell_use_height);
353       table_cell = fo_area_size_request (table_cell);
354 
355       /* The row will have set this cell's available height.
356          If there are other cells in the row that need more height,
357          the available height may have been more than requested. */
358       fo_area_area_set_height (table_cell,
359 			       fo_area_get_available_height (table_cell));
360 
361       table_cell_child_available_bpdim =
362 	MAX (fo_area_get_available_height (table_cell) -
363 	     fo_area_area_get_border_before (table_cell) -
364 	     fo_area_area_get_padding_before (table_cell) -
365 	     fo_area_area_get_padding_after (table_cell) -
366 	     fo_area_area_get_border_after (table_cell),
367 	     0);
368       fo_area_set_child_available_bpdim (table_cell,
369 					 table_cell_child_available_bpdim);
370     }
371 
372   /* Work out the total child height again because this area's parent
373      area may have adjusted the children. */
374   total_child_height = 0.0;
375   fo_area_children_foreach (table_cell,
376 			    G_TRAVERSE_ALL,
377 			    &fo_area_accumulate_height,
378 			    &total_child_height);
379 
380   /* Work out the new target height. */
381   table_cell_target_height =
382     total_child_height +
383     fo_area_area_get_border_before (table_cell) +
384     fo_area_area_get_padding_before (table_cell) +
385     fo_area_area_get_padding_after (table_cell) +
386     fo_area_area_get_border_after (table_cell);
387 
388   /* In the absence of 'display-align', the first child is placed just
389      inside the borders and padding of this table cell. */
390   fo_area_set_next_x (table_cell,
391 		      fo_area_area_get_border_start (table_cell) +
392 		      fo_area_area_get_padding_start (table_cell));
393   fo_area_set_next_y (table_cell,
394 		      - (fo_area_area_get_border_before (table_cell) +
395 			 fo_area_area_get_padding_before (table_cell)));
396 
397   if (table_cell_target_height <= table_cell_child_available_bpdim)
398     {
399       FoDatatype *fo_cell_display_align =
400 	fo_property_get_value (fo_table_cell_get_display_align (table_cell->generated_by));
401 
402       FoEnumEnum display_align =
403 	fo_enum_get_value (fo_cell_display_align);
404 
405       gdouble next_y;
406       switch (display_align)
407 	{
408 	case FO_ENUM_ENUM_AUTO:
409 	  /* Treat as 'before' while 'relative-align' is
410 	     unimplemented. */
411 	case FO_ENUM_ENUM_BEFORE:
412 	  next_y =
413 	    - (fo_area_area_get_border_before (table_cell) +
414 	       fo_area_area_get_padding_before (table_cell));
415 	  break;
416 	case FO_ENUM_ENUM_CENTER:
417 	  next_y =
418 	    - (fo_area_area_get_border_before (table_cell) +
419 	       fo_area_area_get_padding_before (table_cell) +
420 	       (table_cell_child_available_bpdim -
421 		total_child_height) / 2.0);
422 	  break;
423 	case FO_ENUM_ENUM_AFTER:
424 	  next_y =
425 	    - (fo_area_area_get_border_before (table_cell) +
426 	       fo_area_area_get_padding_before (table_cell) +
427 	       (table_cell_child_available_bpdim -
428 		total_child_height));
429 	  break;
430 	default:
431 	  g_assert_not_reached ();
432 	}
433       fo_area_set_next_y (table_cell,
434 			  next_y);
435       /* Since the children all fit within the allowed height, adjust
436 	 their sizes and positions in sequence. */
437       fo_area_children_foreach (table_cell,
438 				G_TRAVERSE_ALL,
439 				&fo_area_table_cell_size_adjust,
440 				NULL);
441     }
442   else
443     {
444       /* Since the children don't all fit, place the ones that fit in
445 	 the available height, and split at or before the allowed
446 	 height. */
447       fo_area_children_foreach (table_cell,
448 				G_TRAVERSE_ALL,
449 				&fo_area_table_cell_set_or_split,
450 				NULL);
451 #if defined(LIBFO_DEBUG) && 0
452       g_message ("table_cell_size_request (%p):: total > available:: return:: table_cell->last: %s; generated by: %s",
453 		 child,
454 		 fo_object_debug_sprintf (fo_area_last_child (table_cell)),
455 		 fo_object_debug_sprintf (fo_area_last_child (table_cell)->generated_by));
456 #endif
457     }
458 
459   /* The result will be the current child unless the child has been
460      split, in which case the result is the portion after the last
461      split. */
462   return_child = child;
463 
464   while ((return_child->next_part != NULL) &&
465 	 (return_child->next_part != child_original_next_part))
466     {
467       return_child = return_child->next_part;
468     }
469 
470   return return_child;
471 }
472 
473 /* return the new area containing what comes after the split */
474 /* leave @area as area remaining after split */
475 FoArea*
fo_area_table_cell_split_before_height(FoArea * area,gdouble max_height)476 fo_area_table_cell_split_before_height (FoArea *area,
477 					gdouble max_height)
478 {
479   FoArea *use_child_area;
480   gdouble minus_child_y = 0.0;
481   gdouble child_height = 0.0;
482 
483   g_return_val_if_fail (FO_IS_AREA_TABLE_CELL (area), NULL);
484   g_return_val_if_fail (fo_area_n_children (area) > 0, NULL);
485   g_return_val_if_fail (max_height > 0, NULL);
486 
487   /* if the current area is less than max height, then no new area */
488   if (fo_area_area_get_height (area) < max_height)
489     return NULL;
490 
491   use_child_area = fo_area_first_child (area);
492 
493   while (use_child_area)
494     {
495       minus_child_y = -fo_area_area_get_y (use_child_area);
496       child_height = fo_area_area_get_height (use_child_area);
497 
498       if (minus_child_y + child_height >= max_height)
499 	break;
500       else
501        use_child_area = fo_area_next_sibling (use_child_area);
502     }
503 
504 #if defined(LIBFO_DEBUG) && 1
505   g_message ("table_cell_split_before_height:: splitting: area: %s; generated by: %s; y: %f; height: %f",
506 	     fo_object_debug_sprintf (use_child_area),
507 	     fo_object_debug_sprintf (fo_area_get_generated_by (use_child_area)),
508 	     fo_area_area_get_y (use_child_area),
509 	     fo_area_area_get_height (use_child_area));
510 #endif
511 
512   if (use_child_area == NULL)
513     return NULL;
514 
515   if (minus_child_y >= max_height)
516     {
517       /* max_height falls before use_child_area, i.e. in space between areas */
518 
519       if (use_child_area == fo_area_first_child (area))
520 	{
521 	  return NULL;
522 	}
523       else
524 	{
525 	  FoFo *child_fo =
526 	    fo_area_get_generated_by (use_child_area);
527 	  FoDatatype *child_kwpwp_datatype =
528 	    fo_keep_get_within_page (fo_property_get_value (fo_block_fo_get_keep_with_previous (child_fo)));
529 	  FoDatatype *child_kwpwc_datatype =
530 	    fo_keep_get_within_column (fo_property_get_value (fo_block_fo_get_keep_with_previous (child_fo)));
531 
532 	  FoFo *prev_child_fo =
533 	    fo_area_get_generated_by (fo_area_prev_sibling (use_child_area));
534 	  FoDatatype *prev_child_kwnwp_datatype =
535 	    fo_keep_get_within_page (fo_property_get_value (fo_block_fo_get_keep_with_next (prev_child_fo)));
536 	  FoDatatype *prev_child_kwnwc_datatype =
537 	    fo_keep_get_within_column (fo_property_get_value (fo_block_fo_get_keep_with_next (prev_child_fo)));
538 
539 	  /* FIXME: Need to handle integer keeps */
540 	  if ((FO_IS_ENUM (prev_child_kwnwp_datatype) &&
541 	       fo_enum_get_value (prev_child_kwnwp_datatype) == FO_ENUM_ENUM_AUTO) &&
542 	      (FO_IS_ENUM (prev_child_kwnwc_datatype) &&
543 	       fo_enum_get_value (prev_child_kwnwc_datatype) == FO_ENUM_ENUM_AUTO)  &&
544 	      (FO_IS_ENUM (child_kwpwp_datatype) &&
545 	       fo_enum_get_value (child_kwpwp_datatype) == FO_ENUM_ENUM_AUTO) &&
546 	      (FO_IS_ENUM (child_kwpwc_datatype) &&
547 	       fo_enum_get_value (child_kwpwc_datatype) == FO_ENUM_ENUM_AUTO))
548 	    {
549 	      /* If got to here, all relevant keeps are 'auto' */
550 	      FoArea *clone = fo_area_clone (area);
551 
552 	      fo_area_unlink_with_next_siblings (use_child_area);
553 	      fo_area_insert_with_next_siblings (clone, 0, use_child_area);
554 
555 	      return clone;
556 	    }
557 	  else
558 	    {
559 	      gdouble minus_prev_y =
560 		fo_area_area_get_y (fo_area_prev_sibling (use_child_area));
561 	      gdouble prev_height =
562 		fo_area_area_get_height (fo_area_prev_sibling (use_child_area));
563 	      /* If can't split between use_child_area and previous, maybe
564 		 can split at lower height */
565 	      return fo_area_table_cell_split_before_height (area,
566 							     minus_prev_y +
567 							     prev_height);
568 	    }
569 	}
570     }
571   else
572     {
573       /* max_height falls within use_child_area */
574       gboolean child_can_split = fo_area_split_before_height_check (use_child_area,
575 								    max_height -
576 								    minus_child_y);
577 
578       if (child_can_split)
579 	{
580 	  FoArea *clone = fo_area_clone (area);
581 	  FoArea *split_child = fo_area_split_before_height (use_child_area,
582 							     max_height -
583 							     minus_child_y);
584 	  fo_area_unlink_with_next_siblings (split_child);
585 	  fo_area_insert_with_next_siblings (clone, 0, split_child);
586 
587 	  return clone;
588 	}
589       else
590 	{
591 	  /* If can't split use_child_area, maybe
592 	     can split at lower height */
593 	  return fo_area_table_cell_split_before_height (area,
594 							 minus_child_y);
595 	}
596     }
597 }
598 
599 /* return the new area containing what comes after the split */
600 /* leave @area as area remaining after split */
601 gboolean
fo_area_table_cell_split_before_height_check(FoArea * area,gdouble max_height)602 fo_area_table_cell_split_before_height_check (FoArea *area,
603 					     gdouble max_height)
604 {
605   FoArea *use_child_area;
606   gdouble minus_child_y = 0.0;
607   gdouble child_height = 0.0;
608 
609   g_return_val_if_fail (FO_IS_AREA_TABLE_CELL (area), FALSE);
610   g_return_val_if_fail (fo_area_n_children (area) > 0, FALSE);
611   /* FIXME: Getting max_height value of '-0'. */
612   g_return_val_if_fail (max_height >= 0, FALSE);
613 
614   /* if the current area is less than max height, then no new area */
615   if (fo_area_area_get_height (area) < max_height)
616     return FALSE;
617 
618   use_child_area = fo_area_first_child (area);
619 
620   while (use_child_area)
621     {
622       minus_child_y = -fo_area_area_get_y (use_child_area);
623       child_height = fo_area_area_get_height (use_child_area);
624 
625       if (minus_child_y + child_height >= max_height)
626 	break;
627       else
628        use_child_area = fo_area_next_sibling (use_child_area);
629     }
630 
631 #if defined(LIBFO_DEBUG) && 1
632   g_message ("table_cell_split_before_height_check:: splitting: area: %s; generated by: %s; y: %f; height: %f",
633 	     fo_object_debug_sprintf (use_child_area),
634 	     fo_object_debug_sprintf (fo_area_get_generated_by (use_child_area)),
635 	     fo_area_area_get_y (use_child_area),
636 	     fo_area_area_get_height (use_child_area));
637 #endif
638 
639   if (use_child_area == NULL)
640     return FALSE;
641 
642   if (minus_child_y >= max_height)
643     {
644       /* max_height falls before use_child_area, i.e. in space between areas */
645 
646       if (use_child_area == fo_area_first_child (area))
647 	{
648 	  return FALSE;
649 	}
650       else
651 	{
652 	  FoFo *child_fo =
653 	    fo_area_get_generated_by (use_child_area);
654 	  FoDatatype *child_kwpwp_datatype =
655 	    fo_keep_get_within_page (fo_property_get_value (fo_block_fo_get_keep_with_previous (child_fo)));
656 	  FoDatatype *child_kwpwc_datatype =
657 	    fo_keep_get_within_column (fo_property_get_value (fo_block_fo_get_keep_with_previous (child_fo)));
658 
659 	  FoFo *prev_child_fo =
660 	    fo_area_get_generated_by (fo_area_prev_sibling (use_child_area));
661 	  FoDatatype *prev_child_kwnwp_datatype =
662 	    fo_keep_get_within_page (fo_property_get_value (fo_block_fo_get_keep_with_next (prev_child_fo)));
663 	  FoDatatype *prev_child_kwnwc_datatype =
664 	    fo_keep_get_within_column (fo_property_get_value (fo_block_fo_get_keep_with_next (prev_child_fo)));
665 
666 	  /* FIXME: Need to handle integer keeps */
667 	  if ((FO_IS_ENUM (prev_child_kwnwp_datatype) &&
668 	       fo_enum_get_value (prev_child_kwnwp_datatype) == FO_ENUM_ENUM_AUTO) &&
669 	      (FO_IS_ENUM (prev_child_kwnwc_datatype) &&
670 	       fo_enum_get_value (prev_child_kwnwc_datatype) == FO_ENUM_ENUM_AUTO)  &&
671 	      (FO_IS_ENUM (child_kwpwp_datatype) &&
672 	       fo_enum_get_value (child_kwpwp_datatype) == FO_ENUM_ENUM_AUTO) &&
673 	      (FO_IS_ENUM (child_kwpwc_datatype) &&
674 	       fo_enum_get_value (child_kwpwc_datatype) == FO_ENUM_ENUM_AUTO))
675 	    {
676 	      /* If got to here, all relevant keeps are 'auto' */
677 	      return TRUE;
678 	    }
679 	  else
680 	    {
681 	      gdouble minus_prev_y =
682 		fo_area_area_get_y (fo_area_prev_sibling (use_child_area));
683 	      gdouble prev_height =
684 		fo_area_area_get_height (fo_area_prev_sibling (use_child_area));
685 	      /* If can't split between use_child_area and previous, maybe
686 		 can split at lower height */
687 	      return fo_area_table_cell_split_before_height_check (area,
688 								   minus_prev_y +
689 								   prev_height);
690 	    }
691 	}
692     }
693   else
694     {
695       /* max_height falls within use_child_area */
696       gboolean child_can_split = fo_area_split_before_height_check (use_child_area,
697 							      max_height -
698 							      minus_child_y);
699 
700       if (child_can_split)
701 	{
702 	  return TRUE;
703 	}
704       else
705 	{
706 	  /* If can't split use_child_area, maybe
707 	     can split at lower height */
708 	  return fo_area_table_cell_split_before_height_check (area,
709 							       minus_child_y);
710 	}
711     }
712 }
713 
714 /**
715  * fo_area_table_cell_update_after_clone:
716  * @clone:    New object cloned from @original
717  * @original: Original area object
718  *
719  * Update the #FoAreaTableCell-specific instance variables of @clone to
720  * match those of @original.
721  **/
722 void
fo_area_table_cell_update_after_clone(FoArea * clone,FoArea * original)723 fo_area_table_cell_update_after_clone (FoArea *clone,
724 				       FoArea *original)
725 {
726   FO_AREA_CLASS (parent_class)->update_after_clone (clone, original);
727 
728   fo_area_set_child_available_ipdim (clone,
729 				     fo_area_get_child_available_ipdim (original));
730   fo_area_area_set_width (clone,
731 			  fo_area_area_get_width (original));
732 
733 }
734