1 /* Fo
2  * fo-area-table-cell-proxy.c: Proxy area object for table-cell
3  *                               formatting objects that span rows
4  *
5  * Copyright (C) 2001 Sun Microsystems
6  * Copyright (C) 2007-2009 Menteith Consulting Ltd
7  * Copyright (C) 2011-2012 Mentea
8  *
9  * See COPYING for the status of this software.
10  */
11 
12 #include "fo-utils.h"
13 #include "fo-area.h"
14 #include "fo-area-private.h"
15 #include "fo-area-area.h"
16 #include "fo-area-area-private.h"
17 #include "fo-area-table-cell-proxy.h"
18 #include "fo-area-spanning-table-cell.h"
19 #include "fo/fo-table-cell.h"
20 #include "property/fo-property-block-progression-dimension.h"
21 
22 struct _FoAreaTableCellProxy
23 {
24   FoAreaArea parent_instance;
25 
26   FoArea *table_cell;
27   gint    row_number;
28   gdouble  min_height;
29 };
30 
31 struct _FoAreaTableCellProxyClass
32 {
33   FoAreaAreaClass parent_class;
34 };
35 
36 static void fo_area_table_cell_proxy_class_init  (FoAreaTableCellProxyClass *klass);
37 static void fo_area_table_cell_proxy_finalize    (GObject           *object);
38 
39 static void fo_area_table_cell_proxy_debug_dump_properties (FoArea *area,
40 							      gint depth);
41 static FoArea* fo_area_table_cell_proxy_size_request (FoArea *child);
42 static FoArea* fo_area_table_cell_proxy_split_before_height (FoArea *area,
43 						      gdouble max_height);
44 static gboolean fo_area_table_cell_proxy_split_before_height_check (FoArea *area,
45 							     gdouble max_height);
46 
47 static gpointer parent_class;
48 
49 /**
50  * fo_area_table_cell_proxy_get_type:
51  * @void:
52  *
53  * Register the FoTableCellProxy object type.
54  *
55  * Return value: GType value of the FoTableCellProxy object type.
56  **/
57 GType
fo_area_table_cell_proxy_get_type(void)58 fo_area_table_cell_proxy_get_type (void)
59 {
60   static GType object_type = 0;
61 
62   if (!object_type)
63     {
64       static const GTypeInfo object_info =
65       {
66         sizeof (FoAreaTableCellProxyClass),
67         (GBaseInitFunc) NULL,
68         (GBaseFinalizeFunc) NULL,
69         (GClassInitFunc) fo_area_table_cell_proxy_class_init,
70         NULL,           /* class_finalize */
71         NULL,           /* class_data */
72         sizeof (FoAreaTableCellProxy),
73         0,              /* n_preallocs */
74         NULL,		/* instance_init */
75 	NULL		/* value_table */
76       };
77 
78       object_type = g_type_register_static (FO_TYPE_AREA_AREA,
79                                             "FoAreaTableCellProxy",
80                                             &object_info, 0);
81     }
82 
83   return object_type;
84 }
85 
86 /**
87  * fo_area_table_cell_proxy_class_init:
88  * @klass: FoTableCellProxyClass object to initialise
89  *
90  * Implements GClassInitFunc for FoTableCellProxyClass
91  **/
92 void
fo_area_table_cell_proxy_class_init(FoAreaTableCellProxyClass * klass)93 fo_area_table_cell_proxy_class_init (FoAreaTableCellProxyClass *klass)
94 {
95   GObjectClass *object_class = G_OBJECT_CLASS (klass);
96   FoAreaClass *fo_area_class = FO_AREA_CLASS (klass);
97 
98   parent_class = g_type_class_peek_parent (klass);
99 
100   object_class->finalize = fo_area_table_cell_proxy_finalize;
101 
102   fo_area_class->debug_dump_properties = fo_area_table_cell_proxy_debug_dump_properties;
103   fo_area_class->size_request = fo_area_table_cell_proxy_size_request;
104   fo_area_class->split_before_height = fo_area_table_cell_proxy_split_before_height;
105   fo_area_class->split_before_height_check = fo_area_table_cell_proxy_split_before_height_check;
106 }
107 
108 /**
109  * fo_area_table_cell_proxy_finalize:
110  * @object: FoTableCellProxy object to finalize
111  *
112  * Implements GObjectFinalizeFunc for FoTableCellProxy
113  **/
114 void
fo_area_table_cell_proxy_finalize(GObject * object)115 fo_area_table_cell_proxy_finalize (GObject *object)
116 {
117   FoAreaTableCellProxy *fo_area_table_cell_proxy;
118 
119   fo_area_table_cell_proxy = FO_AREA_TABLE_CELL_PROXY (object);
120 
121   G_OBJECT_CLASS (parent_class)->finalize (object);
122 }
123 
124 
125 /**
126  * fo_area_table_cell_proxy_new:
127  *
128  * Creates a new #FoAreaTableCellProxy initialized to default value.
129  *
130  * Return value: the new #FoAreaTableCellProxy
131  **/
132 FoArea*
fo_area_table_cell_proxy_new(void)133 fo_area_table_cell_proxy_new (void)
134 {
135   return FO_AREA (g_object_new (fo_area_table_cell_proxy_get_type (), NULL));
136 }
137 
138 /**
139  * fo_area_table_cell_proxy_set_table_cell:
140  * @fo_area:        The #FoAreaTableCellProxy object
141  * @new_table_cell: The new "row-number" property value
142  *
143  * Sets the #line-last property of @area_area to @new_table_cell
144 **/
145 void
fo_area_table_cell_proxy_set_table_cell(FoArea * fo_area,FoArea * new_table_cell)146 fo_area_table_cell_proxy_set_table_cell (FoArea *fo_area,
147 					 FoArea *new_table_cell)
148 {
149   g_return_if_fail (fo_area != NULL);
150   g_return_if_fail (FO_IS_AREA_TABLE_CELL_PROXY (fo_area));
151   g_return_if_fail (FO_IS_AREA_SPANNING_TABLE_CELL (new_table_cell));
152 
153   FO_AREA_TABLE_CELL_PROXY (fo_area)->table_cell = new_table_cell;
154   /* g_object_notify (G_OBJECT (fo_area), "table_cell");*/
155 }
156 
157 /**
158  * fo_area_table_cell_proxy_get_table_cell:
159  * @fo_area: The #FoAreaTableCellProxy object
160  *
161  * Gets the 'row-number' property of @area_table_cell_proxy
162  *
163  * Return value: The "row-number" property value
164 **/
165 FoArea*
fo_area_table_cell_proxy_get_table_cell(FoArea * fo_area)166 fo_area_table_cell_proxy_get_table_cell (FoArea *fo_area)
167 {
168   g_return_val_if_fail (fo_area != NULL, 0);
169   g_return_val_if_fail (FO_IS_AREA_TABLE_CELL_PROXY (fo_area), 0);
170 
171   return FO_AREA_TABLE_CELL_PROXY (fo_area)->table_cell;
172 }
173 
174 /**
175  * fo_area_table_cell_proxy_set_row_number:
176  * @fo_area:        The #FoAreaTableCellProxy object
177  * @new_row_number: The new "row-number" property value
178  *
179  * Sets the "row-number" property of @fo_area to @new_row_number
180 **/
181 void
fo_area_table_cell_proxy_set_row_number(FoArea * fo_area,gint new_row_number)182 fo_area_table_cell_proxy_set_row_number (FoArea *fo_area,
183 					 gint new_row_number)
184 {
185   g_return_if_fail (fo_area != NULL);
186   g_return_if_fail (FO_IS_AREA_TABLE_CELL_PROXY (fo_area));
187 
188   FO_AREA_TABLE_CELL_PROXY (fo_area)->row_number = new_row_number;
189   /* g_object_notify (G_OBJECT (fo_area), "row-number");*/
190 }
191 
192 /**
193  * fo_area_table_cell_proxy_get_row_number:
194  * @fo_area: The #FoAreaTableCellProxy object
195  *
196  * Gets the 'row-number' property of @area_table_cell_proxy
197  *
198  * Return value: The "row-number" property value
199 **/
200 gint
fo_area_table_cell_proxy_get_row_number(FoArea * fo_area)201 fo_area_table_cell_proxy_get_row_number (FoArea *fo_area)
202 {
203   g_return_val_if_fail (fo_area != NULL, 0);
204   g_return_val_if_fail (FO_IS_AREA_TABLE_CELL_PROXY (fo_area), 0);
205 
206   return FO_AREA_TABLE_CELL_PROXY (fo_area)->row_number;
207 }
208 
209 /**
210  * fo_area_table_cell_proxy_set_min_height:
211  * @fo_area:        The #FoAreaTableCellProxy object
212  * @new_min_height: The new "min-height" property value
213  *
214  * Sets the "min-height" property of @fo_area to @new_min_height
215 **/
216 void
fo_area_table_cell_proxy_set_min_height(FoArea * fo_area,gdouble new_min_height)217 fo_area_table_cell_proxy_set_min_height (FoArea *fo_area,
218 					 gdouble new_min_height)
219 {
220   g_return_if_fail (fo_area != NULL);
221   g_return_if_fail (FO_IS_AREA_TABLE_CELL_PROXY (fo_area));
222 
223   FO_AREA_TABLE_CELL_PROXY (fo_area)->min_height = new_min_height;
224   /* g_object_notify (G_OBJECT (fo_area), "min-height");*/
225 }
226 
227 /**
228  * fo_area_table_cell_proxy_get_min_height:
229  * @fo_area: The #FoAreaTableCellProxy object
230  *
231  * Gets the 'min-height' property of @fo_area
232  *
233  * Return value: The "min-height" property value
234 **/
235 gdouble
fo_area_table_cell_proxy_get_min_height(FoArea * fo_area)236 fo_area_table_cell_proxy_get_min_height (FoArea *fo_area)
237 {
238   g_return_val_if_fail (fo_area != NULL, 0);
239   g_return_val_if_fail (FO_IS_AREA_TABLE_CELL_PROXY (fo_area), 0);
240 
241   return FO_AREA_TABLE_CELL_PROXY (fo_area)->min_height;
242 }
243 
244 /**
245  * fo_area_table_cell_proxy_debug_dump_properties:
246  * @area:  The #FoArea object
247  * @depth: Indent level to add to the output
248  *
249  * Logs the value of each significant property of @area then calls
250  * debug_dump_properties method of parent class.
251  **/
252 void
fo_area_table_cell_proxy_debug_dump_properties(FoArea * area,gint depth)253 fo_area_table_cell_proxy_debug_dump_properties (FoArea *area, gint depth)
254 {
255   FoAreaTableCellProxy *table_cell_proxy;
256   gchar *indent = g_strnfill (depth * 2, ' ');
257   gchar *string;
258 
259   g_return_if_fail (area != NULL);
260   g_return_if_fail (FO_IS_AREA_TABLE_CELL_PROXY (area));
261 
262   table_cell_proxy = FO_AREA_TABLE_CELL_PROXY (area);
263 
264   string = fo_object_debug_sprintf (table_cell_proxy->table_cell);
265   g_log (G_LOG_DOMAIN,
266 	 G_LOG_LEVEL_DEBUG,
267 	 "%stable-cell area: %s",
268 	 indent,
269 	 string);
270   g_free (string);
271 
272   g_log (G_LOG_DOMAIN,
273 	 G_LOG_LEVEL_DEBUG,
274 	 "%srow-number:      %d",
275 	 indent,
276 	 table_cell_proxy->row_number);
277 
278   g_free (indent);
279   FO_AREA_CLASS (parent_class)->debug_dump_properties (area, depth + 1);
280 }
281 
282 /**
283  * fo_area_table_cell_proxy_size_request:
284  * @child: Child area
285  *
286  * Check that the parent area of @child has sufficient space for
287  * @child.  If not enough space, request that the parent has
288  * sufficient space allocated for it, then adjust @child and its
289  * siblings as necessary to fit into the resized parent area.
290  *
291  * Return value: Pointer to the last area generated from @child after
292  * any reallocation and resizing
293  **/
294 FoArea*
fo_area_table_cell_proxy_size_request(FoArea * child)295 fo_area_table_cell_proxy_size_request (FoArea *child)
296 {
297   FoArea *use_child_area;
298   FoArea *parent;
299   FoDatatype *fo_row_bpdim;
300   gdouble max_child_height = 0;
301   gdouble parent_child_available_bpdim;
302   gdouble parent_use_height = 0;
303   gdouble child_height;
304 
305   g_return_val_if_fail (child != NULL, NULL);
306   g_return_val_if_fail (FO_IS_AREA_AREA (child), NULL);
307   g_return_val_if_fail (!FO_AREA_IS_ROOT (child), NULL);
308   g_return_val_if_fail (fo_area_parent (child) != NULL, NULL);
309   g_return_val_if_fail (FO_IS_AREA_TABLE_CELL_PROXY (fo_area_parent (child)), NULL);
310 
311   child_height = fo_area_area_get_height (child);
312 
313   parent = fo_area_parent (child);
314   parent_child_available_bpdim = fo_area_get_child_available_bpdim (parent);
315 
316   if (child_height <= parent_child_available_bpdim)
317     {
318       fo_area_set_available_height (child, parent_child_available_bpdim);
319       return child;
320     }
321 
322   use_child_area = fo_area_first_child (parent);
323 
324   while (use_child_area)
325     {
326       max_child_height =
327 	MAX (max_child_height,
328 	     fo_area_area_get_height (use_child_area));
329       use_child_area = fo_area_next_sibling (use_child_area);
330     }
331 
332   fo_row_bpdim =
333     fo_property_get_value (fo_table_cell_get_block_progression_dimension (parent->generated_by));
334 
335   if (FO_IS_LENGTH_RANGE (fo_row_bpdim))
336     {
337       FoDatatype *min_datatype = fo_length_range_get_minimum (fo_row_bpdim);
338       FoDatatype *opt_datatype = fo_length_range_get_optimum (fo_row_bpdim);
339       FoDatatype *max_datatype = fo_length_range_get_maximum (fo_row_bpdim);
340 
341       if (FO_IS_LENGTH (min_datatype) &&
342 	  max_child_height <= fo_length_get_value (min_datatype))
343 	{
344 	  parent_use_height = fo_length_get_value (min_datatype);
345 
346 #if defined(LIBFO_DEBUG) && 0
347 	  g_message ("table_cell_proxy_size_request:: child: %g; min: %g",
348 		     max_child_height,
349 		     fo_length_get_value (min_datatype));
350 #endif
351 	}
352       else if (FO_IS_LENGTH (opt_datatype) &&
353 	  fo_length_get_value (opt_datatype) < max_child_height)
354 	{
355 	  parent_use_height = fo_length_get_value (opt_datatype);
356 
357 #if defined(LIBFO_DEBUG) && 0
358 	  g_message ("table_cell_proxy_size_request:: child: %g; opt: %g",
359 		     max_child_height,
360 		     fo_length_get_value (opt_datatype));
361 #endif
362 	}
363       else if (FO_IS_LENGTH (max_datatype) &&
364 	  fo_length_get_value (max_datatype) < max_child_height)
365 	{
366 	  parent_use_height = fo_length_get_value (max_datatype);
367 
368 #if defined(LIBFO_DEBUG) && 0
369 	  g_message ("table_cell_proxy_size_request:: child: %g; max: %g",
370 		     max_child_height,
371 		     fo_length_get_value (max_datatype));
372 #endif
373 	}
374       else
375 	{
376 	  parent_use_height = max_child_height;
377 	}
378     }
379   else
380     {
381       g_assert_not_reached ();
382     }
383 
384   if (parent_child_available_bpdim < parent_use_height)
385     {
386       fo_area_area_set_height (parent, parent_use_height);
387       parent = fo_area_size_request (parent);
388       parent_child_available_bpdim = fo_area_get_child_available_bpdim (parent);
389     }
390 
391   use_child_area = fo_area_first_child (parent);
392   max_child_height = 0;
393 
394   while (use_child_area)
395     {
396       max_child_height =
397 	MAX (max_child_height,
398 	     fo_area_area_get_height (use_child_area));
399       use_child_area = fo_area_next_sibling (use_child_area);
400     }
401 
402   if (max_child_height <= parent_child_available_bpdim)
403     {
404       use_child_area = fo_area_first_child (parent);
405 
406       while (use_child_area)
407 	{
408 	  fo_area_set_available_height (use_child_area,
409 					parent_child_available_bpdim);
410 	  use_child_area = fo_area_next_sibling (use_child_area);
411 	}
412 
413       /* FIXME: Should this be last area in FO's glist of areas?
414 	 Currently is *not* the last area of child unless child is
415 	 the last child of parent (which is only true when child
416 	 appended to parent). */
417       return fo_area_last_child (parent);
418     }
419   else
420     {
421       use_child_area = fo_area_first_child (parent);
422 
423       while (use_child_area)
424 	{
425 	  if (fo_area_area_get_height (use_child_area) <=
426 	      parent_child_available_bpdim)
427 	    {
428 	      fo_area_set_available_height (use_child_area,
429 					    parent_child_available_bpdim);
430 
431 	      use_child_area = fo_area_next_sibling (use_child_area);
432 	    }
433 	  else
434 	    {
435 	      use_child_area =
436 		fo_area_split_before_height (use_child_area,
437 					     parent_child_available_bpdim);
438 	      parent = fo_area_parent (use_child_area);
439 	      parent_child_available_bpdim = fo_area_get_child_available_bpdim (parent);
440 
441 	      /* Don't change use_child_area since need to test
442 		 again against parent_child_available_bpdim. */
443 	    }
444 	}
445       return fo_area_last_child (parent);
446     }
447 }
448 
449 /* return the new area containing what comes after the split */
450 /* leave @area as area remaining after split */
451 FoArea*
fo_area_table_cell_proxy_split_before_height(FoArea * area,gdouble max_height)452 fo_area_table_cell_proxy_split_before_height (FoArea *area,
453 				       gdouble max_height)
454 {
455   FoArea *use_child_area;
456   gboolean can_split = TRUE;
457 
458   g_return_val_if_fail (FO_IS_AREA_TABLE_CELL_PROXY (area), NULL);
459   g_return_val_if_fail (fo_area_n_children (area) > 0, NULL);
460   g_return_val_if_fail (max_height > 0, NULL);
461 
462   use_child_area = fo_area_first_child (area);
463 
464   while (use_child_area && can_split)
465     {
466       gdouble child_height = fo_area_area_get_height (use_child_area);
467 
468       can_split &= ((child_height <= max_height) ||
469 		    fo_area_split_before_height_check (use_child_area,
470 						       max_height));
471 
472       use_child_area = fo_area_next_sibling (use_child_area);
473     }
474 
475   if (can_split)
476     {
477       FoArea *split_child;
478       FoArea *clone = fo_area_clone (area);
479       gdouble max_remaining_child_height = 0;
480       gdouble max_split_child_height = 0;
481 
482       area->is_last = FALSE;
483 
484       clone->is_first = FALSE;
485       ((FoAreaArea *)clone)->border_before = 0;
486 
487       use_child_area = fo_area_first_child (area);
488 
489       while (use_child_area)
490 	{
491 	  split_child = fo_area_split_before_height (use_child_area,
492 						     max_height);
493 
494 	  fo_area_unlink (split_child);
495 	  fo_area_append (clone, split_child);
496 
497 	  max_remaining_child_height =
498 	    MAX (max_remaining_child_height,
499 		 fo_area_area_get_height (use_child_area));
500 	  max_split_child_height =
501 	    MAX (max_split_child_height,
502 		 fo_area_area_get_height (split_child));
503 
504 	  use_child_area = fo_area_next_sibling (use_child_area);
505 	}
506 
507       fo_area_area_set_height (area, max_remaining_child_height);
508       fo_area_area_set_height (clone, max_split_child_height);
509 
510       /*
511       fo_area_size_request (area);
512       fo_area_size_request (clone);
513       */
514 
515       return clone;
516     }
517   else
518     {
519       return NULL;
520     }
521 }
522 /* return the new area containing what comes after the split */
523 /* leave @area as area remaining after split */
524 gboolean
fo_area_table_cell_proxy_split_before_height_check(FoArea * area,gdouble max_height)525 fo_area_table_cell_proxy_split_before_height_check (FoArea *area,
526 					     gdouble max_height)
527 {
528   FoArea *use_child_area;
529   gboolean can_split = TRUE;
530 
531   g_return_val_if_fail (FO_IS_AREA_TABLE_CELL_PROXY (area), FALSE);
532   g_return_val_if_fail (fo_area_n_children (area) > 0, FALSE);
533   g_return_val_if_fail (max_height > 0, FALSE);
534 
535   use_child_area = fo_area_first_child (area);
536 
537   while (use_child_area && can_split)
538     {
539       gdouble child_height = fo_area_area_get_height (use_child_area);
540 
541       can_split &= ((child_height <= max_height) ||
542 		    fo_area_split_before_height_check (use_child_area,
543 						       max_height));
544 
545       use_child_area = fo_area_next_sibling (use_child_area);
546     }
547 
548   return can_split;
549 }
550