1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009,2010 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.
23 * @short_description: A tabular entity
25 * The #AdgTable is the entity to be used for rendering data arranged
26 * in tabular evironments.
28 * To define a table, you should add to its internal model any number
29 * of row using adg_table_row_new() or adg_table_row_new_before().
31 * Every row could be segmented with different cells by using
32 * adg_table_cell_new() or adg_table_cell_new_before(). Any cell can
33 * be filled with a title and a value: the font to be used will be
34 * picked up from the #AdgTableStyle got by resolving the
35 * #AdgTable:table-dress property.
37 * The default title is placed at the upper left corner of the cell
38 * while the value is centered up to the bottom edge of the cell.
39 * Anyway, the value position can be customized by using the
40 * adg_table_cell_set_value_pos() method. Anyway, both entities react
41 * to the common map displacements.
43 * Some convenient functions to easily create title and value entities
44 * with plain text are provided: adg_table_cell_new_full(),
45 * adg_table_cell_set_text_title() and adg_table_cell_set_text_value().
46 * When using these methods keep in mind the underlying #AdgToyText
47 * entities will be displaced accordingly to the
48 * #AdgTableStyle:cell-padding value (not used when setting the
49 * entities throught other APIs).
55 * All fields are private and should not be used directly.
56 * Use its public methods instead.
62 * An opaque structure referring to a row of an #AdgTable. Any
63 * table can have an unlimited number of rows.
69 * An opaque structure referring to the cell of an #AdgTableRow.
70 * Any row can have an unlimited number of cells.
74 #include "adg-internal.h"
75 #include "adg-table.h"
76 #include "adg-table-private.h"
77 #include "adg-alignment.h"
78 #include "adg-dress-builtins.h"
80 #include "adg-line-style.h"
81 #include "adg-toy-text.h"
83 #define _ADG_OLD_OBJECT_CLASS ((GObjectClass *) adg_table_parent_class)
84 #define _ADG_OLD_ENTITY_CLASS ((AdgEntityClass *) adg_table_parent_class)
87 G_DEFINE_TYPE(AdgTable
, adg_table
, ADG_TYPE_ENTITY
);
96 static void _adg_dispose (GObject
*object
);
97 static void _adg_finalize (GObject
*object
);
98 static void _adg_get_property (GObject
*object
,
102 static void _adg_set_property (GObject
*object
,
106 static void _adg_global_changed (AdgEntity
*entity
);
107 static void _adg_local_changed (AdgEntity
*entity
);
108 static void _adg_invalidate (AdgEntity
*entity
);
109 static void _adg_arrange (AdgEntity
*entity
);
110 static void _adg_arrange_grid (AdgEntity
*entity
);
111 static void _adg_arrange_frame (AdgEntity
*entity
,
112 const CpmlExtents
*extents
);
113 static void _adg_render (AdgEntity
*entity
,
115 static void _adg_propagate (AdgTable
*table
,
116 const gchar
*detailed_signal
,
118 static AdgTableRow
* _adg_row_new (AdgTable
*table
,
119 AdgTableRow
*before_row
);
120 static void _adg_row_arrange (AdgTableRow
*row
);
121 static void _adg_row_arrange_size (AdgTableRow
*row
);
122 static void _adg_row_dispose (AdgTableRow
*row
);
123 static void _adg_row_free (AdgTableRow
*row
);
124 static AdgTableCell
* _adg_cell_new (AdgTableRow
*row
,
125 AdgTableCell
*before_cell
,
131 static void _adg_cell_set_name (AdgTableCell
*cell
,
133 static gboolean
_adg_cell_set_title (AdgTableCell
*cell
,
135 static gboolean
_adg_cell_set_value (AdgTableCell
*cell
,
137 static void _adg_cell_set_value_pos (AdgTableCell
*cell
,
138 const AdgPair
*from_factor
,
139 const AdgPair
*to_factor
);
140 static void _adg_cell_arrange (AdgTableCell
*cell
);
141 static void _adg_cell_arrange_size (AdgTableCell
*cell
);
142 static void _adg_cell_dispose (AdgTableCell
*cell
);
143 static void _adg_cell_free (AdgTableCell
*cell
);
144 static gboolean
_adg_value_match (gpointer key
,
150 adg_table_class_init(AdgTableClass
*klass
)
152 GObjectClass
*gobject_class
;
153 AdgEntityClass
*entity_class
;
156 gobject_class
= (GObjectClass
*) klass
;
157 entity_class
= (AdgEntityClass
*) klass
;
159 g_type_class_add_private(klass
, sizeof(AdgTablePrivate
));
161 gobject_class
->dispose
= _adg_dispose
;
162 gobject_class
->finalize
= _adg_finalize
;
163 gobject_class
->get_property
= _adg_get_property
;
164 gobject_class
->set_property
= _adg_set_property
;
166 entity_class
->global_changed
= _adg_global_changed
;
167 entity_class
->local_changed
= _adg_local_changed
;
168 entity_class
->invalidate
= _adg_invalidate
;
169 entity_class
->arrange
= _adg_arrange
;
170 entity_class
->render
= _adg_render
;
172 param
= adg_param_spec_dress("table-dress",
174 P_("The dress to use for stroking this entity"),
177 g_object_class_install_property(gobject_class
, PROP_TABLE_DRESS
, param
);
179 param
= g_param_spec_boolean("has-frame",
180 P_("Has Frame Flag"),
181 P_("If enabled, a frame using the proper dress found in this table style will be drawn around the table extents"),
184 g_object_class_install_property(gobject_class
, PROP_HAS_FRAME
, param
);
188 adg_table_init(AdgTable
*table
)
190 AdgTablePrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(table
,
194 data
->table_dress
= ADG_DRESS_TABLE
;
195 data
->has_frame
= TRUE
;
197 data
->table_style
= NULL
;
201 data
->cell_names
= NULL
;
207 _adg_dispose(GObject
*object
)
209 AdgTablePrivate
*data
= ((AdgTable
*) object
)->data
;
212 g_object_unref(data
->grid
);
217 g_object_unref(data
->frame
);
221 /* The rows finalization will happen in the finalize() method */
223 g_slist_foreach(data
->rows
, (GFunc
) _adg_row_dispose
, NULL
);
225 if (_ADG_OLD_OBJECT_CLASS
->dispose
)
226 _ADG_OLD_OBJECT_CLASS
->dispose(object
);
230 _adg_finalize(GObject
*object
)
233 AdgTablePrivate
*data
;
235 table
= (AdgTable
*) object
;
239 g_slist_foreach(data
->rows
, (GFunc
) _adg_row_free
, NULL
);
240 g_slist_free(data
->rows
);
243 if (data
->cell_names
)
244 g_hash_table_destroy(data
->cell_names
);
246 if (_ADG_OLD_OBJECT_CLASS
->finalize
)
247 _ADG_OLD_OBJECT_CLASS
->finalize(object
);
251 _adg_get_property(GObject
*object
, guint prop_id
,
252 GValue
*value
, GParamSpec
*pspec
)
254 AdgTablePrivate
*data
= ((AdgTable
*) object
)->data
;
257 case PROP_TABLE_DRESS
:
258 g_value_set_int(value
, data
->table_dress
);
261 g_value_set_boolean(value
, data
->has_frame
);
264 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
270 _adg_set_property(GObject
*object
, guint prop_id
,
271 const GValue
*value
, GParamSpec
*pspec
)
273 AdgTablePrivate
*data
= ((AdgTable
*) object
)->data
;
276 case PROP_TABLE_DRESS
:
277 data
->table_dress
= g_value_get_int(value
);
280 data
->has_frame
= g_value_get_boolean(value
);
283 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
292 * Creates a new empty table entity. The #AdgEntity:local-method
293 * property is set by default to #ADG_MIX_DISABLED, that is the
294 * table is not subject to any local transformations.
296 * Returns: the newly created table entity
301 return g_object_new(ADG_TYPE_TABLE
,
302 "local-method", ADG_MIX_DISABLED
,
307 * adg_table_set_table_dress:
308 * @table: an #AdgTable
309 * @dress: the new #AdgDress to use
311 * Sets a new table dress for rendering @table. The new dress
312 * must be related to the original dress for this property:
313 * you cannot set a dress used for line styles to a dress
316 * The check is done by calling adg_dress_are_related() with
317 * @dress and the previous dress as arguments. Check out its
318 * documentation for details on what is a related dress.
321 adg_table_set_table_dress(AdgTable
*table
, AdgDress dress
)
323 g_return_if_fail(ADG_IS_TABLE(table
));
324 g_object_set((GObject
*) table
, "table-dress", dress
, NULL
);
328 * adg_table_get_table_dress:
329 * @table: an #AdgTable
331 * Gets the table dress to be used in rendering @table.
333 * Returns: the current table dress
336 adg_table_get_table_dress(AdgTable
*table
)
338 AdgTablePrivate
*data
;
340 g_return_val_if_fail(ADG_IS_TABLE(table
), ADG_DRESS_UNDEFINED
);
344 return data
->table_dress
;
348 * adg_table_switch_frame:
349 * @table: an #AdgTable
350 * @new_state: the new state of the frame
352 * Sets the #AdgTable:has-frame property: %TRUE will draw a
353 * frame around the whole table using the #AdgTableStyle:frame-dress
354 * dress of the table style.
357 adg_table_switch_frame(AdgTable
*table
, gboolean new_state
)
359 g_return_if_fail(ADG_IS_TABLE(table
));
360 g_object_set(table
, "has-frame", new_state
, NULL
);
364 * adg_table_has_frame:
365 * @table: an #AdgTable
367 * Returns the state of the #AdgTable:has-frame property.
369 * Returns: the current state
372 adg_table_has_frame(AdgTable
*table
)
374 AdgTablePrivate
*data
;
376 g_return_val_if_fail(ADG_IS_TABLE(table
), FALSE
);
380 return data
->has_frame
;
384 * adg_table_get_n_rows:
385 * @table: an #AdgTable
387 * Gets the number of rows stored in @table.
389 * Returns: the number of rows or %0 on empty @table or errors
392 adg_table_get_n_rows(AdgTable
*table
)
394 AdgTablePrivate
*data
;
396 g_return_val_if_fail(ADG_IS_TABLE(table
), 0);
400 if (data
->rows
== NULL
)
403 return g_slist_length(data
->rows
);
408 * @table: an #AdgTable
410 * Creates a new empty row and appends it at the end of the rows
411 * yet present in @table. By default, the height of this new
412 * row will be the fallback value provided by the table style:
413 * you can override it by using adg_table_row_set_height().
415 * Returns: the newly created row or %NULL on errors
418 adg_table_row_new(AdgTable
*table
)
420 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
422 return _adg_row_new(table
, NULL
);
426 * adg_table_row_new_before:
427 * @row: a valid #AdgTableRow
429 * Creates a new empty row with default height and inserts it
432 * Returns: the newly created row or %NULL on errors
435 adg_table_row_new_before(AdgTableRow
*row
)
437 g_return_val_if_fail(row
!= NULL
, NULL
);
438 g_return_val_if_fail(ADG_IS_TABLE(row
->table
), NULL
);
440 return _adg_row_new(row
->table
, row
);
444 * adg_table_row_delete:
445 * @row: a valid #AdgTableRow
447 * Removes @row from its owner table and frees every resources allocated
448 * by it. This means also the eventual cells owned by @row will be freed.
451 adg_table_row_delete(AdgTableRow
*row
)
454 AdgTablePrivate
*data
;
456 g_return_if_fail(row
!= NULL
);
460 g_return_if_fail(ADG_IS_TABLE(table
));
464 g_slist_foreach(row
->cells
, (GFunc
) _adg_cell_free
, NULL
);
465 g_slist_free(row
->cells
);
466 data
->rows
= g_slist_remove(data
->rows
, row
);
472 * adg_table_row_get_n_cells:
473 * @row: a valid #AdgTableRow
475 * Gets the number of cells stored in @row.
477 * Returns: the number of cells or %0 on empty row or errors
480 adg_table_row_get_n_cells(const AdgTableRow
*row
)
482 g_return_val_if_fail(row
!= NULL
, 0);
484 if (row
->cells
== NULL
)
487 return g_slist_length(row
->cells
);
491 * adg_table_row_set_height:
492 * @row: a valid #AdgTableRow
493 * @height: the new height
495 * Sets a new height on @row. The extents will be invalidated to
496 * recompute the whole layout of the table. Specifying %0 in
497 * @height will use the default height set in the table style.
500 adg_table_row_set_height(AdgTableRow
*row
, gdouble height
)
502 g_return_if_fail(row
!= NULL
);
504 row
->height
= height
;
506 adg_entity_invalidate((AdgEntity
*) row
->table
);
510 * adg_table_row_get_height:
511 * @row: a valid #AdgTableRow
513 * Gets the height of @row.
515 * Returns: the requested height or %0 on errors
518 adg_table_row_get_height(AdgTableRow
*row
)
520 g_return_val_if_fail(row
!= NULL
, 0.);
526 * adg_table_row_get_extents:
527 * @row: a valid #AdgTableRow
529 * Gets the extents of @row. This function is useful only after the
530 * arrange() phase as in the other situation the extents will likely
533 * Returns: the extents of @row or %NULL on errors
536 adg_table_row_get_extents(AdgTableRow
*row
)
538 g_return_val_if_fail(row
!= NULL
, NULL
);
540 return &row
->extents
;
544 * adg_table_cell_new:
545 * @row: a valid #AdgTableRow
546 * @width: width of the cell
548 * Creates a new empty cell without a frame and appends it at the
549 * end of the cells yet present in @row. You can add content to the
550 * cell by using adg_table_cell_set_title() and
551 * adg_table_cell_set_value() or enable the frame with
552 * adg_table_cell_switch_frame().
554 * A positive @width value specifies the width of this cell in global
555 * space: if the width of its content (that is, either the title or the
556 * value entity) will be greater than @width, it will be rendered
557 * outside the cell boundary box, luckely overwriting the adiacent
560 * Using %0 as @width means the width of the cell will be automatically
561 * adjusted to the maximum width of its content.
563 * Negative width values are not allowed: this condition will raise
564 * a warning without any further processing.
566 * Returns: the newly created cell or %NULL on errors
569 adg_table_cell_new(AdgTableRow
*row
, gdouble width
)
571 g_return_val_if_fail(row
!= NULL
, NULL
);
572 g_return_val_if_fail(width
>= 0, NULL
);
574 return _adg_cell_new(row
, NULL
, width
, FALSE
, NULL
, NULL
, NULL
);
578 * adg_table_cell_new_before:
579 * @cell: a valid #AdgTableCell
580 * @width: width of the cell
582 * Creates a new cell and inserts it rigthly before the @cell cell.
583 * This works similarily and accepts the same parameters as the
584 * adg_table_cell_new() function.
586 * Returns: the newly created cell or %NULL on errors
589 adg_table_cell_new_before(AdgTableCell
*cell
, gdouble width
)
591 g_return_val_if_fail(cell
!= NULL
, NULL
);
592 g_return_val_if_fail(cell
->row
!= NULL
, NULL
);
593 g_return_val_if_fail(width
>= 0, NULL
);
595 return _adg_cell_new(cell
->row
, cell
, width
, FALSE
, NULL
, NULL
, NULL
);
599 * adg_table_cell_new_full:
600 * @row: a valid #AdgTableRow
601 * @width: width of the cell
602 * @name: name to associate
603 * @title: title to render
604 * @value: value to render
606 * A convenient function to append a framed cell to @row with a
607 * specific title and value text. The font to use for rendering
608 * @title and @value will be picked up from the table style, so
609 * be sure to have the correct table dress set before calling
612 * @row and @width have the same meanings as in adg_table_cell_new():
613 * check its documentation for details.
615 * @name is an optional identifier to univoquely access this cell
616 * by using adg_table_cell(). The identifier must be univoque:
617 * if there is yet a cell with the same name a warning message will
618 * be raised and the function will fail.
620 * Returns: the newly created cell or %NULL on errors
623 adg_table_cell_new_full(AdgTableRow
*row
, gdouble width
, const gchar
*name
,
624 const gchar
*title
, const gchar
*value
)
628 g_return_val_if_fail(row
!= NULL
, NULL
);
630 cell
= _adg_cell_new(row
, NULL
, width
, TRUE
, name
, NULL
, NULL
);
633 adg_table_cell_set_text_title(cell
, title
);
636 adg_table_cell_set_text_value(cell
, value
);
643 * @table: an #AdgTable
644 * @name: the name of a cell
646 * Gets the cell named @name inside @table. Only named cells can be
647 * retrieved by this method.
649 * Returns: the requested cell or %NULL if not found
652 adg_table_cell(AdgTable
*table
, const gchar
*name
)
654 AdgTablePrivate
*data
;
656 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
660 if (data
->cell_names
== NULL
)
663 return g_hash_table_lookup(data
->cell_names
, name
);
667 * adg_table_cell_delete:
668 * @cell: a valid #AdgTableCell
670 * Deletes @cell removing it from the container row and freeing
671 * any resource associated to it.
674 adg_table_cell_delete(AdgTableCell
*cell
)
678 g_return_if_fail(cell
!= NULL
);
682 g_return_if_fail(row
!= NULL
);
684 _adg_cell_free(cell
);
685 row
->cells
= g_slist_remove(row
->cells
, cell
);
689 * adg_table_cell_set_name:
690 * @cell: a valid #AdgTableCell
691 * @name: the new name of @cell
693 * Sets a new name on @cell: this will allow to access @cell by
694 * name at a later time using the adg_table_cell() API.
697 adg_table_cell_set_name(AdgTableCell
*cell
, const gchar
*name
)
699 AdgTablePrivate
*data
;
701 g_return_if_fail(cell
!= NULL
);
703 data
= cell
->row
->table
->data
;
705 _adg_cell_set_name(cell
, NULL
);
706 _adg_cell_set_name(cell
, name
);
710 * adg_table_cell_get_name:
711 * @cell: a valid #AdgTableCell
713 * Gets the name assigned to @cell. This function is highly inefficient
714 * as the cell names are stored in a hash table optimized to get a cell
715 * from a name. Getting the name from a cell involves a full hash table
718 * Returns: the name bound of @cell or %NULL on no name or errors
721 adg_table_cell_get_name(AdgTableCell
*cell
)
723 AdgTablePrivate
*data
;
725 g_return_val_if_fail(cell
!= NULL
, NULL
);
727 data
= cell
->row
->table
->data
;
729 return g_hash_table_find(data
->cell_names
, _adg_value_match
, cell
);
733 * adg_table_cell_set_title:
734 * @cell: a valid #AdgTableCell
735 * @title: the new title entity
737 * Sets @title as the new title entity of @cell. The top left
738 * corner of the bounding box of @title will be cohincident to
739 * the top left corner of the cell extents, taking into accounts
740 * eventual padding spaces specified by the table style.
742 * The old internal entity is unrefenrenced while the @title (if
743 * not %NULL) is refenenced with g_object_ref_sink().
745 * @title can be %NULL, in which case the old entity is removed.
748 adg_table_cell_set_title(AdgTableCell
*cell
, AdgEntity
*title
)
750 g_return_if_fail(cell
!= NULL
);
751 g_return_if_fail(title
== NULL
|| ADG_IS_ENTITY(title
));
753 if (_adg_cell_set_title(cell
, title
))
754 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
758 * adg_table_cell_set_text_title:
759 * @cell: a valid #AdgTableCell
760 * @title: a text string
762 * Convenient function to set a the title of a cell using an #AdgToyText
763 * entity with the font dress picked from #AdgTable:table-dress with
764 * a call to adg_table_style_get_title_dress().
767 adg_table_cell_set_text_title(AdgTableCell
*cell
, const gchar
*title
)
771 AdgTableStyle
*table_style
;
772 const AdgPair
*padding
;
773 AdgDress table_dress
, font_dress
;
776 g_return_if_fail(cell
!= NULL
);
779 adg_table_cell_set_title(cell
, NULL
);
782 const gchar
*old_title
;
784 if (ADG_IS_TOY_TEXT(cell
->title
))
785 old_title
= adg_toy_text_get_label((AdgToyText
*) cell
->title
);
789 if (g_strcmp0(title
, old_title
) == 0)
793 table
= cell
->row
->table
;
794 table_dress
= adg_table_get_table_dress(table
);
795 table_style
= (AdgTableStyle
*) adg_entity_style((AdgEntity
*) table
,
797 padding
= adg_table_style_get_cell_padding(table_style
);
798 font_dress
= adg_table_style_get_title_dress(table_style
);
799 entity
= g_object_new(ADG_TYPE_TOY_TEXT
, "label", title
,
800 "font-dress", font_dress
, NULL
);
802 cairo_matrix_init_translate(&map
, padding
->x
, padding
->y
);
803 adg_entity_set_global_map(entity
, &map
);
805 adg_table_cell_set_title(cell
, entity
);
809 * adg_table_cell_title:
810 * @cell: a valid #AdgTableCell
812 * Gets the current title of @cell. The returned string is owned
813 * by @cell and must not be modified or freed.
815 * Returns: the title entity or %NULL for undefined title
818 adg_table_cell_title(AdgTableCell
*cell
)
820 g_return_val_if_fail(cell
!= NULL
, NULL
);
826 * adg_table_cell_set_value:
827 * @cell: a valid #AdgTableCell
828 * @value: the new value entity
830 * Sets @value as the new value entity of @cell. The bottom middle
831 * point of the bounding box of @value will be cohincident to the
832 * bottom middle point of the cell extents, taking into accounts
833 * eventual padding spaces specified by the table style.
835 * The old internal entity is unrefenrenced while the @value (if
836 * not %NULL) is refenenced with g_object_ref_sink().
838 * @value can be %NULL, in which case the old entity is removed.
841 adg_table_cell_set_value(AdgTableCell
*cell
, AdgEntity
*value
)
843 g_return_if_fail(cell
!= NULL
);
844 g_return_if_fail(value
== NULL
|| ADG_IS_ENTITY(value
));
846 if (_adg_cell_set_value(cell
, value
))
847 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
851 * adg_table_cell_set_text_value:
852 * @cell: a valid #AdgTableCell
853 * @value: a text string
855 * Convenient function to set a the value of a cell using an #AdgToyText
856 * entity with a value font dress picked from #AdgTable:table-dress with
857 * a call to adg_table_style_get_value_dress().
860 adg_table_cell_set_text_value(AdgTableCell
*cell
, const gchar
*value
)
864 AdgTableStyle
*table_style
;
865 const AdgPair
*padding
;
866 AdgDress table_dress
, font_dress
;
869 g_return_if_fail(cell
!= NULL
);
872 adg_table_cell_set_value(cell
, NULL
);
875 const gchar
*old_value
;
877 if (ADG_IS_TOY_TEXT(cell
->value
))
878 old_value
= adg_toy_text_get_label((AdgToyText
*) cell
->value
);
882 if (g_strcmp0(value
, old_value
) == 0)
886 table
= cell
->row
->table
;
887 table_dress
= adg_table_get_table_dress(table
);
888 table_style
= (AdgTableStyle
*) adg_entity_style((AdgEntity
*) table
,
890 padding
= adg_table_style_get_cell_padding(table_style
);
891 font_dress
= adg_table_style_get_value_dress(table_style
);
892 entity
= g_object_new(ADG_TYPE_TOY_TEXT
, "label", value
,
893 "font-dress", font_dress
, NULL
);
895 cairo_matrix_init_translate(&map
, 0, -padding
->y
);
896 adg_entity_set_global_map(entity
, &map
);
898 adg_table_cell_set_value(cell
, entity
);
902 * adg_table_cell_value:
903 * @cell: a valid #AdgTableCell
905 * Gets the current value of @cell. The returned string is owned
906 * by @cell and must not be modified or freed.
908 * Returns: the value entity or %NULL for undefined value
911 adg_table_cell_value(AdgTableCell
*cell
)
913 g_return_val_if_fail(cell
!= NULL
, NULL
);
919 * adg_table_cell_set_value_pos:
920 * @cell: a valid #AdgTableCell
921 * @from_factor: the alignment factor on the value entity
922 * @to_factor: the alignment factor on the cell
924 * Sets a new custom position for the value entity of @cell. The
925 * @from_factor specifies the source point (as a fraction of the
926 * value extents) while the @to_factor is the destination point
927 * (specified as a fraction of the cell extents) the source point
931 adg_table_cell_set_value_pos(AdgTableCell
*cell
, const AdgPair
*from_factor
,
932 const AdgPair
*to_factor
)
934 g_return_if_fail(cell
!= NULL
);
935 g_return_if_fail(cell
->value
!= NULL
);
937 _adg_cell_set_value_pos(cell
, from_factor
, to_factor
);
941 * adg_table_cell_set_width:
942 * @cell: a valid #AdgTableCell
943 * @width: the new width
945 * Sets a new width on @cell. The extents on the whole table
946 * will be invalidated, so will be recomputed in the next
950 adg_table_cell_set_width(AdgTableCell
*cell
, gdouble width
)
952 g_return_if_fail(cell
!= NULL
);
956 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
960 * adg_table_cell_get_width:
961 * @cell: a valid #AdgTableCell
963 * Gets the width of @cell.
965 * Returns: the requested width or %0 on errors
968 adg_table_cell_get_width(AdgTableCell
*cell
)
970 g_return_val_if_fail(cell
!= NULL
, 0.);
976 * adg_table_cell_switch_frame:
977 * @cell: a valid #AdgTableCell
978 * @new_state: the new frame state
980 * Sets the frame flag of @cell: if @new_state is %TRUE, a frame around
981 * @cell will be rendered using the #AdgTableStyle:cell-dress dress
982 * of the table style.
985 adg_table_cell_switch_frame(AdgTableCell
*cell
, gboolean new_state
)
987 AdgTablePrivate
*data
;
989 g_return_if_fail(cell
!= NULL
);
991 if (cell
->has_frame
== new_state
)
994 data
= cell
->row
->table
->data
;
995 cell
->has_frame
= new_state
;
998 g_object_unref(data
->grid
);
1004 * adg_table_cell_has_frame:
1005 * @cell: a valid #AdgTableCell
1007 * Gets the frame flag of @cell.
1009 * Returns: the frame flag
1012 adg_table_cell_has_frame(AdgTableCell
*cell
)
1014 g_return_val_if_fail(cell
!= NULL
, FALSE
);
1016 return cell
->has_frame
;
1020 * adg_table_cell_get_extents:
1021 * @cell: a valid #AdgTableCell
1023 * Gets the extents of @cell. This function is useful only after the
1024 * arrange() phase as in the other situation the extents will likely
1025 * be not up to date.
1027 * Returns: the extents of @cell or %NULL on errors
1030 adg_table_cell_get_extents(AdgTableCell
*cell
)
1032 g_return_val_if_fail(cell
!= NULL
, NULL
);
1034 return &cell
->extents
;
1039 _adg_global_changed(AdgEntity
*entity
)
1041 if (_ADG_OLD_ENTITY_CLASS
->global_changed
)
1042 _ADG_OLD_ENTITY_CLASS
->global_changed(entity
);
1044 _adg_propagate((AdgTable
*) entity
, "global-changed");
1048 _adg_local_changed(AdgEntity
*entity
)
1050 if (_ADG_OLD_ENTITY_CLASS
->local_changed
)
1051 _ADG_OLD_ENTITY_CLASS
->local_changed(entity
);
1053 _adg_propagate((AdgTable
*) entity
, "local-changed");
1057 _adg_invalidate(AdgEntity
*entity
)
1059 _adg_propagate((AdgTable
*) entity
, "invalidate");
1063 _adg_arrange(AdgEntity
*entity
)
1066 AdgTablePrivate
*data
;
1067 CpmlExtents extents
= { 0 };
1068 const AdgPair
*spacing
;
1073 table
= (AdgTable
*) entity
;
1076 /* Resolve the table style */
1077 if (data
->table_style
== NULL
)
1078 data
->table_style
= (AdgTableStyle
*)
1079 adg_entity_style(entity
, data
->table_dress
);
1081 spacing
= adg_table_style_get_cell_spacing(data
->table_style
);
1083 /* Compute the size of the table */
1084 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1085 row
= row_node
->data
;
1087 _adg_row_arrange_size(row
);
1089 if (row
->extents
.size
.x
> extents
.size
.x
)
1090 extents
.size
.x
= row
->extents
.size
.x
;
1091 extents
.size
.y
+= row
->extents
.size
.y
;
1094 /* Arrange the layout of the table components */
1095 y
= extents
.org
.y
+ spacing
->y
;
1096 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1097 row
= row_node
->data
;
1099 row
->extents
.org
.x
= extents
.org
.x
;
1100 row
->extents
.org
.y
= y
;
1101 row
->extents
.size
.x
= extents
.size
.x
;
1103 _adg_row_arrange(row
);
1105 y
+= row
->extents
.size
.y
+ spacing
->y
;
1108 _adg_arrange_grid(entity
);
1109 _adg_arrange_frame(entity
, &extents
);
1111 extents
.is_defined
= TRUE
;
1112 cpml_extents_transform(&extents
, adg_entity_get_global_matrix(entity
));
1113 cpml_extents_transform(&extents
, adg_entity_get_local_matrix(entity
));
1114 adg_entity_set_extents(entity
, &extents
);
1118 _adg_arrange_grid(AdgEntity
*entity
)
1120 AdgTablePrivate
*data
;
1123 GSList
*row_node
, *cell_node
;
1129 data
= ((AdgTable
*) entity
)->data
;
1134 path
= adg_path_new();
1135 trail
= (AdgTrail
*) path
;
1137 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1138 row
= row_node
->data
;
1140 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1141 cell
= cell_node
->data
;
1143 if (!cell
->has_frame
)
1146 cpml_pair_copy(&pair
, &cell
->extents
.org
);
1147 adg_path_move_to(path
, &pair
);
1148 pair
.x
+= cell
->extents
.size
.x
;
1149 adg_path_line_to(path
, &pair
);
1150 pair
.y
+= cell
->extents
.size
.y
;
1151 adg_path_line_to(path
, &pair
);
1152 pair
.x
-= cell
->extents
.size
.x
;
1153 adg_path_line_to(path
, &pair
);
1154 adg_path_close(path
);
1158 if (!adg_trail_get_extents(trail
)->is_defined
)
1161 dress
= adg_table_style_get_grid_dress(data
->table_style
);
1162 data
->grid
= g_object_new(ADG_TYPE_STROKE
,
1163 "line-dress", dress
,
1167 adg_entity_arrange((AdgEntity
*) data
->grid
);
1171 _adg_arrange_frame(AdgEntity
*entity
, const CpmlExtents
*extents
)
1173 AdgTablePrivate
*data
;
1179 data
= ((AdgTable
*) entity
)->data
;
1181 if (data
->frame
|| !data
->has_frame
)
1184 path
= adg_path_new();
1185 trail
= (AdgTrail
*) path
;
1187 cpml_pair_copy(&pair
, &extents
->org
);
1188 adg_path_move_to(path
, &pair
);
1189 pair
.x
+= extents
->size
.x
;
1190 adg_path_line_to(path
, &pair
);
1191 pair
.y
+= extents
->size
.y
;
1192 adg_path_line_to(path
, &pair
);
1193 pair
.x
-= extents
->size
.x
;
1194 adg_path_line_to(path
, &pair
);
1195 adg_path_close(path
);
1197 dress
= adg_table_style_get_frame_dress(data
->table_style
);
1198 data
->frame
= g_object_new(ADG_TYPE_STROKE
,
1199 "line-dress", dress
,
1203 adg_entity_arrange((AdgEntity
*) data
->frame
);
1207 _adg_render(AdgEntity
*entity
, cairo_t
*cr
)
1209 AdgTablePrivate
*data
= ((AdgTable
*) entity
)->data
;
1211 cairo_transform(cr
, adg_entity_get_local_matrix(entity
));
1212 adg_style_apply((AdgStyle
*) data
->table_style
, entity
, cr
);
1214 _adg_propagate((AdgTable
*) entity
, "render", cr
);
1218 _adg_propagate(AdgTable
*table
, const gchar
*detailed_signal
, ...)
1222 va_list var_args
, var_copy
;
1223 AdgTablePrivate
*data
;
1228 AdgAlignment
*alignment
;
1230 if (!g_signal_parse_name(detailed_signal
, G_TYPE_FROM_INSTANCE(table
),
1231 &signal_id
, &detail
, FALSE
)) {
1232 g_return_if_reached();
1235 va_start(var_args
, detailed_signal
);
1239 G_VA_COPY(var_copy
, var_args
);
1240 g_signal_emit_valist(data
->frame
, signal_id
, detail
, var_copy
);
1244 G_VA_COPY(var_copy
, var_args
);
1245 g_signal_emit_valist(data
->grid
, signal_id
, detail
, var_copy
);
1248 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1249 row
= row_node
->data
;
1251 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1252 cell
= cell_node
->data
;
1255 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->title
);
1256 G_VA_COPY(var_copy
, var_args
);
1257 g_signal_emit_valist(alignment
, signal_id
, detail
, var_copy
);
1261 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->value
);
1262 G_VA_COPY(var_copy
, var_args
);
1263 g_signal_emit_valist(alignment
, signal_id
, detail
, var_copy
);
1271 static AdgTableRow
*
1272 _adg_row_new(AdgTable
*table
, AdgTableRow
*before_row
)
1274 AdgTablePrivate
*data
;
1275 AdgTableRow
*new_row
;
1278 new_row
= g_new(AdgTableRow
, 1);
1279 new_row
->table
= table
;
1280 new_row
->cells
= NULL
;
1281 new_row
->height
= 0;
1282 new_row
->extents
.is_defined
= FALSE
;
1284 if (before_row
== NULL
) {
1285 data
->rows
= g_slist_append(data
->rows
, new_row
);
1287 GSList
*before_node
= g_slist_find(data
->rows
, before_row
);
1289 /* This MUST be present, otherwise something really bad happened */
1290 g_return_val_if_fail(before_node
!= NULL
, NULL
);
1292 data
->rows
= g_slist_insert_before(data
->rows
, before_node
, new_row
);
1295 _adg_invalidate((AdgEntity
*) table
);
1300 /* Before calling this function, row->extents should be updated */
1302 _adg_row_arrange(AdgTableRow
*row
)
1304 AdgTableStyle
*table_style
;
1305 const AdgPair
*spacing
;
1311 table_style
= GET_TABLE_STYLE(row
->table
);
1312 spacing
= adg_table_style_get_cell_spacing(table_style
);
1313 org
= &row
->extents
.org
;
1314 x
= org
->x
+ spacing
->x
;
1316 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1317 cell
= cell_node
->data
;
1319 cell
->extents
.org
.x
= x
;
1320 cell
->extents
.org
.y
= org
->y
;
1322 _adg_cell_arrange(cell
);
1324 x
+= cell
->extents
.size
.x
+ spacing
->x
;
1327 row
->extents
.is_defined
= TRUE
;
1331 _adg_row_arrange_size(AdgTableRow
*row
)
1333 AdgTableStyle
*table_style
;
1334 const AdgPair
*spacing
;
1339 table_style
= GET_TABLE_STYLE(row
->table
);
1340 spacing
= adg_table_style_get_cell_spacing(table_style
);
1341 size
= &row
->extents
.size
;
1344 if (row
->height
== 0)
1345 size
->y
= adg_table_style_get_row_height(table_style
);
1347 size
->y
= row
->height
;
1349 /* Compute the row width by summing every cell width */
1350 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1351 cell
= cell_node
->data
;
1353 _adg_cell_arrange_size(cell
);
1355 size
->x
+= cell
->extents
.size
.x
+ spacing
->x
;
1359 size
->x
+= spacing
->x
;
1363 _adg_row_dispose(AdgTableRow
*row
)
1365 g_slist_foreach(row
->cells
, (GFunc
) _adg_cell_dispose
, NULL
);
1369 _adg_row_free(AdgTableRow
*row
)
1371 g_slist_foreach(row
->cells
, (GFunc
) _adg_cell_free
, NULL
);
1372 g_slist_free(row
->cells
);
1377 static AdgTableCell
*
1378 _adg_cell_new(AdgTableRow
*row
, AdgTableCell
*before_cell
,
1379 gdouble width
, gboolean has_frame
,
1380 const gchar
*name
, AdgEntity
*title
, AdgEntity
*value
)
1382 AdgTablePrivate
*data
;
1383 AdgTableCell
*new_cell
;
1385 data
= row
->table
->data
;
1387 if (name
&& data
->cell_names
&&
1388 g_hash_table_lookup(data
->cell_names
, name
)) {
1389 g_warning(_("%s: `%s' cell name is yet used"), G_STRLOC
, name
);
1393 new_cell
= g_new(AdgTableCell
, 1);
1394 new_cell
->row
= row
;
1395 new_cell
->width
= width
;
1396 new_cell
->has_frame
= has_frame
;
1397 new_cell
->title
= NULL
;
1398 new_cell
->value
= NULL
;
1399 new_cell
->extents
.is_defined
= FALSE
;
1400 new_cell
->value_factor
.x
= 0.5;
1401 new_cell
->value_factor
.y
= 1;
1403 _adg_cell_set_title(new_cell
, title
);
1404 _adg_cell_set_value(new_cell
, value
);
1406 if (before_cell
== NULL
) {
1407 row
->cells
= g_slist_append(row
->cells
, new_cell
);
1409 GSList
*before_node
= g_slist_find(row
->cells
, before_cell
);
1411 /* This MUST be not null, otherwise something really bad happened */
1412 g_return_val_if_fail(before_node
!= NULL
, NULL
);
1414 row
->cells
= g_slist_insert_before(row
->cells
, before_node
, new_cell
);
1418 _adg_cell_set_name(new_cell
, name
);
1424 _adg_cell_set_name(AdgTableCell
*cell
, const gchar
*name
)
1426 AdgTablePrivate
*data
= cell
->row
->table
->data
;
1428 if (data
->cell_names
== NULL
&& name
== NULL
)
1431 if (data
->cell_names
== NULL
)
1432 data
->cell_names
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1436 g_hash_table_foreach_remove(data
->cell_names
, _adg_value_match
, cell
);
1438 g_hash_table_insert(data
->cell_names
, g_strdup(name
), cell
);
1442 _adg_cell_set_title(AdgTableCell
*cell
, AdgEntity
*title
)
1444 AdgAlignment
*alignment
;
1446 if (cell
->title
== title
)
1450 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->title
);
1451 g_object_unref(alignment
);
1454 cell
->title
= title
;
1457 alignment
= adg_alignment_new_explicit(0, -1);
1458 g_object_ref_sink(alignment
);
1459 adg_entity_set_parent((AdgEntity
*) alignment
,
1460 (AdgEntity
*) cell
->row
->table
);
1462 adg_container_add((AdgContainer
*) alignment
, title
);
1469 _adg_cell_set_value(AdgTableCell
*cell
, AdgEntity
*value
)
1471 AdgAlignment
*alignment
;
1473 if (cell
->value
== value
)
1477 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->value
);
1478 g_object_unref(alignment
);
1481 cell
->value
= value
;
1484 alignment
= adg_alignment_new_explicit(0.5, 0);
1485 g_object_ref_sink(alignment
);
1486 adg_entity_set_parent((AdgEntity
*) alignment
,
1487 (AdgEntity
*) cell
->row
->table
);
1489 adg_container_add((AdgContainer
*) alignment
, value
);
1496 _adg_cell_set_value_pos(AdgTableCell
*cell
,
1497 const AdgPair
*from_factor
, const AdgPair
*to_factor
)
1499 AdgAlignment
*alignment
;
1501 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->value
);
1504 adg_alignment_set_factor(alignment
, from_factor
);
1507 cell
->value_factor
= *to_factor
;
1510 /* Before calling this function, cell->extents should be updated */
1512 _adg_cell_arrange(AdgTableCell
*cell
)
1514 CpmlExtents
*extents
;
1515 AdgAlignment
*alignment
;
1518 extents
= &cell
->extents
;
1521 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->title
);
1523 cairo_matrix_init_translate(&map
, extents
->org
.x
, extents
->org
.y
);
1524 adg_entity_set_global_map((AdgEntity
*) alignment
, &map
);
1530 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->value
);
1531 to
.x
= extents
->size
.x
* cell
->value_factor
.x
+ extents
->org
.x
;
1532 to
.y
= extents
->size
.y
* cell
->value_factor
.y
+ extents
->org
.y
;
1534 cairo_matrix_init_translate(&map
, to
.x
, to
.y
);
1535 adg_entity_set_global_map((AdgEntity
*) alignment
, &map
);
1538 extents
->is_defined
= TRUE
;
1542 _adg_cell_arrange_size(AdgTableCell
*cell
)
1545 AdgAlignment
*title_alignment
;
1546 AdgAlignment
*value_alignment
;
1548 size
= &cell
->extents
.size
;
1551 title_alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->title
);
1552 adg_entity_arrange((AdgEntity
*) title_alignment
);
1554 title_alignment
= NULL
;
1558 value_alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->value
);
1559 adg_entity_arrange((AdgEntity
*) value_alignment
);
1561 value_alignment
= NULL
;
1564 size
->y
= cell
->row
->extents
.size
.y
;
1566 if (cell
->width
== 0) {
1567 AdgTableStyle
*table_style
= GET_TABLE_STYLE(cell
->row
->table
);
1568 const CpmlExtents
*extents
;
1570 /* The width depends on the cell content (default = 0) */
1573 if (title_alignment
) {
1574 extents
= adg_entity_get_extents((AdgEntity
*) title_alignment
);
1575 size
->x
= extents
->size
.x
;
1578 if (value_alignment
) {
1579 extents
= adg_entity_get_extents((AdgEntity
*) value_alignment
);
1580 if (extents
->size
.x
> size
->x
)
1581 size
->x
= extents
->size
.x
;
1584 size
->x
+= adg_table_style_get_cell_spacing(table_style
)->x
* 2;
1586 size
->x
= cell
->width
;
1591 _adg_cell_dispose(AdgTableCell
*cell
)
1593 _adg_cell_set_title(cell
, NULL
);
1594 _adg_cell_set_value(cell
, NULL
);
1598 _adg_cell_free(AdgTableCell
*cell
)
1600 _adg_cell_set_name(cell
, NULL
);
1601 _adg_cell_dispose(cell
);
1606 _adg_value_match(gpointer key
, gpointer value
, gpointer user_data
)
1608 return value
== user_data
;