1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007,2008,2009 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 should 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.
41 * All fields are private and should not be used directly.
42 * Use its public methods instead.
48 * An opaque structure referring to a row of an #AdgTable. Any
49 * table can have an unlimited number of rows.
55 * An opaque structure referring to the cell of an #AdgTableRow.
56 * Any row can have an unlimited number of cells.
60 #include "adg-table.h"
61 #include "adg-table-private.h"
62 #include "adg-dress-builtins.h"
64 #include "adg-line-style.h"
65 #include "adg-toy-text.h"
68 #define PARENT_OBJECT_CLASS ((GObjectClass *) adg_table_parent_class)
69 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_table_parent_class)
77 static void dispose (GObject
*object
);
78 static void finalize (GObject
*object
);
79 static void get_property (GObject
*object
,
83 static void set_property (GObject
*object
,
87 static void global_changed (AdgEntity
*entity
);
88 static void local_changed (AdgEntity
*entity
);
89 static void invalidate (AdgEntity
*entity
);
90 static void arrange (AdgEntity
*entity
);
91 static void arrange_grid (AdgEntity
*entity
);
92 static void arrange_frame (AdgEntity
*entity
);
93 static void render (AdgEntity
*entity
,
95 static void propagate (AdgTable
*table
,
96 const gchar
*detailed_signal
,
98 static AdgTableRow
* row_new (AdgTable
*table
,
99 AdgTableRow
*before_row
);
100 static void row_arrange_size (AdgTableRow
*row
);
101 static void row_arrange (AdgTableRow
*row
);
102 static void row_dispose (AdgTableRow
*row
);
103 static void row_free (AdgTableRow
*row
);
104 static AdgTableCell
* cell_new (AdgTableRow
*row
,
105 AdgTableCell
*before_cell
,
110 static void cell_arrange_size (AdgTableCell
*cell
);
111 static void cell_arrange (AdgTableCell
*cell
);
112 static void cell_dispose (AdgTableCell
*cell
);
113 static void cell_free (AdgTableCell
*cell
);
114 static gboolean
value_match (gpointer key
,
119 G_DEFINE_TYPE(AdgTable
, adg_table
, ADG_TYPE_ENTITY
);
123 adg_table_class_init(AdgTableClass
*klass
)
125 GObjectClass
*gobject_class
;
126 AdgEntityClass
*entity_class
;
129 gobject_class
= (GObjectClass
*) klass
;
130 entity_class
= (AdgEntityClass
*) klass
;
132 g_type_class_add_private(klass
, sizeof(AdgTablePrivate
));
134 gobject_class
->dispose
= dispose
;
135 gobject_class
->finalize
= finalize
;
136 gobject_class
->get_property
= get_property
;
137 gobject_class
->set_property
= set_property
;
139 entity_class
->global_changed
= global_changed
;
140 entity_class
->local_changed
= local_changed
;
141 entity_class
->invalidate
= invalidate
;
142 entity_class
->arrange
= arrange
;
143 entity_class
->render
= render
;
145 param
= adg_param_spec_dress("table-dress",
147 P_("The dress to use for stroking this entity"),
150 g_object_class_install_property(gobject_class
, PROP_TABLE_DRESS
, param
);
154 adg_table_init(AdgTable
*table
)
156 AdgTablePrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(table
,
160 data
->table_dress
= ADG_DRESS_TABLE
;
164 data
->cell_names
= NULL
;
170 dispose(GObject
*object
)
172 AdgTablePrivate
*data
= ((AdgTable
*) object
)->data
;
174 invalidate((AdgEntity
*) object
);
176 if (data
->rows
!= NULL
)
177 g_slist_foreach(data
->rows
, (GFunc
) row_dispose
, NULL
);
179 if (PARENT_OBJECT_CLASS
->dispose
!= NULL
)
180 PARENT_OBJECT_CLASS
->dispose(object
);
184 finalize(GObject
*object
)
187 AdgTablePrivate
*data
;
189 table
= (AdgTable
*) object
;
192 if (data
->rows
!= NULL
) {
193 g_slist_foreach(data
->rows
, (GFunc
) row_free
, NULL
);
194 g_slist_free(data
->rows
);
197 if (data
->cell_names
!= NULL
)
198 g_hash_table_destroy(data
->cell_names
);
200 if (PARENT_OBJECT_CLASS
->finalize
!= NULL
)
201 PARENT_OBJECT_CLASS
->finalize(object
);
205 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
207 AdgTablePrivate
*data
= ((AdgTable
*) object
)->data
;
210 case PROP_TABLE_DRESS
:
211 g_value_set_int(value
, data
->table_dress
);
214 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
220 set_property(GObject
*object
, guint prop_id
,
221 const GValue
*value
, GParamSpec
*pspec
)
224 AdgTablePrivate
*data
;
226 table
= (AdgTable
*) object
;
230 case PROP_TABLE_DRESS
:
231 adg_dress_set(&data
->table_dress
, g_value_get_int(value
));
234 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
243 * Creates a new empty table entity. The #AdgEntity:local-mode
244 * property is set by default to #ADG_LOCAL_NONE, that is the
245 * table is not subject to any local transformations.
247 * Returns: the newly created table entity
252 return g_object_new(ADG_TYPE_TABLE
,
253 "local-mode", ADG_LOCAL_NONE
, NULL
);
257 * adg_table_get_table_dress:
258 * @table: an #AdgTable
260 * Gets the table dress to be used in rendering @table.
262 * Returns: the current table dress
265 adg_table_get_table_dress(AdgTable
*table
)
267 AdgTablePrivate
*data
;
269 g_return_val_if_fail(ADG_IS_TABLE(table
), ADG_DRESS_UNDEFINED
);
273 return data
->table_dress
;
277 * adg_table_set_table_dress:
278 * @table: an #AdgTable
279 * @dress: the new #AdgDress to use
281 * Sets a new table dress for rendering @table. The new dress
282 * must be related to the original dress for this property:
283 * you cannot set a dress used for line styles to a dress
286 * The check is done by calling adg_dress_are_related() with
287 * @dress and the previous dress as arguments. Check out its
288 * documentation for details on what is a related dress.
291 adg_table_set_table_dress(AdgTable
*table
, AdgDress dress
)
293 AdgTablePrivate
*data
;
295 g_return_if_fail(ADG_IS_TABLE(table
));
299 if (adg_dress_set(&data
->table_dress
, dress
))
300 g_object_notify((GObject
*) table
, "table-dress");
304 * adg_table_get_n_rows:
305 * @table: an #AdgTable
307 * Gets the number of rows stored in @table.
309 * Returns: the number of rows or %0 on empty @table or errors
312 adg_table_get_n_rows(AdgTable
*table
)
314 AdgTablePrivate
*data
;
316 g_return_val_if_fail(ADG_IS_TABLE(table
), 0);
320 if (data
->rows
== NULL
)
323 return g_slist_length(data
->rows
);
328 * @table: an #AdgTable
330 * Creates a new empty row and appends it at the end of the rows
331 * yet present in @table. By default, the height of this new
332 * row will be the fallback value provided by the table style:
333 * you can override it by using adg_table_row_set_height().
335 * Returns: the newly created row or %NULL on errors
338 adg_table_row_new(AdgTable
*table
)
340 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
342 return row_new(table
, NULL
);
346 * adg_table_row_new_before:
347 * @row: a valid #AdgTableRow
349 * Creates a new empty row with default height and inserts it
352 * Returns: the newly created row or %NULL on errors
355 adg_table_row_new_before(AdgTableRow
*row
)
357 g_return_val_if_fail(row
!= NULL
, NULL
);
358 g_return_val_if_fail(ADG_IS_TABLE(row
->table
), NULL
);
360 return row_new(row
->table
, row
);
364 * adg_table_row_delete:
365 * @row: a valid #AdgTableRow
367 * Removes @row from its owner table and frees every resources allocated
368 * by it. This means also the eventual cells owned by @row will be freed.
371 adg_table_row_delete(AdgTableRow
*row
)
374 AdgTablePrivate
*data
;
376 g_return_if_fail(row
!= NULL
);
380 g_return_if_fail(ADG_IS_TABLE(table
));
384 g_slist_foreach(row
->cells
, (GFunc
) cell_free
, NULL
);
385 g_slist_free(row
->cells
);
386 data
->rows
= g_slist_remove(data
->rows
, row
);
392 * adg_table_row_get_n_cells:
393 * @row: a valid #AdgTableRow
395 * Gets the number of cells stored in @row.
397 * Returns: the number of cells or %0 on empty row or errors
400 adg_table_row_get_n_cells(const AdgTableRow
*row
)
402 g_return_val_if_fail(row
!= NULL
, 0);
404 if (row
->cells
== NULL
)
407 return g_slist_length(row
->cells
);
411 * adg_table_row_set_height:
412 * @row: a valid #AdgTableRow
413 * @height: the new height
415 * Sets a new height on @row. The extents will be invalidated to
416 * recompute the whole layout of the table. Specifying %0 in
417 * @height will use the default height set in the table style.
420 adg_table_row_set_height(AdgTableRow
*row
, gdouble height
)
422 g_return_if_fail(row
!= NULL
);
424 row
->height
= height
;
426 adg_entity_invalidate((AdgEntity
*) row
->table
);
430 * adg_table_row_extents:
431 * @row: a valid #AdgTableRow
433 * Gets the extents of @row. This function is useful only after the
434 * arrange() phase as in the other situation the extents will likely
437 * Returns: the extents of @row or %NULL on errors
440 adg_table_row_extents(AdgTableRow
*row
)
442 g_return_val_if_fail(row
!= NULL
, NULL
);
444 return &row
->extents
;
448 * adg_table_get_cell_by_name:
449 * @table: an #AdgTable
450 * @name: the name of a cell
452 * Gets the cell named @name inside @table.
454 * Returns: the requested cell or %NULL if not found
457 adg_table_get_cell_by_name(AdgTable
*table
, const gchar
*name
)
459 AdgTablePrivate
*data
;
461 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
465 if (data
->cell_names
== NULL
)
468 return g_hash_table_lookup(data
->cell_names
, name
);
472 * adg_table_cell_new:
473 * @row: a valid #AdgTableRow
474 * @width: width of the cell
476 * Creates a new cell and appends it at the end of the cells
477 * yet present in @row. You can add content to the cell by using
478 * adg_table_cell_set_title() and adg_table_cell_set_value().
480 * A positive @width value specifies the width of this cell in global
481 * space: if the width of its content (that is, either the title or the
482 * value entity) will be greater than @width, it will be rendered
483 * outside the cell boundary box, luckely overwriting the adiacent
486 * Using %0 as @width means the width of the cell will be automatically
487 * adjusted to the maximum width of its content.
489 * Negative width values are not allowed: this condition will raise
490 * a warning without any further processing.
492 * Returns: the newly created cell or %NULL on errors
495 adg_table_cell_new(AdgTableRow
*row
, gdouble width
)
497 g_return_val_if_fail(row
!= NULL
, NULL
);
498 g_return_val_if_fail(width
>= 0, NULL
);
500 return cell_new(row
, NULL
, width
, NULL
, NULL
, NULL
);
504 * adg_table_cell_new_before:
505 * @cell: a valid #AdgTableCell
506 * @width: width of the cell
508 * Creates a new cell and inserts it rigthly before the @cell cell.
509 * This works similarily and accepts the same parameters as the
510 * adg_table_cell_new() function.
512 * Returns: the newly created cell or %NULL on errors
515 adg_table_cell_new_before(AdgTableCell
*cell
, gdouble width
)
517 g_return_val_if_fail(cell
!= NULL
, NULL
);
518 g_return_val_if_fail(cell
->row
!= NULL
, NULL
);
519 g_return_val_if_fail(width
>= 0, NULL
);
521 return cell_new(cell
->row
, cell
, width
, NULL
, NULL
, NULL
);
525 * adg_table_cell_new_full:
526 * @row: a valid #AdgTableRow
527 * @width: width of the cell
528 * @name: name to associate
529 * @title: title to render
530 * @value: value to render
532 * A convenient function to append a cell to @row with a specific
533 * title and value text. The font to use for rendering @title and
534 * @value will be picked up from the table style, so be sure to
535 * have the correct table dress set before calling this function.
537 * @row and @width have the same meanings as in adg_table_cell_new():
538 * check its documentation for details.
540 * @name is an optional identifier to univoquely access this cell
541 * by using adg_table_get_cell_by_name(). The identifier must be
542 * univoque: if there is yet a cell with the same name a warning
543 * message will be raised and the function will fail.
545 * Returns: the newly created cell or %NULL on errors
548 adg_table_cell_new_full(AdgTableRow
*row
, gdouble width
, const gchar
*name
,
549 const gchar
*title
, const gchar
*value
)
551 AdgEntity
*table
, *title_entity
, *value_entity
;
552 AdgTableStyle
*table_style
;
554 g_return_val_if_fail(row
!= NULL
, NULL
);
556 table
= (AdgEntity
*) row
->table
;
558 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
560 table_style
= (AdgTableStyle
*)
561 adg_entity_style(table
, adg_table_get_table_dress(row
->table
));
564 AdgDress dress
= adg_table_style_get_title_dress(table_style
);
565 title_entity
= g_object_new(ADG_TYPE_TOY_TEXT
,
566 "local-mode", ADG_LOCAL_FROM_PARENT
,
569 "parent", table
, NULL
);
575 AdgDress dress
= adg_table_style_get_value_dress(table_style
);
576 value_entity
= g_object_new(ADG_TYPE_TOY_TEXT
,
577 "local-mode", ADG_LOCAL_FROM_PARENT
,
580 "parent", table
, NULL
);
585 return cell_new(row
, NULL
, width
, name
, title_entity
, value_entity
);
589 * adg_table_cell_delete:
590 * @cell: a valid #AdgTableCell
592 * Deletes @cell removing it from the container row and freeing
593 * any resource associated to it.
596 adg_table_cell_delete(AdgTableCell
*cell
)
600 g_return_if_fail(cell
!= NULL
);
604 g_return_if_fail(row
!= NULL
);
607 row
->cells
= g_slist_remove(row
->cells
, cell
);
611 * adg_table_cell_get_title:
612 * @cell: a valid #AdgTableCell
614 * Gets the current title of @cell. The returned string is owned
615 * by @cell and must not be modified or freed.
617 * Returns: the title entity or %NULL for undefined title
620 adg_table_cell_get_title(AdgTableCell
*cell
)
622 g_return_val_if_fail(cell
!= NULL
, NULL
);
628 * adg_table_cell_set_title:
629 * @cell: a valid #AdgTableCell
630 * @title: the new title entity
632 * Sets @title as the new title entity of @cell. The top left
633 * corner of the bounding box of @title will be cohincident to
634 * the top left corner of the cell extents, taking into accounts
635 * eventual padding spaces specified by the table style.
637 * The old internal entity is unrefenrenced while the @title (if
638 * not %NULL) is refenenced with g_object_ref_sink().
640 * @title can be %NULL, in which case the old entity is removed.
643 adg_table_cell_set_title(AdgTableCell
*cell
, AdgEntity
*title
)
645 g_return_if_fail(cell
!= NULL
);
646 g_return_if_fail(title
== NULL
|| ADG_IS_ENTITY(title
));
648 if (title
== cell
->title
)
651 if (cell
->title
!= NULL
)
652 g_object_unref(cell
->title
);
656 if (cell
->title
!= NULL
)
657 g_object_ref_sink(cell
->title
);
659 /* Invalidate the whole table if the with of this cell depends
660 * on the cell content */
661 if (cell
->width
== 0)
662 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
666 * adg_table_cell_get_value:
667 * @cell: a valid #AdgTableCell
669 * Gets the current value of @cell. The returned string is owned
670 * by @cell and must not be modified or freed.
672 * Returns: the value entity or %NULL for undefined value
675 adg_table_cell_get_value(AdgTableCell
*cell
)
677 g_return_val_if_fail(cell
!= NULL
, NULL
);
683 * adg_table_cell_set_value:
684 * @cell: a valid #AdgTableCell
685 * @value: the new value entity
687 * Sets @value as the new value entity of @cell. The bottom middle
688 * point of the bounding box of @value will be cohincident to the
689 * bottom middle point of the cell extents, taking into accounts
690 * eventual padding spaces specified by the table style.
692 * The old internal entity is unrefenrenced while the @value (if
693 * not %NULL) is refenenced with g_object_ref_sink().
695 * @value can be %NULL, in which case the old entity is removed.
698 adg_table_cell_set_value(AdgTableCell
*cell
, AdgEntity
*value
)
700 g_return_if_fail(cell
!= NULL
);
701 g_return_if_fail(value
== NULL
|| ADG_IS_ENTITY(value
));
703 if (value
== cell
->value
)
706 if (cell
->value
!= NULL
)
707 g_object_unref(cell
->value
);
711 if (cell
->value
!= NULL
)
712 g_object_ref_sink(cell
->value
);
714 /* Invalidate the whole table if the with of this cell depends
715 * on the cell content */
716 if (cell
->width
== 0)
717 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
721 * adg_table_cell_set_width:
722 * @cell: a valid #AdgTableCell
723 * @width: the new width
725 * Sets a new width on @cell. The extents on the whole table
726 * will be invalidated, so will be recomputed in the next
730 adg_table_cell_set_width(AdgTableCell
*cell
, gdouble width
)
732 g_return_if_fail(cell
!= NULL
);
736 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
740 * adg_table_cell_extents:
741 * @cell: a valid #AdgTableCell
743 * Gets the extents of @cell. This function is useful only after the
744 * arrange() phase as in the other situation the extents will likely
747 * Returns: the extents of @cell or %NULL on errors
750 adg_table_cell_extents(AdgTableCell
*cell
)
752 g_return_val_if_fail(cell
!= NULL
, NULL
);
754 return &cell
->extents
;
759 global_changed(AdgEntity
*entity
)
761 PARENT_ENTITY_CLASS
->global_changed(entity
);
762 propagate((AdgTable
*) entity
, "global-changed");
766 local_changed(AdgEntity
*entity
)
768 PARENT_ENTITY_CLASS
->local_changed(entity
);
769 propagate((AdgTable
*) entity
, "local-changed");
773 invalidate(AdgEntity
*entity
)
775 propagate((AdgTable
*) entity
, "invalidate");
779 arrange(AdgEntity
*entity
)
782 AdgTablePrivate
*data
;
784 const AdgPair
*spacing
;
789 table
= (AdgTable
*) entity
;
791 cpml_extents_copy(&extents
, adg_entity_extents(entity
));
793 /* Resolve the table style */
794 if (data
->table_style
== NULL
)
795 data
->table_style
= (AdgTableStyle
*)
796 adg_entity_style(entity
, data
->table_dress
);
798 if (extents
.is_defined
) {
799 if (data
->grid
!= NULL
)
800 adg_entity_arrange((AdgEntity
*) data
->grid
);
801 if (data
->frame
!= NULL
)
802 adg_entity_arrange((AdgEntity
*) data
->frame
);
806 spacing
= adg_table_style_get_cell_spacing(data
->table_style
);
810 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
811 row
= row_node
->data
;
813 row_arrange_size(row
);
815 if (row
->extents
.size
.x
> extents
.size
.x
)
816 extents
.size
.x
= row
->extents
.size
.x
;
817 extents
.size
.y
+= row
->extents
.size
.y
;
820 /* TODO: update the org according to the table alignments */
822 y
= extents
.org
.y
+ spacing
->y
;
823 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
824 row
= row_node
->data
;
826 row
->extents
.org
.x
= extents
.org
.x
;
827 row
->extents
.org
.y
= y
;
831 y
+= row
->extents
.size
.y
+ spacing
->y
;
834 extents
.is_defined
= TRUE
;
835 adg_entity_set_extents(entity
, &extents
);
837 arrange_grid(entity
);
838 arrange_frame(entity
);
842 arrange_grid(AdgEntity
*entity
)
844 AdgTablePrivate
*data
;
847 GSList
*row_node
, *cell_node
;
853 data
= ((AdgTable
*) entity
)->data
;
855 if (data
->grid
!= NULL
)
858 path
= adg_path_new();
859 trail
= (AdgTrail
*) path
;
861 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
862 row
= row_node
->data
;
864 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
865 cell
= cell_node
->data
;
867 if (cell
->title
== NULL
&& cell
->value
== NULL
)
870 cpml_pair_copy(&pair
, &cell
->extents
.org
);
871 adg_path_move_to(path
, &pair
);
872 pair
.x
+= cell
->extents
.size
.x
;
873 adg_path_line_to(path
, &pair
);
874 pair
.y
+= cell
->extents
.size
.y
;
875 adg_path_line_to(path
, &pair
);
876 pair
.x
-= cell
->extents
.size
.x
;
877 adg_path_line_to(path
, &pair
);
878 adg_path_close(path
);
882 if (!adg_trail_extents(trail
)->is_defined
)
885 dress
= adg_table_style_get_grid_dress(data
->table_style
);
886 data
->grid
= g_object_new(ADG_TYPE_STROKE
,
887 "local-mode", ADG_LOCAL_FROM_PARENT
,
890 "parent", entity
, NULL
);
891 adg_entity_arrange((AdgEntity
*) data
->grid
);
895 arrange_frame(AdgEntity
*entity
)
897 AdgTablePrivate
*data
;
899 const CpmlExtents
*extents
;
903 data
= ((AdgTable
*) entity
)->data
;
905 if (data
->frame
!= NULL
)
908 path
= adg_path_new();
909 extents
= adg_entity_extents(entity
);
911 cpml_pair_copy(&pair
, &extents
->org
);
912 adg_path_move_to(path
, &pair
);
913 pair
.x
+= extents
->size
.x
;
914 adg_path_line_to(path
, &pair
);
915 pair
.y
+= extents
->size
.y
;
916 adg_path_line_to(path
, &pair
);
917 pair
.x
-= extents
->size
.x
;
918 adg_path_line_to(path
, &pair
);
919 adg_path_close(path
);
921 dress
= adg_table_style_get_frame_dress(data
->table_style
);
923 data
->frame
= g_object_new(ADG_TYPE_STROKE
,
924 "local-mode", ADG_LOCAL_FROM_PARENT
,
926 "trail", (AdgTrail
*) path
,
927 "parent", entity
, NULL
);
928 adg_entity_arrange((AdgEntity
*) data
->frame
);
932 render(AdgEntity
*entity
, cairo_t
*cr
)
934 cairo_set_matrix(cr
, adg_entity_ctm(entity
));
936 propagate((AdgTable
*) entity
, "render", cr
);
940 propagate(AdgTable
*table
, const gchar
*detailed_signal
, ...)
944 va_list var_args
, var_copy
;
945 AdgTablePrivate
*data
;
951 if (!g_signal_parse_name(detailed_signal
, G_TYPE_FROM_INSTANCE(table
),
952 &signal_id
, &detail
, FALSE
)) {
953 g_assert_not_reached();
956 va_start(var_args
, detailed_signal
);
959 if (data
->frame
!= NULL
) {
960 G_VA_COPY(var_copy
, var_args
);
961 g_signal_emit_valist(data
->frame
, signal_id
, detail
, var_copy
);
964 if (data
->grid
!= NULL
) {
965 G_VA_COPY(var_copy
, var_args
);
966 g_signal_emit_valist(data
->grid
, signal_id
, detail
, var_copy
);
969 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
970 row
= row_node
->data
;
972 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
973 cell
= cell_node
->data
;
975 if (cell
->title
!= NULL
) {
976 G_VA_COPY(var_copy
, var_args
);
977 g_signal_emit_valist(cell
->title
, signal_id
, detail
, var_copy
);
980 if (cell
->value
!= NULL
) {
981 G_VA_COPY(var_copy
, var_args
);
982 g_signal_emit_valist(cell
->value
, signal_id
, detail
, var_copy
);
991 row_new(AdgTable
*table
, AdgTableRow
*before_row
)
993 AdgTablePrivate
*data
;
994 AdgTableRow
*new_row
;
997 new_row
= g_new(AdgTableRow
, 1);
998 new_row
->table
= table
;
999 new_row
->cells
= NULL
;
1000 new_row
->height
= 0;
1001 new_row
->extents
.is_defined
= FALSE
;
1003 if (before_row
== NULL
) {
1004 data
->rows
= g_slist_append(data
->rows
, new_row
);
1006 GSList
*before_node
= g_slist_find(data
->rows
, before_row
);
1008 /* This MUST be present, otherwise something really bad happened */
1009 g_assert(before_node
!= NULL
);
1011 data
->rows
= g_slist_insert_before(data
->rows
, before_node
, new_row
);
1014 invalidate((AdgEntity
*) table
);
1020 row_arrange_size(AdgTableRow
*row
)
1022 AdgTableStyle
*table_style
;
1023 const AdgPair
*spacing
;
1028 table_style
= GET_TABLE_STYLE(row
->table
);
1029 spacing
= adg_table_style_get_cell_spacing(table_style
);
1030 size
= &row
->extents
.size
;
1033 if (row
->height
== 0)
1034 size
->y
= adg_table_style_get_row_height(table_style
);
1036 size
->y
= row
->height
;
1038 /* Compute the row width by summing every cell width */
1039 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1040 cell
= cell_node
->data
;
1042 cell_arrange_size(cell
);
1044 size
->x
+= cell
->extents
.size
.x
+ spacing
->x
;
1048 size
->x
+= spacing
->x
;
1051 /* Before calling this function, row->extents should be updated */
1053 row_arrange(AdgTableRow
*row
)
1055 AdgTableStyle
*table_style
;
1056 const AdgPair
*spacing
;
1062 table_style
= GET_TABLE_STYLE(row
->table
);
1063 spacing
= adg_table_style_get_cell_spacing(table_style
);
1064 org
= &row
->extents
.org
;
1065 x
= org
->x
+ spacing
->x
;
1067 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1068 cell
= cell_node
->data
;
1070 cell
->extents
.org
.x
= x
;
1071 cell
->extents
.org
.y
= org
->y
;
1075 x
+= cell
->extents
.size
.x
+ spacing
->x
;
1078 row
->extents
.is_defined
= TRUE
;
1082 row_dispose(AdgTableRow
*row
)
1084 g_slist_foreach(row
->cells
, (GFunc
) cell_dispose
, NULL
);
1088 row_free(AdgTableRow
*row
)
1090 g_slist_foreach(row
->cells
, (GFunc
) cell_free
, NULL
);
1091 g_slist_free(row
->cells
);
1096 static AdgTableCell
*
1097 cell_new(AdgTableRow
*row
, AdgTableCell
*before_cell
, gdouble width
,
1098 const gchar
*name
, AdgEntity
*title
, AdgEntity
*value
)
1100 AdgTablePrivate
*data
;
1101 AdgTableCell
*new_cell
;
1103 data
= row
->table
->data
;
1106 if (data
->cell_names
== NULL
) {
1107 data
->cell_names
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1109 } else if (g_hash_table_lookup(data
->cell_names
, name
) != NULL
) {
1110 g_warning(_("%s: `%s' cell name is yet used"),
1116 new_cell
= g_new(AdgTableCell
, 1);
1117 new_cell
->row
= row
;
1118 new_cell
->width
= width
;
1119 new_cell
->title
= title
;
1120 new_cell
->value
= value
;
1121 new_cell
->extents
.is_defined
= FALSE
;
1124 g_object_ref_sink(title
);
1127 g_object_ref_sink(value
);
1129 if (before_cell
== NULL
) {
1130 row
->cells
= g_slist_append(row
->cells
, new_cell
);
1132 GSList
*before_node
= g_slist_find(row
->cells
, before_cell
);
1134 /* This MUST be present, otherwise something really bad happened */
1135 g_assert(before_node
!= NULL
);
1137 row
->cells
= g_slist_insert_before(row
->cells
, before_node
, new_cell
);
1141 g_hash_table_insert(data
->cell_names
, g_strdup(name
), new_cell
);
1147 cell_arrange_size(AdgTableCell
*cell
)
1149 AdgTableStyle
*table_style
;
1152 table_style
= GET_TABLE_STYLE(cell
->row
->table
);
1153 size
= &cell
->extents
.size
;
1155 if (cell
->title
!= NULL
)
1156 adg_entity_arrange(cell
->title
);
1158 if (cell
->value
!= NULL
)
1159 adg_entity_arrange(cell
->value
);
1161 size
->y
= cell
->row
->extents
.size
.y
;
1163 if (cell
->width
== 0) {
1164 const CpmlVector
*content_size
;
1166 /* The width depends on the cell content (default: 0) */
1169 if (cell
->title
!= NULL
) {
1170 content_size
= &adg_entity_extents(cell
->title
)->size
;
1171 size
->x
= content_size
->x
;
1174 if (cell
->value
!= NULL
) {
1175 content_size
= &adg_entity_extents(cell
->value
)->size
;
1176 if (content_size
->x
> size
->x
)
1177 size
->x
= content_size
->x
;
1180 size
->x
+= adg_table_style_get_cell_padding(table_style
)->x
* 2;
1182 size
->x
= cell
->width
;
1186 /* Before calling this function, cell->extents should be updated */
1188 cell_arrange(AdgTableCell
*cell
)
1190 AdgTableStyle
*table_style
;
1191 const AdgPair
*padding
;
1192 const CpmlPair
*org
;
1193 const CpmlVector
*size
, *content_size
;
1196 table_style
= GET_TABLE_STYLE(cell
->row
->table
);
1197 padding
= adg_table_style_get_cell_padding(table_style
);
1198 org
= &cell
->extents
.org
;
1199 size
= &cell
->extents
.size
;
1201 if (cell
->title
!= NULL
) {
1202 content_size
= &adg_entity_extents(cell
->title
)->size
;
1204 cairo_matrix_init_translate(&map
,
1205 org
->x
+ padding
->x
,
1206 org
->y
+ content_size
->y
+ padding
->y
);
1208 adg_entity_set_global_map(cell
->title
, &map
);
1211 if (cell
->value
!= NULL
) {
1212 content_size
= &adg_entity_extents(cell
->value
)->size
;
1214 cairo_matrix_init_translate(&map
,
1215 org
->x
+ (size
->x
- content_size
->x
) / 2,
1216 org
->y
+ size
->y
- padding
->y
);
1218 adg_entity_set_global_map(cell
->value
, &map
);
1221 cell
->extents
.is_defined
= TRUE
;
1225 cell_dispose(AdgTableCell
*cell
)
1227 if (cell
->title
!= NULL
) {
1228 g_object_unref(cell
->title
);
1232 if (cell
->value
!= NULL
) {
1233 g_object_unref(cell
->value
);
1239 cell_free(AdgTableCell
*cell
)
1241 AdgTablePrivate
*data
= cell
->row
->table
->data
;
1243 g_hash_table_foreach_remove(data
->cell_names
, value_match
, cell
);
1250 value_match(gpointer key
, gpointer value
, gpointer user_data
)
1252 return value
== user_data
;