cpml: protect CpmlPair methods against NULL args
[adg.git] / src / adg / adg-table-row.c
blobd06033df0309a553c00e4ebf0e1f7616da32f9fb
1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010,2011,2012,2013 Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 /**
22 * SECTION:adg-table-row
23 * @Section_Id:AdgTableRow
24 * @title: AdgTableRow
25 * @short_description: A boxed type representing a table row
27 * The #AdgTableRow is a boxed type containing a single row of cells
28 * of an #AdgTable object.
30 * Every row is segmented into different cells. It must be populated
31 * by using the #AdgCell APIs, such as adg_table_cell_new() or
32 * adg_table_cell_new_before().
34 * Since: 1.0
35 **/
37 /**
38 * AdgTableRow:
40 * An opaque structure referring to a row of an #AdgTable. Any
41 * table can have an unlimited number of rows.
43 * Since: 1.0
44 **/
47 #include "adg-internal.h"
49 #include "adg-style.h"
50 #include "adg-table-style.h"
52 #include "adg-table.h"
53 #include "adg-table-row.h"
54 #include "adg-table-cell.h"
57 struct _AdgTableRow {
58 AdgTable *table;
59 GSList *cells;
60 gdouble height;
61 CpmlExtents extents;
65 static AdgTableRow * _adg_row_new (AdgTable *table);
68 GType
69 adg_table_row_get_type(void)
71 static GType row_type = 0;
73 if (G_UNLIKELY(row_type == 0))
74 row_type = g_boxed_type_register_static("AdgTableRow",
75 (GBoxedCopyFunc) adg_table_row_dup,
76 (GBoxedFreeFunc) adg_table_row_free);
78 return row_type;
82 /**
83 * adg_table_row_dup:
84 * @table_row: an #AdgTableRow structure
86 * Duplicates @table_row. The returned duplicate should be freed
87 * with adg_table_row_free() when no longer needed.
89 * Returns: (transfer full): a duplicate of @table_row.
91 * Since: 1.0
92 **/
93 AdgTableRow *
94 adg_table_row_dup(const AdgTableRow *table_row)
96 return g_memdup(table_row, sizeof(AdgTableRow));
99 /**
100 * adg_table_row_new:
101 * @table: an #AdgTable
103 * Creates a new empty row and appends it at the end of the rows
104 * yet present in @table. By default, the height of this new
105 * row will be the fallback value provided by the table style:
106 * you can override it by using adg_table_row_set_height().
108 * Returns: (transfer full): the newly created row or %NULL on errors.
110 * Since: 1.0
112 AdgTableRow *
113 adg_table_row_new(AdgTable *table)
115 AdgTableRow *table_row;
117 g_return_val_if_fail(ADG_IS_TABLE(table), NULL);
119 table_row = _adg_row_new(table);
120 adg_table_insert(table, table_row, NULL);
121 adg_entity_invalidate((AdgEntity *) table);
123 return table_row;
127 * adg_table_row_new_before:
128 * @before_row: a valid #AdgTableRow
130 * Creates a new empty row with default height and inserts it
131 * just before @before_row.
133 * Returns: (transfer full): the newly created row or %NULL on errors.
135 * Since: 1.0
137 AdgTableRow *
138 adg_table_row_new_before(AdgTableRow *before_row)
140 AdgTableRow *table_row;
141 AdgTable *table;
143 g_return_val_if_fail(before_row != NULL, NULL);
145 table = (AdgTable *) before_row->table;
146 g_return_val_if_fail(ADG_IS_TABLE(table), NULL);
148 table_row = _adg_row_new(table);
149 adg_table_insert(table, table_row, before_row);
150 adg_entity_invalidate((AdgEntity *) before_row->table);
152 return table_row;
157 * adg_table_row_free:
158 * @table_row: an #AdgTableRow structure
160 * Releases all the memory allocated by @table_row, itself included.
162 * Since: 1.0
164 void
165 adg_table_row_free(AdgTableRow *table_row)
167 AdgTable *table;
169 g_slist_foreach(table_row->cells, (GFunc) adg_table_cell_free, NULL);
170 g_slist_free(table_row->cells);
172 table = table_row->table;
173 if (table != NULL)
174 adg_table_remove(table, table_row);
176 g_free(table_row);
180 * adg_table_row_insert:
181 * @table_row: a valid #AdgTableRow
182 * @table_cell: the #AdgTableCell to insert
183 * @before_cell: (allow-none): an #AdgTableRow or %NULL
185 * Inserts @table_cell inside @table_row. If @before_cell
186 * is specified, @table_cell is inserted before it.
188 * Since: 1.0
190 void
191 adg_table_row_insert(AdgTableRow *table_row, AdgTableCell *table_cell,
192 AdgTableCell *before_cell)
194 g_return_if_fail(table_row != NULL);
195 g_return_if_fail(table_cell != NULL);
197 if (before_cell == NULL) {
198 table_row->cells = g_slist_append(table_row->cells, table_cell);
199 } else {
200 GSList *before = g_slist_find(table_row->cells, before_cell);
202 /* This MUST be present, otherwise something really bad happened */
203 g_return_if_fail(before != NULL);
205 table_row->cells = g_slist_insert_before(table_row->cells,
206 before, table_cell);
211 * adg_table_row_remove:
212 * @table_row: a valid #AdgTableRow
213 * @table_cell: the #AdgTableCell to remove
215 * Removes @table_cell from list of cells of @table_row.
217 * Since: 1.0
219 void
220 adg_table_row_remove(AdgTableRow *table_row, AdgTableCell *table_cell)
222 g_return_if_fail(table_row != NULL);
223 g_return_if_fail(table_cell != NULL);
225 table_row->cells = g_slist_remove(table_row->cells, table_cell);
229 * adg_table_row_foreach:
230 * @table_row: an #AdgTableRow
231 * @callback: (scope call): a callback
232 * @user_data: callback user data
234 * Invokes @callback on each cell of @table_row.
235 * The callback should be declared as:
237 * |[
238 * void callback(AdgTableCell *table_cell, gpointer user_data);
239 * ]|
241 * Since: 1.0
243 void
244 adg_table_row_foreach(AdgTableRow *table_row,
245 GCallback callback, gpointer user_data)
247 g_return_if_fail(table_row != NULL);
248 g_return_if_fail(callback != NULL);
250 g_slist_foreach(table_row->cells, (GFunc) callback, user_data);
254 * adg_table_row_get_table:
255 * @table_row: a valid #AdgTableRow
257 * Returns the container table of @table_row. The returned table
258 * is owned by @table_row and must not be modified or freed.
260 * Returns: (transfer none): the requested table or %NULL on errors.
262 * Since: 1.0
264 AdgTable *
265 adg_table_row_get_table(AdgTableRow *table_row)
267 g_return_val_if_fail(table_row != NULL, NULL);
269 return table_row->table;
273 * adg_table_row_set_height:
274 * @table_row: a valid #AdgTableRow
275 * @height: the new height
277 * Sets a new height on @table_row. The extents will be invalidated to
278 * recompute the whole layout of the table. Specifying %0 in
279 * @height will use the default height set in the table style.
281 * Since: 1.0
283 void
284 adg_table_row_set_height(AdgTableRow *table_row, gdouble height)
286 g_return_if_fail(table_row != NULL);
288 table_row->height = height;
290 adg_entity_invalidate((AdgEntity *) table_row->table);
294 * adg_table_row_get_height:
295 * @table_row: a valid #AdgTableRow
297 * Gets the height of @table_row.
299 * Returns: the requested height or %0 on errors
301 * Since: 1.0
303 gdouble
304 adg_table_row_get_height(AdgTableRow *table_row)
306 g_return_val_if_fail(table_row != NULL, 0.);
308 return table_row->height;
312 * adg_table_row_get_extents:
313 * @table_row: a valid #AdgTableRow
315 * Gets the extents of @table_row. This function is useful only after
316 * the arrange() phase as in the other situation the extents will
317 * likely be not up to date.
319 * Returns: (transfer none): the extents of @table_row or %NULL on errors.
321 * Since: 1.0
323 const CpmlExtents *
324 adg_table_row_get_extents(AdgTableRow *table_row)
326 g_return_val_if_fail(table_row != NULL, NULL);
328 return &table_row->extents;
332 * adg_table_row_size_request:
333 * @table_row: a valid #AdgTableRow
335 * Computes the minimum space needed to properly render @table_row
336 * and updates the size component of the internal #CpmlExtents struct,
337 * returning it to the caller. The returned #CpmlPair is owned by
338 * @table_row and should not be modified or freed.
340 * Returns: (transfer none): the minimum size required.
342 * Since: 1.0
344 const CpmlPair *
345 adg_table_row_size_request(AdgTableRow *table_row)
347 AdgTableStyle *table_style;
348 const CpmlPair *spacing;
349 CpmlExtents *extents;
350 CpmlVector *size;
351 AdgTableCell *cell;
352 GSList *cell_node;
353 const CpmlPair *cell_size;
355 g_return_val_if_fail(table_row != NULL, NULL);
357 table_style = (AdgTableStyle *) adg_table_get_table_style(table_row->table);
358 spacing = adg_table_style_get_cell_spacing(table_style);
359 extents = &table_row->extents;
360 size = &extents->size;
362 size->x = 0;
363 if (table_row->height == 0)
364 size->y = adg_table_style_get_row_height(table_style);
365 else
366 size->y = table_row->height;
368 /* Compute the row width by summing every cell width */
369 for (cell_node = table_row->cells; cell_node; cell_node = cell_node->next) {
370 cell = cell_node->data;
371 cell_size = adg_table_cell_size_request(cell, extents);
372 size->x += cell_size->x + spacing->x;
375 if (size->x > 0)
376 size->x += spacing->x;
378 return size;
382 * adg_table_row_arrange:
383 * @table_row: an #AdgTableRow
384 * @layout: the new extents to use
386 * Rearranges the underlying #AdgTableCell owned by @table_row using
387 * the new extents provided in @layout. If the x or y size component
388 * of @layout is negative, the value holded by the internal extents
389 * struct is not overriden.
391 * <note><para>
392 * table_row->extents must be up to date if @layout->size.x or
393 * @layout->size.y is negative in order to have a valid size.
394 * </para></note>
396 * Returns: (transfer none): the extents of @table_row or %NULL on errors.
398 * Since: 1.0
400 const CpmlExtents *
401 adg_table_row_arrange(AdgTableRow *table_row, const CpmlExtents *layout)
403 CpmlExtents *extents;
404 CpmlExtents cell_layout;
405 const CpmlExtents *cell_extents;
406 AdgTableStyle *table_style;
407 const CpmlPair *spacing;
408 AdgTableCell *cell;
409 GSList *cell_node;
411 g_return_val_if_fail(table_row != NULL, NULL);
413 /* Set the new extents */
414 extents = &table_row->extents;
415 extents->org = layout->org;
416 if (layout->size.x > 0)
417 extents->size.x = layout->size.x;
418 if (layout->size.y > 0)
419 extents->size.y = layout->size.y;
420 extents->is_defined = TRUE;
422 table_style = (AdgTableStyle *) adg_table_get_table_style(table_row->table);
423 spacing = adg_table_style_get_cell_spacing(table_style);
425 /* Propagate the arrange to the table cells */
426 cell_layout.org.x = extents->org.x + spacing->x;
427 cell_layout.org.y = extents->org.y;
428 cell_layout.size.x = -1;
429 cell_layout.size.y = extents->size.y;
431 for (cell_node = table_row->cells; cell_node; cell_node = cell_node->next) {
432 cell = cell_node->data;
433 cell_extents = adg_table_cell_arrange(cell, &cell_layout);
434 cell_layout.org.x += cell_extents->size.x + spacing->x;
437 return extents;
441 static AdgTableRow *
442 _adg_row_new(AdgTable *table)
444 AdgTableRow *table_row = g_new(AdgTableRow, 1);
446 table_row->table = table;
447 table_row->cells = NULL;
448 table_row->height = 0;
449 table_row->extents.is_defined = FALSE;
451 return table_row;