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)
78 static void dispose (GObject
*object
);
79 static void finalize (GObject
*object
);
80 static void get_property (GObject
*object
,
84 static void set_property (GObject
*object
,
88 static void global_changed (AdgEntity
*entity
);
89 static void local_changed (AdgEntity
*entity
);
90 static void invalidate (AdgEntity
*entity
);
91 static void arrange (AdgEntity
*entity
);
92 static void arrange_grid (AdgEntity
*entity
);
93 static void arrange_frame (AdgEntity
*entity
);
94 static void render (AdgEntity
*entity
,
96 static gboolean
switch_frame (AdgTable
*table
,
98 static void propagate (AdgTable
*table
,
99 const gchar
*detailed_signal
,
101 static AdgTableRow
* row_new (AdgTable
*table
,
102 AdgTableRow
*before_row
);
103 static void row_arrange_size (AdgTableRow
*row
);
104 static void row_arrange (AdgTableRow
*row
);
105 static void row_dispose (AdgTableRow
*row
);
106 static void row_free (AdgTableRow
*row
);
107 static AdgTableCell
* cell_new (AdgTableRow
*row
,
108 AdgTableCell
*before_cell
,
114 static void cell_arrange_size (AdgTableCell
*cell
);
115 static void cell_arrange (AdgTableCell
*cell
);
116 static void cell_dispose (AdgTableCell
*cell
);
117 static void cell_free (AdgTableCell
*cell
);
118 static gboolean
value_match (gpointer key
,
123 G_DEFINE_TYPE(AdgTable
, adg_table
, ADG_TYPE_ENTITY
);
127 adg_table_class_init(AdgTableClass
*klass
)
129 GObjectClass
*gobject_class
;
130 AdgEntityClass
*entity_class
;
133 gobject_class
= (GObjectClass
*) klass
;
134 entity_class
= (AdgEntityClass
*) klass
;
136 g_type_class_add_private(klass
, sizeof(AdgTablePrivate
));
138 gobject_class
->dispose
= dispose
;
139 gobject_class
->finalize
= finalize
;
140 gobject_class
->get_property
= get_property
;
141 gobject_class
->set_property
= set_property
;
143 entity_class
->global_changed
= global_changed
;
144 entity_class
->local_changed
= local_changed
;
145 entity_class
->invalidate
= invalidate
;
146 entity_class
->arrange
= arrange
;
147 entity_class
->render
= render
;
149 param
= adg_param_spec_dress("table-dress",
151 P_("The dress to use for stroking this entity"),
154 g_object_class_install_property(gobject_class
, PROP_TABLE_DRESS
, param
);
156 param
= g_param_spec_boolean("has-frame",
157 P_("Has Frame Flag"),
158 P_("If enabled, a frame using the proper dress found in this table style will be drawn around the table extents"),
161 g_object_class_install_property(gobject_class
, PROP_HAS_FRAME
, param
);
165 adg_table_init(AdgTable
*table
)
167 AdgTablePrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(table
,
171 data
->table_dress
= ADG_DRESS_TABLE
;
172 data
->has_frame
= TRUE
;
174 data
->table_style
= NULL
;
178 data
->cell_names
= NULL
;
184 dispose(GObject
*object
)
186 AdgTablePrivate
*data
= ((AdgTable
*) object
)->data
;
188 invalidate((AdgEntity
*) object
);
190 if (data
->grid
!= NULL
) {
191 g_object_unref(data
->grid
);
195 if (data
->frame
!= NULL
) {
196 g_object_unref(data
->frame
);
200 /* The rows finalization will happen in the finalize() method */
201 if (data
->rows
!= NULL
)
202 g_slist_foreach(data
->rows
, (GFunc
) row_dispose
, NULL
);
204 if (PARENT_OBJECT_CLASS
->dispose
!= NULL
)
205 PARENT_OBJECT_CLASS
->dispose(object
);
209 finalize(GObject
*object
)
212 AdgTablePrivate
*data
;
214 table
= (AdgTable
*) object
;
217 if (data
->rows
!= NULL
) {
218 g_slist_foreach(data
->rows
, (GFunc
) row_free
, NULL
);
219 g_slist_free(data
->rows
);
222 if (data
->cell_names
!= NULL
)
223 g_hash_table_destroy(data
->cell_names
);
225 if (PARENT_OBJECT_CLASS
->finalize
!= NULL
)
226 PARENT_OBJECT_CLASS
->finalize(object
);
230 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
232 AdgTablePrivate
*data
= ((AdgTable
*) object
)->data
;
235 case PROP_TABLE_DRESS
:
236 g_value_set_int(value
, data
->table_dress
);
239 g_value_set_boolean(value
, data
->has_frame
);
242 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
248 set_property(GObject
*object
, guint prop_id
,
249 const GValue
*value
, GParamSpec
*pspec
)
252 AdgTablePrivate
*data
;
254 table
= (AdgTable
*) object
;
258 case PROP_TABLE_DRESS
:
259 adg_dress_set(&data
->table_dress
, g_value_get_int(value
));
262 switch_frame(table
, g_value_get_boolean(value
));
265 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
274 * Creates a new empty table entity. The #AdgEntity:local-method
275 * property is set by default to #ADG_MIX_DISABLED, that is the
276 * table is not subject to any local transformations.
278 * Returns: the newly created table entity
283 return g_object_new(ADG_TYPE_TABLE
,
284 "local-method", ADG_MIX_DISABLED
, NULL
);
288 * adg_table_get_table_dress:
289 * @table: an #AdgTable
291 * Gets the table dress to be used in rendering @table.
293 * Returns: the current table dress
296 adg_table_get_table_dress(AdgTable
*table
)
298 AdgTablePrivate
*data
;
300 g_return_val_if_fail(ADG_IS_TABLE(table
), ADG_DRESS_UNDEFINED
);
304 return data
->table_dress
;
308 * adg_table_set_table_dress:
309 * @table: an #AdgTable
310 * @dress: the new #AdgDress to use
312 * Sets a new table dress for rendering @table. The new dress
313 * must be related to the original dress for this property:
314 * you cannot set a dress used for line styles to a dress
317 * The check is done by calling adg_dress_are_related() with
318 * @dress and the previous dress as arguments. Check out its
319 * documentation for details on what is a related dress.
322 adg_table_set_table_dress(AdgTable
*table
, AdgDress dress
)
324 AdgTablePrivate
*data
;
326 g_return_if_fail(ADG_IS_TABLE(table
));
330 if (adg_dress_set(&data
->table_dress
, dress
))
331 g_object_notify((GObject
*) table
, "table-dress");
335 * adg_table_has_frame:
336 * @table: an #AdgTable
338 * Returns the state of the #AdgTable:has-frame property.
340 * Returns: the current state
343 adg_table_has_frame(AdgTable
*table
)
345 AdgTablePrivate
*data
;
347 g_return_val_if_fail(ADG_IS_TABLE(table
), FALSE
);
351 return data
->has_frame
;
355 * adg_table_switch_frame:
356 * @table: an #AdgTable
357 * @state: the new state of the frame
359 * Sets the #AdgTable:has-frame property: %TRUE will draw a
360 * frame around the whole table using the #AdgTableStyle:frame-dress
361 * dress of the table style.
364 adg_table_switch_frame(AdgTable
*table
, gboolean state
)
366 g_return_if_fail(ADG_IS_TABLE(table
));
368 if (switch_frame(table
, state
))
369 g_object_notify((GObject
*) table
, "has-frame");
373 * adg_table_get_n_rows:
374 * @table: an #AdgTable
376 * Gets the number of rows stored in @table.
378 * Returns: the number of rows or %0 on empty @table or errors
381 adg_table_get_n_rows(AdgTable
*table
)
383 AdgTablePrivate
*data
;
385 g_return_val_if_fail(ADG_IS_TABLE(table
), 0);
389 if (data
->rows
== NULL
)
392 return g_slist_length(data
->rows
);
397 * @table: an #AdgTable
399 * Creates a new empty row and appends it at the end of the rows
400 * yet present in @table. By default, the height of this new
401 * row will be the fallback value provided by the table style:
402 * you can override it by using adg_table_row_set_height().
404 * Returns: the newly created row or %NULL on errors
407 adg_table_row_new(AdgTable
*table
)
409 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
411 return row_new(table
, NULL
);
415 * adg_table_row_new_before:
416 * @row: a valid #AdgTableRow
418 * Creates a new empty row with default height and inserts it
421 * Returns: the newly created row or %NULL on errors
424 adg_table_row_new_before(AdgTableRow
*row
)
426 g_return_val_if_fail(row
!= NULL
, NULL
);
427 g_return_val_if_fail(ADG_IS_TABLE(row
->table
), NULL
);
429 return row_new(row
->table
, row
);
433 * adg_table_row_delete:
434 * @row: a valid #AdgTableRow
436 * Removes @row from its owner table and frees every resources allocated
437 * by it. This means also the eventual cells owned by @row will be freed.
440 adg_table_row_delete(AdgTableRow
*row
)
443 AdgTablePrivate
*data
;
445 g_return_if_fail(row
!= NULL
);
449 g_return_if_fail(ADG_IS_TABLE(table
));
453 g_slist_foreach(row
->cells
, (GFunc
) cell_free
, NULL
);
454 g_slist_free(row
->cells
);
455 data
->rows
= g_slist_remove(data
->rows
, row
);
461 * adg_table_row_get_n_cells:
462 * @row: a valid #AdgTableRow
464 * Gets the number of cells stored in @row.
466 * Returns: the number of cells or %0 on empty row or errors
469 adg_table_row_get_n_cells(const AdgTableRow
*row
)
471 g_return_val_if_fail(row
!= NULL
, 0);
473 if (row
->cells
== NULL
)
476 return g_slist_length(row
->cells
);
480 * adg_table_row_set_height:
481 * @row: a valid #AdgTableRow
482 * @height: the new height
484 * Sets a new height on @row. The extents will be invalidated to
485 * recompute the whole layout of the table. Specifying %0 in
486 * @height will use the default height set in the table style.
489 adg_table_row_set_height(AdgTableRow
*row
, gdouble height
)
491 g_return_if_fail(row
!= NULL
);
493 row
->height
= height
;
495 adg_entity_invalidate((AdgEntity
*) row
->table
);
499 * adg_table_row_extents:
500 * @row: a valid #AdgTableRow
502 * Gets the extents of @row. This function is useful only after the
503 * arrange() phase as in the other situation the extents will likely
506 * Returns: the extents of @row or %NULL on errors
509 adg_table_row_extents(AdgTableRow
*row
)
511 g_return_val_if_fail(row
!= NULL
, NULL
);
513 return &row
->extents
;
517 * adg_table_get_cell_by_name:
518 * @table: an #AdgTable
519 * @name: the name of a cell
521 * Gets the cell named @name inside @table.
523 * Returns: the requested cell or %NULL if not found
526 adg_table_get_cell_by_name(AdgTable
*table
, const gchar
*name
)
528 AdgTablePrivate
*data
;
530 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
534 if (data
->cell_names
== NULL
)
537 return g_hash_table_lookup(data
->cell_names
, name
);
541 * adg_table_cell_new:
542 * @row: a valid #AdgTableRow
543 * @width: width of the cell
545 * Creates a new empty cell without a frame and appends it at the
546 * end of the cells yet present in @row. You can add content to the
547 * cell by using adg_table_cell_set_title() and
548 * adg_table_cell_set_value() or enable the frame with
549 * adg_table_cell_switch_frame().
551 * A positive @width value specifies the width of this cell in global
552 * space: if the width of its content (that is, either the title or the
553 * value entity) will be greater than @width, it will be rendered
554 * outside the cell boundary box, luckely overwriting the adiacent
557 * Using %0 as @width means the width of the cell will be automatically
558 * adjusted to the maximum width of its content.
560 * Negative width values are not allowed: this condition will raise
561 * a warning without any further processing.
563 * Returns: the newly created cell or %NULL on errors
566 adg_table_cell_new(AdgTableRow
*row
, gdouble width
)
568 g_return_val_if_fail(row
!= NULL
, NULL
);
569 g_return_val_if_fail(width
>= 0, NULL
);
571 return cell_new(row
, NULL
, width
, FALSE
, NULL
, NULL
, NULL
);
575 * adg_table_cell_new_before:
576 * @cell: a valid #AdgTableCell
577 * @width: width of the cell
579 * Creates a new cell and inserts it rigthly before the @cell cell.
580 * This works similarily and accepts the same parameters as the
581 * adg_table_cell_new() function.
583 * Returns: the newly created cell or %NULL on errors
586 adg_table_cell_new_before(AdgTableCell
*cell
, gdouble width
)
588 g_return_val_if_fail(cell
!= NULL
, NULL
);
589 g_return_val_if_fail(cell
->row
!= NULL
, NULL
);
590 g_return_val_if_fail(width
>= 0, NULL
);
592 return cell_new(cell
->row
, cell
, width
, FALSE
, NULL
, NULL
, NULL
);
596 * adg_table_cell_new_full:
597 * @row: a valid #AdgTableRow
598 * @width: width of the cell
599 * @name: name to associate
600 * @title: title to render
601 * @value: value to render
603 * A convenient function to append a framed cell to @row with a
604 * specific title and value text. The font to use for rendering
605 * @title and @value will be picked up from the table style, so
606 * be sure to have the correct table dress set before calling
609 * @row and @width have the same meanings as in adg_table_cell_new():
610 * check its documentation for details.
612 * @name is an optional identifier to univoquely access this cell
613 * by using adg_table_get_cell_by_name(). The identifier must be
614 * univoque: if there is yet a cell with the same name a warning
615 * message will be raised and the function will fail.
617 * Returns: the newly created cell or %NULL on errors
620 adg_table_cell_new_full(AdgTableRow
*row
, gdouble width
, const gchar
*name
,
621 const gchar
*title
, const gchar
*value
)
623 AdgEntity
*table
, *title_entity
, *value_entity
;
624 AdgTableStyle
*table_style
;
626 g_return_val_if_fail(row
!= NULL
, NULL
);
628 table
= (AdgEntity
*) row
->table
;
630 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
632 table_style
= (AdgTableStyle
*)
633 adg_entity_style(table
, adg_table_get_table_dress(row
->table
));
636 AdgDress dress
= adg_table_style_get_title_dress(table_style
);
637 title_entity
= g_object_new(ADG_TYPE_TOY_TEXT
,
638 "local-method", ADG_MIX_PARENT
,
641 "parent", table
, NULL
);
647 AdgDress dress
= adg_table_style_get_value_dress(table_style
);
648 value_entity
= g_object_new(ADG_TYPE_TOY_TEXT
,
649 "local-method", ADG_MIX_PARENT
,
652 "parent", table
, NULL
);
657 return cell_new(row
, NULL
, width
, TRUE
, name
, title_entity
, value_entity
);
661 * adg_table_cell_delete:
662 * @cell: a valid #AdgTableCell
664 * Deletes @cell removing it from the container row and freeing
665 * any resource associated to it.
668 adg_table_cell_delete(AdgTableCell
*cell
)
672 g_return_if_fail(cell
!= NULL
);
676 g_return_if_fail(row
!= NULL
);
679 row
->cells
= g_slist_remove(row
->cells
, cell
);
683 * adg_table_cell_get_title:
684 * @cell: a valid #AdgTableCell
686 * Gets the current title of @cell. The returned string is owned
687 * by @cell and must not be modified or freed.
689 * Returns: the title entity or %NULL for undefined title
692 adg_table_cell_get_title(AdgTableCell
*cell
)
694 g_return_val_if_fail(cell
!= NULL
, NULL
);
700 * adg_table_cell_set_title:
701 * @cell: a valid #AdgTableCell
702 * @title: the new title entity
704 * Sets @title as the new title entity of @cell. The top left
705 * corner of the bounding box of @title will be cohincident to
706 * the top left corner of the cell extents, taking into accounts
707 * eventual padding spaces specified by the table style.
709 * The old internal entity is unrefenrenced while the @title (if
710 * not %NULL) is refenenced with g_object_ref_sink().
712 * @title can be %NULL, in which case the old entity is removed.
715 adg_table_cell_set_title(AdgTableCell
*cell
, AdgEntity
*title
)
717 g_return_if_fail(cell
!= NULL
);
718 g_return_if_fail(title
== NULL
|| ADG_IS_ENTITY(title
));
720 if (title
== cell
->title
)
723 if (cell
->title
!= NULL
)
724 g_object_unref(cell
->title
);
728 if (cell
->title
!= NULL
)
729 g_object_ref_sink(cell
->title
);
731 /* Invalidate the whole table if the with of this cell depends
732 * on the cell content */
733 if (cell
->width
== 0)
734 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
738 * adg_table_cell_get_value:
739 * @cell: a valid #AdgTableCell
741 * Gets the current value of @cell. The returned string is owned
742 * by @cell and must not be modified or freed.
744 * Returns: the value entity or %NULL for undefined value
747 adg_table_cell_get_value(AdgTableCell
*cell
)
749 g_return_val_if_fail(cell
!= NULL
, NULL
);
755 * adg_table_cell_set_value:
756 * @cell: a valid #AdgTableCell
757 * @value: the new value entity
759 * Sets @value as the new value entity of @cell. The bottom middle
760 * point of the bounding box of @value will be cohincident to the
761 * bottom middle point of the cell extents, taking into accounts
762 * eventual padding spaces specified by the table style.
764 * The old internal entity is unrefenrenced while the @value (if
765 * not %NULL) is refenenced with g_object_ref_sink().
767 * @value can be %NULL, in which case the old entity is removed.
770 adg_table_cell_set_value(AdgTableCell
*cell
, AdgEntity
*value
)
772 g_return_if_fail(cell
!= NULL
);
773 g_return_if_fail(value
== NULL
|| ADG_IS_ENTITY(value
));
775 if (value
== cell
->value
)
778 if (cell
->value
!= NULL
)
779 g_object_unref(cell
->value
);
783 if (cell
->value
!= NULL
)
784 g_object_ref_sink(cell
->value
);
786 /* Invalidate the whole table if the with of this cell depends
787 * on the cell content */
788 if (cell
->width
== 0)
789 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
793 * adg_table_cell_set_width:
794 * @cell: a valid #AdgTableCell
795 * @width: the new width
797 * Sets a new width on @cell. The extents on the whole table
798 * will be invalidated, so will be recomputed in the next
802 adg_table_cell_set_width(AdgTableCell
*cell
, gdouble width
)
804 g_return_if_fail(cell
!= NULL
);
808 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
812 * adg_table_cell_has_frame:
813 * @cell: a valid #AdgTableCell
815 * Gets the frame flag of @cell.
817 * Returns: the frame flag
820 adg_table_cell_has_frame(AdgTableCell
*cell
)
822 g_return_val_if_fail(cell
!= NULL
, FALSE
);
824 return cell
->has_frame
;
828 * adg_table_cell_switch_frame:
829 * @cell: a valid #AdgTableCell
830 * @state: the new frame state
832 * Sets the frame flag of @cell: if @state is %TRUE, a frame around
833 * @cell will be rendered using the #AdgTableStyle:cell-dress dress
834 * of the table style.
837 adg_table_cell_switch_frame(AdgTableCell
*cell
, gboolean state
)
839 AdgTablePrivate
*data
;
841 g_return_if_fail(cell
!= NULL
);
843 if (cell
->has_frame
== state
)
846 data
= cell
->row
->table
->data
;
847 cell
->has_frame
= state
;
849 if (data
->grid
!= NULL
) {
850 g_object_unref(data
->grid
);
856 * adg_table_cell_extents:
857 * @cell: a valid #AdgTableCell
859 * Gets the extents of @cell. This function is useful only after the
860 * arrange() phase as in the other situation the extents will likely
863 * Returns: the extents of @cell or %NULL on errors
866 adg_table_cell_extents(AdgTableCell
*cell
)
868 g_return_val_if_fail(cell
!= NULL
, NULL
);
870 return &cell
->extents
;
875 global_changed(AdgEntity
*entity
)
877 PARENT_ENTITY_CLASS
->global_changed(entity
);
878 propagate((AdgTable
*) entity
, "global-changed");
882 local_changed(AdgEntity
*entity
)
884 PARENT_ENTITY_CLASS
->local_changed(entity
);
885 propagate((AdgTable
*) entity
, "local-changed");
889 invalidate(AdgEntity
*entity
)
891 propagate((AdgTable
*) entity
, "invalidate");
895 arrange(AdgEntity
*entity
)
898 AdgTablePrivate
*data
;
900 const AdgPair
*spacing
;
905 table
= (AdgTable
*) entity
;
907 cpml_extents_copy(&extents
, adg_entity_extents(entity
));
909 /* Resolve the table style */
910 if (data
->table_style
== NULL
)
911 data
->table_style
= (AdgTableStyle
*)
912 adg_entity_style(entity
, data
->table_dress
);
914 if (extents
.is_defined
) {
915 if (data
->grid
!= NULL
)
916 adg_entity_arrange((AdgEntity
*) data
->grid
);
917 if (data
->frame
!= NULL
)
918 adg_entity_arrange((AdgEntity
*) data
->frame
);
922 spacing
= adg_table_style_get_cell_spacing(data
->table_style
);
926 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
927 row
= row_node
->data
;
929 row_arrange_size(row
);
931 if (row
->extents
.size
.x
> extents
.size
.x
)
932 extents
.size
.x
= row
->extents
.size
.x
;
933 extents
.size
.y
+= row
->extents
.size
.y
;
936 /* TODO: update the org according to the table alignments */
938 y
= extents
.org
.y
+ spacing
->y
;
939 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
940 row
= row_node
->data
;
942 row
->extents
.org
.x
= extents
.org
.x
;
943 row
->extents
.org
.y
= y
;
947 y
+= row
->extents
.size
.y
+ spacing
->y
;
950 extents
.is_defined
= TRUE
;
951 adg_entity_set_extents(entity
, &extents
);
953 arrange_grid(entity
);
954 arrange_frame(entity
);
958 arrange_grid(AdgEntity
*entity
)
960 AdgTablePrivate
*data
;
963 GSList
*row_node
, *cell_node
;
969 data
= ((AdgTable
*) entity
)->data
;
971 if (data
->grid
!= NULL
)
974 path
= adg_path_new();
975 trail
= (AdgTrail
*) path
;
977 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
978 row
= row_node
->data
;
980 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
981 cell
= cell_node
->data
;
983 if (!cell
->has_frame
)
986 cpml_pair_copy(&pair
, &cell
->extents
.org
);
987 adg_path_move_to(path
, &pair
);
988 pair
.x
+= cell
->extents
.size
.x
;
989 adg_path_line_to(path
, &pair
);
990 pair
.y
+= cell
->extents
.size
.y
;
991 adg_path_line_to(path
, &pair
);
992 pair
.x
-= cell
->extents
.size
.x
;
993 adg_path_line_to(path
, &pair
);
994 adg_path_close(path
);
998 if (!adg_trail_extents(trail
)->is_defined
)
1001 dress
= adg_table_style_get_grid_dress(data
->table_style
);
1002 data
->grid
= g_object_new(ADG_TYPE_STROKE
,
1003 "local-method", ADG_MIX_PARENT
,
1004 "line-dress", dress
,
1006 "parent", entity
, NULL
);
1007 adg_entity_arrange((AdgEntity
*) data
->grid
);
1011 arrange_frame(AdgEntity
*entity
)
1013 AdgTablePrivate
*data
;
1015 const CpmlExtents
*extents
;
1019 data
= ((AdgTable
*) entity
)->data
;
1021 if (data
->frame
!= NULL
|| !data
->has_frame
)
1024 path
= adg_path_new();
1025 extents
= adg_entity_extents(entity
);
1027 cpml_pair_copy(&pair
, &extents
->org
);
1028 adg_path_move_to(path
, &pair
);
1029 pair
.x
+= extents
->size
.x
;
1030 adg_path_line_to(path
, &pair
);
1031 pair
.y
+= extents
->size
.y
;
1032 adg_path_line_to(path
, &pair
);
1033 pair
.x
-= extents
->size
.x
;
1034 adg_path_line_to(path
, &pair
);
1035 adg_path_close(path
);
1037 dress
= adg_table_style_get_frame_dress(data
->table_style
);
1039 data
->frame
= g_object_new(ADG_TYPE_STROKE
,
1040 "local-method", ADG_MIX_PARENT
,
1041 "line-dress", dress
,
1042 "trail", (AdgTrail
*) path
,
1043 "parent", entity
, NULL
);
1044 adg_entity_arrange((AdgEntity
*) data
->frame
);
1048 render(AdgEntity
*entity
, cairo_t
*cr
)
1050 cairo_set_matrix(cr
, adg_entity_ctm(entity
));
1052 propagate((AdgTable
*) entity
, "render", cr
);
1056 switch_frame(AdgTable
*table
, gboolean state
)
1058 AdgTablePrivate
*data
= table
->data
;
1060 if (data
->has_frame
== state
)
1063 data
->has_frame
= state
;
1065 if (data
->frame
!= NULL
) {
1066 g_object_unref(data
->frame
);
1074 propagate(AdgTable
*table
, const gchar
*detailed_signal
, ...)
1078 va_list var_args
, var_copy
;
1079 AdgTablePrivate
*data
;
1085 if (!g_signal_parse_name(detailed_signal
, G_TYPE_FROM_INSTANCE(table
),
1086 &signal_id
, &detail
, FALSE
)) {
1087 g_assert_not_reached();
1090 va_start(var_args
, detailed_signal
);
1093 if (data
->frame
!= NULL
) {
1094 G_VA_COPY(var_copy
, var_args
);
1095 g_signal_emit_valist(data
->frame
, signal_id
, detail
, var_copy
);
1098 if (data
->grid
!= NULL
) {
1099 G_VA_COPY(var_copy
, var_args
);
1100 g_signal_emit_valist(data
->grid
, signal_id
, detail
, var_copy
);
1103 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1104 row
= row_node
->data
;
1106 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1107 cell
= cell_node
->data
;
1109 if (cell
->title
!= NULL
) {
1110 G_VA_COPY(var_copy
, var_args
);
1111 g_signal_emit_valist(cell
->title
, signal_id
, detail
, var_copy
);
1114 if (cell
->value
!= NULL
) {
1115 G_VA_COPY(var_copy
, var_args
);
1116 g_signal_emit_valist(cell
->value
, signal_id
, detail
, var_copy
);
1124 static AdgTableRow
*
1125 row_new(AdgTable
*table
, AdgTableRow
*before_row
)
1127 AdgTablePrivate
*data
;
1128 AdgTableRow
*new_row
;
1131 new_row
= g_new(AdgTableRow
, 1);
1132 new_row
->table
= table
;
1133 new_row
->cells
= NULL
;
1134 new_row
->height
= 0;
1135 new_row
->extents
.is_defined
= FALSE
;
1137 if (before_row
== NULL
) {
1138 data
->rows
= g_slist_append(data
->rows
, new_row
);
1140 GSList
*before_node
= g_slist_find(data
->rows
, before_row
);
1142 /* This MUST be present, otherwise something really bad happened */
1143 g_assert(before_node
!= NULL
);
1145 data
->rows
= g_slist_insert_before(data
->rows
, before_node
, new_row
);
1148 invalidate((AdgEntity
*) table
);
1154 row_arrange_size(AdgTableRow
*row
)
1156 AdgTableStyle
*table_style
;
1157 const AdgPair
*spacing
;
1162 table_style
= GET_TABLE_STYLE(row
->table
);
1163 spacing
= adg_table_style_get_cell_spacing(table_style
);
1164 size
= &row
->extents
.size
;
1167 if (row
->height
== 0)
1168 size
->y
= adg_table_style_get_row_height(table_style
);
1170 size
->y
= row
->height
;
1172 /* Compute the row width by summing every cell width */
1173 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1174 cell
= cell_node
->data
;
1176 cell_arrange_size(cell
);
1178 size
->x
+= cell
->extents
.size
.x
+ spacing
->x
;
1182 size
->x
+= spacing
->x
;
1185 /* Before calling this function, row->extents should be updated */
1187 row_arrange(AdgTableRow
*row
)
1189 AdgTableStyle
*table_style
;
1190 const AdgPair
*spacing
;
1196 table_style
= GET_TABLE_STYLE(row
->table
);
1197 spacing
= adg_table_style_get_cell_spacing(table_style
);
1198 org
= &row
->extents
.org
;
1199 x
= org
->x
+ spacing
->x
;
1201 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1202 cell
= cell_node
->data
;
1204 cell
->extents
.org
.x
= x
;
1205 cell
->extents
.org
.y
= org
->y
;
1209 x
+= cell
->extents
.size
.x
+ spacing
->x
;
1212 row
->extents
.is_defined
= TRUE
;
1216 row_dispose(AdgTableRow
*row
)
1218 g_slist_foreach(row
->cells
, (GFunc
) cell_dispose
, NULL
);
1222 row_free(AdgTableRow
*row
)
1224 g_slist_foreach(row
->cells
, (GFunc
) cell_free
, NULL
);
1225 g_slist_free(row
->cells
);
1230 static AdgTableCell
*
1231 cell_new(AdgTableRow
*row
, AdgTableCell
*before_cell
,
1232 gdouble width
, gboolean has_frame
,
1233 const gchar
*name
, AdgEntity
*title
, AdgEntity
*value
)
1235 AdgTablePrivate
*data
;
1236 AdgTableCell
*new_cell
;
1238 data
= row
->table
->data
;
1241 if (data
->cell_names
== NULL
) {
1242 data
->cell_names
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1244 } else if (g_hash_table_lookup(data
->cell_names
, name
) != NULL
) {
1245 g_warning(_("%s: `%s' cell name is yet used"),
1251 new_cell
= g_new(AdgTableCell
, 1);
1252 new_cell
->row
= row
;
1253 new_cell
->width
= width
;
1254 new_cell
->has_frame
= has_frame
;
1255 new_cell
->title
= title
;
1256 new_cell
->value
= value
;
1257 new_cell
->extents
.is_defined
= FALSE
;
1260 g_object_ref_sink(title
);
1263 g_object_ref_sink(value
);
1265 if (before_cell
== NULL
) {
1266 row
->cells
= g_slist_append(row
->cells
, new_cell
);
1268 GSList
*before_node
= g_slist_find(row
->cells
, before_cell
);
1270 /* This MUST be present, otherwise something really bad happened */
1271 g_assert(before_node
!= NULL
);
1273 row
->cells
= g_slist_insert_before(row
->cells
, before_node
, new_cell
);
1277 g_hash_table_insert(data
->cell_names
, g_strdup(name
), new_cell
);
1283 cell_arrange_size(AdgTableCell
*cell
)
1285 AdgTableStyle
*table_style
;
1288 table_style
= GET_TABLE_STYLE(cell
->row
->table
);
1289 size
= &cell
->extents
.size
;
1291 if (cell
->title
!= NULL
)
1292 adg_entity_arrange(cell
->title
);
1294 if (cell
->value
!= NULL
)
1295 adg_entity_arrange(cell
->value
);
1297 size
->y
= cell
->row
->extents
.size
.y
;
1299 if (cell
->width
== 0) {
1300 const CpmlVector
*content_size
;
1302 /* The width depends on the cell content (default: 0) */
1305 if (cell
->title
!= NULL
) {
1306 content_size
= &adg_entity_extents(cell
->title
)->size
;
1307 size
->x
= content_size
->x
;
1310 if (cell
->value
!= NULL
) {
1311 content_size
= &adg_entity_extents(cell
->value
)->size
;
1312 if (content_size
->x
> size
->x
)
1313 size
->x
= content_size
->x
;
1316 size
->x
+= adg_table_style_get_cell_padding(table_style
)->x
* 2;
1318 size
->x
= cell
->width
;
1322 /* Before calling this function, cell->extents should be updated */
1324 cell_arrange(AdgTableCell
*cell
)
1326 AdgTableStyle
*table_style
;
1327 const AdgPair
*padding
;
1328 const CpmlPair
*org
;
1329 const CpmlVector
*size
, *content_size
;
1332 table_style
= GET_TABLE_STYLE(cell
->row
->table
);
1333 padding
= adg_table_style_get_cell_padding(table_style
);
1334 org
= &cell
->extents
.org
;
1335 size
= &cell
->extents
.size
;
1337 if (cell
->title
!= NULL
) {
1338 content_size
= &adg_entity_extents(cell
->title
)->size
;
1340 cairo_matrix_init_translate(&map
,
1341 org
->x
+ padding
->x
,
1342 org
->y
+ content_size
->y
+ padding
->y
);
1344 adg_entity_set_global_map(cell
->title
, &map
);
1347 if (cell
->value
!= NULL
) {
1348 content_size
= &adg_entity_extents(cell
->value
)->size
;
1350 cairo_matrix_init_translate(&map
,
1351 org
->x
+ (size
->x
- content_size
->x
) / 2,
1352 org
->y
+ size
->y
- padding
->y
);
1354 adg_entity_set_global_map(cell
->value
, &map
);
1357 cell
->extents
.is_defined
= TRUE
;
1361 cell_dispose(AdgTableCell
*cell
)
1363 if (cell
->title
!= NULL
) {
1364 g_object_unref(cell
->title
);
1368 if (cell
->value
!= NULL
) {
1369 g_object_unref(cell
->value
);
1375 cell_free(AdgTableCell
*cell
)
1377 AdgTablePrivate
*data
= cell
->row
->table
->data
;
1379 g_hash_table_foreach_remove(data
->cell_names
, value_match
, cell
);
1386 value_match(gpointer key
, gpointer value
, gpointer user_data
)
1388 return value
== user_data
;