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