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 PARENT_OBJECT_CLASS ((GObjectClass *) adg_table_parent_class)
84 #define PARENT_ENTITY_CLASS ((AdgEntityClass *) adg_table_parent_class)
93 static void dispose (GObject
*object
);
94 static void finalize (GObject
*object
);
95 static void get_property (GObject
*object
,
99 static void set_property (GObject
*object
,
103 static void global_changed (AdgEntity
*entity
);
104 static void local_changed (AdgEntity
*entity
);
105 static void invalidate (AdgEntity
*entity
);
106 static void arrange (AdgEntity
*entity
);
107 static void arrange_grid (AdgEntity
*entity
);
108 static void arrange_frame (AdgEntity
*entity
);
109 static void render (AdgEntity
*entity
,
111 static gboolean
switch_frame (AdgTable
*table
,
113 static void propagate (AdgTable
*table
,
114 const gchar
*detailed_signal
,
116 static AdgTableRow
* row_new (AdgTable
*table
,
117 AdgTableRow
*before_row
);
118 static void row_arrange_size (AdgTableRow
*row
);
119 static void row_arrange (AdgTableRow
*row
);
120 static void row_dispose (AdgTableRow
*row
);
121 static void row_free (AdgTableRow
*row
);
122 static AdgTableCell
* cell_new (AdgTableRow
*row
,
123 AdgTableCell
*before_cell
,
129 static void cell_set_name (AdgTableCell
*cell
,
131 static gboolean
cell_set_title (AdgTableCell
*cell
,
133 static gboolean
cell_set_value (AdgTableCell
*cell
,
135 static void cell_set_value_pos (AdgTableCell
*cell
,
136 const AdgPair
*from_factor
,
137 const AdgPair
*to_factor
);
138 static void cell_arrange_size (AdgTableCell
*cell
);
139 static void cell_arrange (AdgTableCell
*cell
);
140 static void cell_dispose (AdgTableCell
*cell
);
141 static void cell_free (AdgTableCell
*cell
);
142 static gboolean
value_match (gpointer key
,
147 G_DEFINE_TYPE(AdgTable
, adg_table
, ADG_TYPE_ENTITY
);
151 adg_table_class_init(AdgTableClass
*klass
)
153 GObjectClass
*gobject_class
;
154 AdgEntityClass
*entity_class
;
157 gobject_class
= (GObjectClass
*) klass
;
158 entity_class
= (AdgEntityClass
*) klass
;
160 g_type_class_add_private(klass
, sizeof(AdgTablePrivate
));
162 gobject_class
->dispose
= dispose
;
163 gobject_class
->finalize
= finalize
;
164 gobject_class
->get_property
= get_property
;
165 gobject_class
->set_property
= set_property
;
167 entity_class
->global_changed
= global_changed
;
168 entity_class
->local_changed
= local_changed
;
169 entity_class
->invalidate
= invalidate
;
170 entity_class
->arrange
= arrange
;
171 entity_class
->render
= render
;
173 param
= adg_param_spec_dress("table-dress",
175 P_("The dress to use for stroking this entity"),
178 g_object_class_install_property(gobject_class
, PROP_TABLE_DRESS
, param
);
180 param
= g_param_spec_boolean("has-frame",
181 P_("Has Frame Flag"),
182 P_("If enabled, a frame using the proper dress found in this table style will be drawn around the table extents"),
185 g_object_class_install_property(gobject_class
, PROP_HAS_FRAME
, param
);
189 adg_table_init(AdgTable
*table
)
191 AdgTablePrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(table
,
195 data
->table_dress
= ADG_DRESS_TABLE
;
196 data
->has_frame
= TRUE
;
198 data
->table_style
= NULL
;
202 data
->cell_names
= NULL
;
208 dispose(GObject
*object
)
210 AdgTablePrivate
*data
= ((AdgTable
*) object
)->data
;
212 if (data
->grid
!= NULL
) {
213 g_object_unref(data
->grid
);
217 if (data
->frame
!= NULL
) {
218 g_object_unref(data
->frame
);
222 /* The rows finalization will happen in the finalize() method */
223 if (data
->rows
!= NULL
)
224 g_slist_foreach(data
->rows
, (GFunc
) row_dispose
, NULL
);
226 if (PARENT_OBJECT_CLASS
->dispose
)
227 PARENT_OBJECT_CLASS
->dispose(object
);
231 finalize(GObject
*object
)
234 AdgTablePrivate
*data
;
236 table
= (AdgTable
*) object
;
239 if (data
->rows
!= NULL
) {
240 g_slist_foreach(data
->rows
, (GFunc
) row_free
, NULL
);
241 g_slist_free(data
->rows
);
244 if (data
->cell_names
!= NULL
)
245 g_hash_table_destroy(data
->cell_names
);
247 if (PARENT_OBJECT_CLASS
->finalize
)
248 PARENT_OBJECT_CLASS
->finalize(object
);
252 get_property(GObject
*object
, guint prop_id
, 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 set_property(GObject
*object
, guint prop_id
,
271 const GValue
*value
, GParamSpec
*pspec
)
274 AdgTablePrivate
*data
;
276 table
= (AdgTable
*) object
;
280 case PROP_TABLE_DRESS
:
281 adg_dress_set(&data
->table_dress
, g_value_get_int(value
));
284 switch_frame(table
, g_value_get_boolean(value
));
287 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
296 * Creates a new empty table entity. The #AdgEntity:local-method
297 * property is set by default to #ADG_MIX_DISABLED, that is the
298 * table is not subject to any local transformations.
300 * Returns: the newly created table entity
305 return g_object_new(ADG_TYPE_TABLE
,
306 "local-method", ADG_MIX_DISABLED
, NULL
);
310 * adg_table_set_table_dress:
311 * @table: an #AdgTable
312 * @dress: the new #AdgDress to use
314 * Sets a new table dress for rendering @table. The new dress
315 * must be related to the original dress for this property:
316 * you cannot set a dress used for line styles to a dress
319 * The check is done by calling adg_dress_are_related() with
320 * @dress and the previous dress as arguments. Check out its
321 * documentation for details on what is a related dress.
324 adg_table_set_table_dress(AdgTable
*table
, AdgDress dress
)
326 AdgTablePrivate
*data
;
328 g_return_if_fail(ADG_IS_TABLE(table
));
332 if (adg_dress_set(&data
->table_dress
, dress
))
333 g_object_notify((GObject
*) table
, "table-dress");
337 * adg_table_get_table_dress:
338 * @table: an #AdgTable
340 * Gets the table dress to be used in rendering @table.
342 * Returns: the current table dress
345 adg_table_get_table_dress(AdgTable
*table
)
347 AdgTablePrivate
*data
;
349 g_return_val_if_fail(ADG_IS_TABLE(table
), ADG_DRESS_UNDEFINED
);
353 return data
->table_dress
;
357 * adg_table_switch_frame:
358 * @table: an #AdgTable
359 * @new_state: the new state of the frame
361 * Sets the #AdgTable:has-frame property: %TRUE will draw a
362 * frame around the whole table using the #AdgTableStyle:frame-dress
363 * dress of the table style.
366 adg_table_switch_frame(AdgTable
*table
, gboolean new_state
)
368 g_return_if_fail(ADG_IS_TABLE(table
));
370 if (switch_frame(table
, new_state
))
371 g_object_notify((GObject
*) table
, "has-frame");
375 * adg_table_has_frame:
376 * @table: an #AdgTable
378 * Returns the state of the #AdgTable:has-frame property.
380 * Returns: the current state
383 adg_table_has_frame(AdgTable
*table
)
385 AdgTablePrivate
*data
;
387 g_return_val_if_fail(ADG_IS_TABLE(table
), FALSE
);
391 return data
->has_frame
;
395 * adg_table_get_n_rows:
396 * @table: an #AdgTable
398 * Gets the number of rows stored in @table.
400 * Returns: the number of rows or %0 on empty @table or errors
403 adg_table_get_n_rows(AdgTable
*table
)
405 AdgTablePrivate
*data
;
407 g_return_val_if_fail(ADG_IS_TABLE(table
), 0);
411 if (data
->rows
== NULL
)
414 return g_slist_length(data
->rows
);
419 * @table: an #AdgTable
421 * Creates a new empty row and appends it at the end of the rows
422 * yet present in @table. By default, the height of this new
423 * row will be the fallback value provided by the table style:
424 * you can override it by using adg_table_row_set_height().
426 * Returns: the newly created row or %NULL on errors
429 adg_table_row_new(AdgTable
*table
)
431 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
433 return row_new(table
, NULL
);
437 * adg_table_row_new_before:
438 * @row: a valid #AdgTableRow
440 * Creates a new empty row with default height and inserts it
443 * Returns: the newly created row or %NULL on errors
446 adg_table_row_new_before(AdgTableRow
*row
)
448 g_return_val_if_fail(row
!= NULL
, NULL
);
449 g_return_val_if_fail(ADG_IS_TABLE(row
->table
), NULL
);
451 return row_new(row
->table
, row
);
455 * adg_table_row_delete:
456 * @row: a valid #AdgTableRow
458 * Removes @row from its owner table and frees every resources allocated
459 * by it. This means also the eventual cells owned by @row will be freed.
462 adg_table_row_delete(AdgTableRow
*row
)
465 AdgTablePrivate
*data
;
467 g_return_if_fail(row
!= NULL
);
471 g_return_if_fail(ADG_IS_TABLE(table
));
475 g_slist_foreach(row
->cells
, (GFunc
) cell_free
, NULL
);
476 g_slist_free(row
->cells
);
477 data
->rows
= g_slist_remove(data
->rows
, row
);
483 * adg_table_row_get_n_cells:
484 * @row: a valid #AdgTableRow
486 * Gets the number of cells stored in @row.
488 * Returns: the number of cells or %0 on empty row or errors
491 adg_table_row_get_n_cells(const AdgTableRow
*row
)
493 g_return_val_if_fail(row
!= NULL
, 0);
495 if (row
->cells
== NULL
)
498 return g_slist_length(row
->cells
);
502 * adg_table_row_set_height:
503 * @row: a valid #AdgTableRow
504 * @height: the new height
506 * Sets a new height on @row. The extents will be invalidated to
507 * recompute the whole layout of the table. Specifying %0 in
508 * @height will use the default height set in the table style.
511 adg_table_row_set_height(AdgTableRow
*row
, gdouble height
)
513 g_return_if_fail(row
!= NULL
);
515 row
->height
= height
;
517 adg_entity_invalidate((AdgEntity
*) row
->table
);
521 * adg_table_row_get_height:
522 * @row: a valid #AdgTableRow
524 * Gets the height of @row.
526 * Returns: the requested height or %0 on errors
529 adg_table_row_get_height(AdgTableRow
*row
)
531 g_return_val_if_fail(row
!= NULL
, 0.);
537 * adg_table_row_get_extents:
538 * @row: a valid #AdgTableRow
540 * Gets the extents of @row. This function is useful only after the
541 * arrange() phase as in the other situation the extents will likely
544 * Returns: the extents of @row or %NULL on errors
547 adg_table_row_get_extents(AdgTableRow
*row
)
549 g_return_val_if_fail(row
!= NULL
, NULL
);
551 return &row
->extents
;
555 * adg_table_cell_new:
556 * @row: a valid #AdgTableRow
557 * @width: width of the cell
559 * Creates a new empty cell without a frame and appends it at the
560 * end of the cells yet present in @row. You can add content to the
561 * cell by using adg_table_cell_set_title() and
562 * adg_table_cell_set_value() or enable the frame with
563 * adg_table_cell_switch_frame().
565 * A positive @width value specifies the width of this cell in global
566 * space: if the width of its content (that is, either the title or the
567 * value entity) will be greater than @width, it will be rendered
568 * outside the cell boundary box, luckely overwriting the adiacent
571 * Using %0 as @width means the width of the cell will be automatically
572 * adjusted to the maximum width of its content.
574 * Negative width values are not allowed: this condition will raise
575 * a warning without any further processing.
577 * Returns: the newly created cell or %NULL on errors
580 adg_table_cell_new(AdgTableRow
*row
, gdouble width
)
582 g_return_val_if_fail(row
!= NULL
, NULL
);
583 g_return_val_if_fail(width
>= 0, NULL
);
585 return cell_new(row
, NULL
, width
, FALSE
, NULL
, NULL
, NULL
);
589 * adg_table_cell_new_before:
590 * @cell: a valid #AdgTableCell
591 * @width: width of the cell
593 * Creates a new cell and inserts it rigthly before the @cell cell.
594 * This works similarily and accepts the same parameters as the
595 * adg_table_cell_new() function.
597 * Returns: the newly created cell or %NULL on errors
600 adg_table_cell_new_before(AdgTableCell
*cell
, gdouble width
)
602 g_return_val_if_fail(cell
!= NULL
, NULL
);
603 g_return_val_if_fail(cell
->row
!= NULL
, NULL
);
604 g_return_val_if_fail(width
>= 0, NULL
);
606 return cell_new(cell
->row
, cell
, width
, FALSE
, NULL
, NULL
, NULL
);
610 * adg_table_cell_new_full:
611 * @row: a valid #AdgTableRow
612 * @width: width of the cell
613 * @name: name to associate
614 * @title: title to render
615 * @value: value to render
617 * A convenient function to append a framed cell to @row with a
618 * specific title and value text. The font to use for rendering
619 * @title and @value will be picked up from the table style, so
620 * be sure to have the correct table dress set before calling
623 * @row and @width have the same meanings as in adg_table_cell_new():
624 * check its documentation for details.
626 * @name is an optional identifier to univoquely access this cell
627 * by using adg_table_cell(). The identifier must be univoque:
628 * if there is yet a cell with the same name a warning message will
629 * be raised and the function will fail.
631 * Returns: the newly created cell or %NULL on errors
634 adg_table_cell_new_full(AdgTableRow
*row
, gdouble width
, const gchar
*name
,
635 const gchar
*title
, const gchar
*value
)
639 g_return_val_if_fail(row
!= NULL
, NULL
);
641 cell
= cell_new(row
, NULL
, width
, TRUE
, name
, NULL
, NULL
);
644 adg_table_cell_set_text_title(cell
, title
);
647 adg_table_cell_set_text_value(cell
, value
);
654 * @table: an #AdgTable
655 * @name: the name of a cell
657 * Gets the cell named @name inside @table. Only named cells can be
658 * retrieved by this method.
660 * Returns: the requested cell or %NULL if not found
663 adg_table_cell(AdgTable
*table
, const gchar
*name
)
665 AdgTablePrivate
*data
;
667 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
671 if (data
->cell_names
== NULL
)
674 return g_hash_table_lookup(data
->cell_names
, name
);
678 * adg_table_cell_delete:
679 * @cell: a valid #AdgTableCell
681 * Deletes @cell removing it from the container row and freeing
682 * any resource associated to it.
685 adg_table_cell_delete(AdgTableCell
*cell
)
689 g_return_if_fail(cell
!= NULL
);
693 g_return_if_fail(row
!= NULL
);
696 row
->cells
= g_slist_remove(row
->cells
, cell
);
700 * adg_table_cell_set_name:
701 * @cell: a valid #AdgTableCell
702 * @name: the new name of @cell
704 * Sets a new name on @cell: this will allow to access @cell by
705 * name at a later time using the adg_table_cell() API.
708 adg_table_cell_set_name(AdgTableCell
*cell
, const gchar
*name
)
710 AdgTablePrivate
*data
;
712 g_return_if_fail(cell
!= NULL
);
714 data
= cell
->row
->table
->data
;
716 cell_set_name(cell
, NULL
);
717 cell_set_name(cell
, name
);
721 * adg_table_cell_get_name:
722 * @cell: a valid #AdgTableCell
724 * Gets the name assigned to @cell. This function is highly inefficient
725 * as the cell names are stored in a hash table optimized to get a cell
726 * from a name. Getting the name from a cell involves a full hash table
729 * Returns: the name bound of @cell or %NULL on no name or errors
732 adg_table_cell_get_name(AdgTableCell
*cell
)
734 AdgTablePrivate
*data
;
736 g_return_val_if_fail(cell
!= NULL
, NULL
);
738 data
= cell
->row
->table
->data
;
740 return g_hash_table_find(data
->cell_names
, value_match
, cell
);
744 * adg_table_cell_set_title:
745 * @cell: a valid #AdgTableCell
746 * @title: the new title entity
748 * Sets @title as the new title entity of @cell. The top left
749 * corner of the bounding box of @title will be cohincident to
750 * the top left corner of the cell extents, taking into accounts
751 * eventual padding spaces specified by the table style.
753 * The old internal entity is unrefenrenced while the @title (if
754 * not %NULL) is refenenced with g_object_ref_sink().
756 * @title can be %NULL, in which case the old entity is removed.
759 adg_table_cell_set_title(AdgTableCell
*cell
, AdgEntity
*title
)
761 g_return_if_fail(cell
!= NULL
);
762 g_return_if_fail(title
== NULL
|| ADG_IS_ENTITY(title
));
764 if (cell_set_title(cell
, title
))
765 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
769 * adg_table_cell_set_text_title:
770 * @cell: a valid #AdgTableCell
771 * @title: a text string
773 * Convenient function to set a the title of a cell using an #AdgToyText
774 * entity with the font dress picked from #AdgTable:table-dress with
775 * a call to adg_table_style_get_title_dress().
778 adg_table_cell_set_text_title(AdgTableCell
*cell
, const gchar
*title
)
782 AdgTableStyle
*table_style
;
783 const AdgPair
*padding
;
784 AdgDress table_dress
, font_dress
;
787 g_return_if_fail(cell
!= NULL
);
790 adg_table_cell_set_title(cell
, NULL
);
792 if (cell
->title
!= NULL
) {
793 const gchar
*old_title
;
795 if (ADG_IS_TOY_TEXT(cell
->title
))
796 old_title
= adg_toy_text_get_label((AdgToyText
*) cell
->title
);
800 if (adg_strcmp(title
, old_title
) == 0)
804 table
= cell
->row
->table
;
805 table_dress
= adg_table_get_table_dress(table
);
806 table_style
= (AdgTableStyle
*) adg_entity_style((AdgEntity
*) table
,
808 padding
= adg_table_style_get_cell_padding(table_style
);
809 font_dress
= adg_table_style_get_title_dress(table_style
);
810 entity
= g_object_new(ADG_TYPE_TOY_TEXT
, "label", title
,
811 "font-dress", font_dress
, NULL
);
813 cairo_matrix_init_translate(&map
, padding
->x
, padding
->y
);
814 adg_entity_set_global_map(entity
, &map
);
816 adg_table_cell_set_title(cell
, entity
);
820 * adg_table_cell_title:
821 * @cell: a valid #AdgTableCell
823 * Gets the current title of @cell. The returned string is owned
824 * by @cell and must not be modified or freed.
826 * Returns: the title entity or %NULL for undefined title
829 adg_table_cell_title(AdgTableCell
*cell
)
831 g_return_val_if_fail(cell
!= NULL
, NULL
);
837 * adg_table_cell_set_value:
838 * @cell: a valid #AdgTableCell
839 * @value: the new value entity
841 * Sets @value as the new value entity of @cell. The bottom middle
842 * point of the bounding box of @value will be cohincident to the
843 * bottom middle point of the cell extents, taking into accounts
844 * eventual padding spaces specified by the table style.
846 * The old internal entity is unrefenrenced while the @value (if
847 * not %NULL) is refenenced with g_object_ref_sink().
849 * @value can be %NULL, in which case the old entity is removed.
852 adg_table_cell_set_value(AdgTableCell
*cell
, AdgEntity
*value
)
854 g_return_if_fail(cell
!= NULL
);
855 g_return_if_fail(value
== NULL
|| ADG_IS_ENTITY(value
));
857 if (cell_set_value(cell
, value
))
858 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
862 * adg_table_cell_set_text_value:
863 * @cell: a valid #AdgTableCell
864 * @value: a text string
866 * Convenient function to set a the value of a cell using an #AdgToyText
867 * entity with a value font dress picked from #AdgTable:table-dress with
868 * a call to adg_table_style_get_value_dress().
871 adg_table_cell_set_text_value(AdgTableCell
*cell
, const gchar
*value
)
875 AdgTableStyle
*table_style
;
876 const AdgPair
*padding
;
877 AdgDress table_dress
, font_dress
;
880 g_return_if_fail(cell
!= NULL
);
883 adg_table_cell_set_value(cell
, NULL
);
885 if (cell
->value
!= NULL
) {
886 const gchar
*old_value
;
888 if (ADG_IS_TOY_TEXT(cell
->value
))
889 old_value
= adg_toy_text_get_label((AdgToyText
*) cell
->value
);
893 if (adg_strcmp(value
, old_value
) == 0)
897 table
= cell
->row
->table
;
898 table_dress
= adg_table_get_table_dress(table
);
899 table_style
= (AdgTableStyle
*) adg_entity_style((AdgEntity
*) table
,
901 padding
= adg_table_style_get_cell_padding(table_style
);
902 font_dress
= adg_table_style_get_value_dress(table_style
);
903 entity
= g_object_new(ADG_TYPE_TOY_TEXT
, "label", value
,
904 "font-dress", font_dress
, NULL
);
906 cairo_matrix_init_translate(&map
, 0, -padding
->y
);
907 adg_entity_set_global_map(entity
, &map
);
909 adg_table_cell_set_value(cell
, entity
);
913 * adg_table_cell_value:
914 * @cell: a valid #AdgTableCell
916 * Gets the current value of @cell. The returned string is owned
917 * by @cell and must not be modified or freed.
919 * Returns: the value entity or %NULL for undefined value
922 adg_table_cell_value(AdgTableCell
*cell
)
924 g_return_val_if_fail(cell
!= NULL
, NULL
);
930 * adg_table_cell_set_value_pos:
931 * @cell: a valid #AdgTableCell
932 * @from_factor: the alignment factor on the value entity
933 * @to_factor: the alignment factor on the cell
935 * Sets a new custom position for the value entity of @cell. The
936 * @from_factor specifies the source point (as a fraction of the
937 * value extents) while the @to_factor is the destination point
938 * (specified as a fraction of the cell extents) the source point
942 adg_table_cell_set_value_pos(AdgTableCell
*cell
, const AdgPair
*from_factor
,
943 const AdgPair
*to_factor
)
945 g_return_if_fail(cell
!= NULL
);
946 g_return_if_fail(cell
->value
!= NULL
);
948 cell_set_value_pos(cell
, from_factor
, to_factor
);
952 * adg_table_cell_set_width:
953 * @cell: a valid #AdgTableCell
954 * @width: the new width
956 * Sets a new width on @cell. The extents on the whole table
957 * will be invalidated, so will be recomputed in the next
961 adg_table_cell_set_width(AdgTableCell
*cell
, gdouble width
)
963 g_return_if_fail(cell
!= NULL
);
967 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
971 * adg_table_cell_get_width:
972 * @cell: a valid #AdgTableCell
974 * Gets the width of @cell.
976 * Returns: the requested width or %0 on errors
979 adg_table_cell_get_width(AdgTableCell
*cell
)
981 g_return_val_if_fail(cell
!= NULL
, 0.);
987 * adg_table_cell_switch_frame:
988 * @cell: a valid #AdgTableCell
989 * @new_state: the new frame state
991 * Sets the frame flag of @cell: if @new_state is %TRUE, a frame around
992 * @cell will be rendered using the #AdgTableStyle:cell-dress dress
993 * of the table style.
996 adg_table_cell_switch_frame(AdgTableCell
*cell
, gboolean new_state
)
998 AdgTablePrivate
*data
;
1000 g_return_if_fail(cell
!= NULL
);
1002 if (cell
->has_frame
== new_state
)
1005 data
= cell
->row
->table
->data
;
1006 cell
->has_frame
= new_state
;
1008 if (data
->grid
!= NULL
) {
1009 g_object_unref(data
->grid
);
1015 * adg_table_cell_has_frame:
1016 * @cell: a valid #AdgTableCell
1018 * Gets the frame flag of @cell.
1020 * Returns: the frame flag
1023 adg_table_cell_has_frame(AdgTableCell
*cell
)
1025 g_return_val_if_fail(cell
!= NULL
, FALSE
);
1027 return cell
->has_frame
;
1031 * adg_table_cell_get_extents:
1032 * @cell: a valid #AdgTableCell
1034 * Gets the extents of @cell. This function is useful only after the
1035 * arrange() phase as in the other situation the extents will likely
1036 * be not up to date.
1038 * Returns: the extents of @cell or %NULL on errors
1041 adg_table_cell_get_extents(AdgTableCell
*cell
)
1043 g_return_val_if_fail(cell
!= NULL
, NULL
);
1045 return &cell
->extents
;
1050 global_changed(AdgEntity
*entity
)
1052 if (PARENT_ENTITY_CLASS
->global_changed
)
1053 PARENT_ENTITY_CLASS
->global_changed(entity
);
1055 propagate((AdgTable
*) entity
, "global-changed");
1059 local_changed(AdgEntity
*entity
)
1061 if (PARENT_ENTITY_CLASS
->local_changed
)
1062 PARENT_ENTITY_CLASS
->local_changed(entity
);
1064 propagate((AdgTable
*) entity
, "local-changed");
1068 invalidate(AdgEntity
*entity
)
1070 propagate((AdgTable
*) entity
, "invalidate");
1074 arrange(AdgEntity
*entity
)
1077 AdgTablePrivate
*data
;
1078 CpmlExtents extents
;
1079 const AdgPair
*spacing
;
1084 table
= (AdgTable
*) entity
;
1086 cpml_extents_copy(&extents
, adg_entity_get_extents(entity
));
1088 /* Resolve the table style */
1089 if (data
->table_style
== NULL
)
1090 data
->table_style
= (AdgTableStyle
*)
1091 adg_entity_style(entity
, data
->table_dress
);
1093 if (extents
.is_defined
) {
1094 if (data
->grid
!= NULL
)
1095 adg_entity_arrange((AdgEntity
*) data
->grid
);
1096 if (data
->frame
!= NULL
)
1097 adg_entity_arrange((AdgEntity
*) data
->frame
);
1101 spacing
= adg_table_style_get_cell_spacing(data
->table_style
);
1105 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1106 row
= row_node
->data
;
1108 row_arrange_size(row
);
1110 if (row
->extents
.size
.x
> extents
.size
.x
)
1111 extents
.size
.x
= row
->extents
.size
.x
;
1112 extents
.size
.y
+= row
->extents
.size
.y
;
1115 /* TODO: update the org according to the table alignments */
1117 y
= extents
.org
.y
+ spacing
->y
;
1118 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1119 row
= row_node
->data
;
1121 row
->extents
.org
.x
= extents
.org
.x
;
1122 row
->extents
.org
.y
= y
;
1126 y
+= row
->extents
.size
.y
+ spacing
->y
;
1129 extents
.is_defined
= TRUE
;
1130 cpml_extents_transform(&extents
, adg_entity_get_global_matrix(entity
));
1131 adg_entity_set_extents(entity
, &extents
);
1133 arrange_grid(entity
);
1134 arrange_frame(entity
);
1138 arrange_grid(AdgEntity
*entity
)
1140 AdgTablePrivate
*data
;
1143 GSList
*row_node
, *cell_node
;
1149 data
= ((AdgTable
*) entity
)->data
;
1151 if (data
->grid
!= NULL
)
1154 path
= adg_path_new();
1155 trail
= (AdgTrail
*) path
;
1157 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1158 row
= row_node
->data
;
1160 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1161 cell
= cell_node
->data
;
1163 if (!cell
->has_frame
)
1166 cpml_pair_copy(&pair
, &cell
->extents
.org
);
1167 adg_path_move_to(path
, &pair
);
1168 pair
.x
+= cell
->extents
.size
.x
;
1169 adg_path_line_to(path
, &pair
);
1170 pair
.y
+= cell
->extents
.size
.y
;
1171 adg_path_line_to(path
, &pair
);
1172 pair
.x
-= cell
->extents
.size
.x
;
1173 adg_path_line_to(path
, &pair
);
1174 adg_path_close(path
);
1178 if (!adg_trail_get_extents(trail
)->is_defined
)
1181 dress
= adg_table_style_get_grid_dress(data
->table_style
);
1182 data
->grid
= g_object_new(ADG_TYPE_STROKE
,
1183 "local-method", ADG_MIX_PARENT
,
1184 "line-dress", dress
,
1186 "parent", entity
, NULL
);
1187 adg_entity_arrange((AdgEntity
*) data
->grid
);
1191 arrange_frame(AdgEntity
*entity
)
1193 AdgTablePrivate
*data
;
1195 const CpmlExtents
*extents
;
1199 data
= ((AdgTable
*) entity
)->data
;
1201 if (data
->frame
!= NULL
|| !data
->has_frame
)
1204 path
= adg_path_new();
1205 extents
= adg_entity_get_extents(entity
);
1207 cpml_pair_copy(&pair
, &extents
->org
);
1208 adg_path_move_to(path
, &pair
);
1209 pair
.x
+= extents
->size
.x
;
1210 adg_path_line_to(path
, &pair
);
1211 pair
.y
+= extents
->size
.y
;
1212 adg_path_line_to(path
, &pair
);
1213 pair
.x
-= extents
->size
.x
;
1214 adg_path_line_to(path
, &pair
);
1215 adg_path_close(path
);
1217 dress
= adg_table_style_get_frame_dress(data
->table_style
);
1219 data
->frame
= g_object_new(ADG_TYPE_STROKE
,
1220 "local-method", ADG_MIX_PARENT
,
1221 "line-dress", dress
,
1222 "trail", (AdgTrail
*) path
,
1223 "parent", entity
, NULL
);
1224 adg_entity_arrange((AdgEntity
*) data
->frame
);
1228 render(AdgEntity
*entity
, cairo_t
*cr
)
1230 cairo_transform(cr
, adg_entity_get_local_matrix(entity
));
1232 propagate((AdgTable
*) entity
, "render", cr
);
1236 switch_frame(AdgTable
*table
, gboolean new_state
)
1238 AdgTablePrivate
*data
= table
->data
;
1240 if (data
->has_frame
== new_state
)
1243 data
->has_frame
= new_state
;
1245 if (data
->frame
!= NULL
) {
1246 g_object_unref(data
->frame
);
1254 propagate(AdgTable
*table
, const gchar
*detailed_signal
, ...)
1258 va_list var_args
, var_copy
;
1259 AdgTablePrivate
*data
;
1264 AdgAlignment
*alignment
;
1266 if (!g_signal_parse_name(detailed_signal
, G_TYPE_FROM_INSTANCE(table
),
1267 &signal_id
, &detail
, FALSE
)) {
1268 g_return_if_reached();
1271 va_start(var_args
, detailed_signal
);
1274 if (data
->frame
!= NULL
) {
1275 G_VA_COPY(var_copy
, var_args
);
1276 g_signal_emit_valist(data
->frame
, signal_id
, detail
, var_copy
);
1279 if (data
->grid
!= NULL
) {
1280 G_VA_COPY(var_copy
, var_args
);
1281 g_signal_emit_valist(data
->grid
, signal_id
, detail
, var_copy
);
1284 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1285 row
= row_node
->data
;
1287 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1288 cell
= cell_node
->data
;
1290 if (cell
->title
!= NULL
) {
1291 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->title
);
1293 G_VA_COPY(var_copy
, var_args
);
1294 g_signal_emit_valist(alignment
, signal_id
, detail
, var_copy
);
1297 if (cell
->value
!= NULL
) {
1298 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->value
);
1300 G_VA_COPY(var_copy
, var_args
);
1301 g_signal_emit_valist(alignment
, signal_id
, detail
, var_copy
);
1309 static AdgTableRow
*
1310 row_new(AdgTable
*table
, AdgTableRow
*before_row
)
1312 AdgTablePrivate
*data
;
1313 AdgTableRow
*new_row
;
1316 new_row
= g_new(AdgTableRow
, 1);
1317 new_row
->table
= table
;
1318 new_row
->cells
= NULL
;
1319 new_row
->height
= 0;
1320 new_row
->extents
.is_defined
= FALSE
;
1322 if (before_row
== NULL
) {
1323 data
->rows
= g_slist_append(data
->rows
, new_row
);
1325 GSList
*before_node
= g_slist_find(data
->rows
, before_row
);
1327 /* This MUST be present, otherwise something really bad happened */
1328 g_return_val_if_fail(before_node
!= NULL
, NULL
);
1330 data
->rows
= g_slist_insert_before(data
->rows
, before_node
, new_row
);
1333 invalidate((AdgEntity
*) table
);
1339 row_arrange_size(AdgTableRow
*row
)
1341 AdgTableStyle
*table_style
;
1342 const AdgPair
*spacing
;
1347 table_style
= GET_TABLE_STYLE(row
->table
);
1348 spacing
= adg_table_style_get_cell_spacing(table_style
);
1349 size
= &row
->extents
.size
;
1352 if (row
->height
== 0)
1353 size
->y
= adg_table_style_get_row_height(table_style
);
1355 size
->y
= row
->height
;
1357 /* Compute the row width by summing every cell width */
1358 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1359 cell
= cell_node
->data
;
1361 cell_arrange_size(cell
);
1363 size
->x
+= cell
->extents
.size
.x
+ spacing
->x
;
1367 size
->x
+= spacing
->x
;
1370 /* Before calling this function, row->extents should be updated */
1372 row_arrange(AdgTableRow
*row
)
1374 AdgTableStyle
*table_style
;
1375 const AdgPair
*spacing
;
1381 table_style
= GET_TABLE_STYLE(row
->table
);
1382 spacing
= adg_table_style_get_cell_spacing(table_style
);
1383 org
= &row
->extents
.org
;
1384 x
= org
->x
+ spacing
->x
;
1386 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1387 cell
= cell_node
->data
;
1389 cell
->extents
.org
.x
= x
;
1390 cell
->extents
.org
.y
= org
->y
;
1394 x
+= cell
->extents
.size
.x
+ spacing
->x
;
1397 row
->extents
.is_defined
= TRUE
;
1401 row_dispose(AdgTableRow
*row
)
1403 g_slist_foreach(row
->cells
, (GFunc
) cell_dispose
, NULL
);
1407 row_free(AdgTableRow
*row
)
1409 g_slist_foreach(row
->cells
, (GFunc
) cell_free
, NULL
);
1410 g_slist_free(row
->cells
);
1415 static AdgTableCell
*
1416 cell_new(AdgTableRow
*row
, AdgTableCell
*before_cell
,
1417 gdouble width
, gboolean has_frame
,
1418 const gchar
*name
, AdgEntity
*title
, AdgEntity
*value
)
1420 AdgTablePrivate
*data
;
1421 AdgTableCell
*new_cell
;
1423 data
= row
->table
->data
;
1425 if (name
!= NULL
&& data
->cell_names
!= NULL
&&
1426 g_hash_table_lookup(data
->cell_names
, name
) != NULL
) {
1427 g_warning(_("%s: `%s' cell name is yet used"), G_STRLOC
, name
);
1431 new_cell
= g_new(AdgTableCell
, 1);
1432 new_cell
->row
= row
;
1433 new_cell
->width
= width
;
1434 new_cell
->has_frame
= has_frame
;
1435 new_cell
->title
= NULL
;
1436 new_cell
->value
= NULL
;
1437 new_cell
->extents
.is_defined
= FALSE
;
1438 new_cell
->value_factor
.x
= 0.5;
1439 new_cell
->value_factor
.y
= 1;
1441 cell_set_title(new_cell
, title
);
1442 cell_set_value(new_cell
, value
);
1444 if (before_cell
== NULL
) {
1445 row
->cells
= g_slist_append(row
->cells
, new_cell
);
1447 GSList
*before_node
= g_slist_find(row
->cells
, before_cell
);
1449 /* This MUST be not null, otherwise something really bad happened */
1450 g_return_val_if_fail(before_node
!= NULL
, NULL
);
1452 row
->cells
= g_slist_insert_before(row
->cells
, before_node
, new_cell
);
1456 cell_set_name(new_cell
, name
);
1462 cell_set_name(AdgTableCell
*cell
, const gchar
*name
)
1464 AdgTablePrivate
*data
= cell
->row
->table
->data
;
1466 if (data
->cell_names
== NULL
&& name
== NULL
)
1469 if (data
->cell_names
== NULL
)
1470 data
->cell_names
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1474 g_hash_table_foreach_remove(data
->cell_names
, value_match
, cell
);
1476 g_hash_table_insert(data
->cell_names
, g_strdup(name
), cell
);
1480 cell_set_title(AdgTableCell
*cell
, AdgEntity
*title
)
1482 AdgAlignment
*alignment
;
1484 if (cell
->title
== title
)
1487 if (cell
->title
!= NULL
) {
1488 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->title
);
1489 g_object_unref(alignment
);
1492 cell
->title
= title
;
1494 if (title
!= NULL
) {
1495 alignment
= adg_alignment_new_explicit(0, -1);
1496 g_object_ref_sink(alignment
);
1497 adg_entity_set_parent((AdgEntity
*) alignment
,
1498 (AdgEntity
*) cell
->row
->table
);
1500 adg_container_add((AdgContainer
*) alignment
, title
);
1507 cell_set_value(AdgTableCell
*cell
, AdgEntity
*value
)
1509 AdgAlignment
*alignment
;
1511 if (cell
->value
== value
)
1514 if (cell
->value
!= NULL
) {
1515 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->value
);
1516 g_object_unref(alignment
);
1519 cell
->value
= value
;
1521 if (value
!= NULL
) {
1522 alignment
= adg_alignment_new_explicit(0.5, 0);
1523 g_object_ref_sink(alignment
);
1524 adg_entity_set_parent((AdgEntity
*) alignment
,
1525 (AdgEntity
*) cell
->row
->table
);
1527 adg_container_add((AdgContainer
*) alignment
, value
);
1534 cell_set_value_pos(AdgTableCell
*cell
,
1535 const AdgPair
*from_factor
, const AdgPair
*to_factor
)
1537 AdgAlignment
*alignment
;
1539 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->value
);
1541 if (from_factor
!= NULL
)
1542 adg_alignment_set_factor(alignment
, from_factor
);
1544 if (to_factor
!= NULL
)
1545 cell
->value_factor
= *to_factor
;
1549 cell_arrange_size(AdgTableCell
*cell
)
1552 AdgAlignment
*title_alignment
;
1553 AdgAlignment
*value_alignment
;
1555 size
= &cell
->extents
.size
;
1557 if (cell
->title
!= NULL
) {
1558 title_alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->title
);
1559 adg_entity_arrange((AdgEntity
*) title_alignment
);
1561 title_alignment
= NULL
;
1564 if (cell
->value
!= NULL
) {
1565 value_alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->value
);
1566 adg_entity_arrange((AdgEntity
*) value_alignment
);
1568 value_alignment
= NULL
;
1571 size
->y
= cell
->row
->extents
.size
.y
;
1573 if (cell
->width
== 0) {
1574 AdgTableStyle
*table_style
= GET_TABLE_STYLE(cell
->row
->table
);
1575 const CpmlExtents
*extents
;
1577 /* The width depends on the cell content (default = 0) */
1580 if (title_alignment
!= NULL
) {
1581 extents
= adg_entity_get_extents((AdgEntity
*) title_alignment
);
1582 size
->x
= extents
->size
.x
;
1585 if (value_alignment
!= NULL
) {
1586 extents
= adg_entity_get_extents((AdgEntity
*) value_alignment
);
1587 if (extents
->size
.x
> size
->x
)
1588 size
->x
= extents
->size
.x
;
1591 size
->x
+= adg_table_style_get_cell_spacing(table_style
)->x
* 2;
1593 size
->x
= cell
->width
;
1597 /* Before calling this function, cell->extents should be updated */
1599 cell_arrange(AdgTableCell
*cell
)
1601 CpmlExtents
*extents
;
1602 AdgAlignment
*alignment
;
1605 extents
= &cell
->extents
;
1607 if (cell
->title
!= NULL
) {
1608 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->title
);
1610 cairo_matrix_init_translate(&map
, extents
->org
.x
, extents
->org
.y
);
1611 adg_entity_set_global_map((AdgEntity
*) alignment
, &map
);
1614 if (cell
->value
!= NULL
) {
1617 alignment
= (AdgAlignment
*) adg_entity_get_parent(cell
->value
);
1618 to
.x
= extents
->size
.x
* cell
->value_factor
.x
+ extents
->org
.x
;
1619 to
.y
= extents
->size
.y
* cell
->value_factor
.y
+ extents
->org
.y
;
1621 cairo_matrix_init_translate(&map
, to
.x
, to
.y
);
1622 adg_entity_set_global_map((AdgEntity
*) alignment
, &map
);
1625 extents
->is_defined
= TRUE
;
1629 cell_dispose(AdgTableCell
*cell
)
1631 cell_set_title(cell
, NULL
);
1632 cell_set_value(cell
, NULL
);
1636 cell_free(AdgTableCell
*cell
)
1638 cell_set_name(cell
, NULL
);
1644 value_match(gpointer key
, gpointer value
, gpointer user_data
)
1646 return value
== user_data
;