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_set_name (AdgTableCell
*cell
,
116 static gboolean
cell_set_title (AdgTableCell
*cell
,
118 static gboolean
cell_set_value (AdgTableCell
*cell
,
120 static void cell_arrange_size (AdgTableCell
*cell
);
121 static void cell_arrange (AdgTableCell
*cell
);
122 static void cell_dispose (AdgTableCell
*cell
);
123 static void cell_free (AdgTableCell
*cell
);
124 static gboolean
value_match (gpointer key
,
129 G_DEFINE_TYPE(AdgTable
, adg_table
, ADG_TYPE_ENTITY
);
133 adg_table_class_init(AdgTableClass
*klass
)
135 GObjectClass
*gobject_class
;
136 AdgEntityClass
*entity_class
;
139 gobject_class
= (GObjectClass
*) klass
;
140 entity_class
= (AdgEntityClass
*) klass
;
142 g_type_class_add_private(klass
, sizeof(AdgTablePrivate
));
144 gobject_class
->dispose
= dispose
;
145 gobject_class
->finalize
= finalize
;
146 gobject_class
->get_property
= get_property
;
147 gobject_class
->set_property
= set_property
;
149 entity_class
->global_changed
= global_changed
;
150 entity_class
->local_changed
= local_changed
;
151 entity_class
->invalidate
= invalidate
;
152 entity_class
->arrange
= arrange
;
153 entity_class
->render
= render
;
155 param
= adg_param_spec_dress("table-dress",
157 P_("The dress to use for stroking this entity"),
160 g_object_class_install_property(gobject_class
, PROP_TABLE_DRESS
, param
);
162 param
= g_param_spec_boolean("has-frame",
163 P_("Has Frame Flag"),
164 P_("If enabled, a frame using the proper dress found in this table style will be drawn around the table extents"),
167 g_object_class_install_property(gobject_class
, PROP_HAS_FRAME
, param
);
171 adg_table_init(AdgTable
*table
)
173 AdgTablePrivate
*data
= G_TYPE_INSTANCE_GET_PRIVATE(table
,
177 data
->table_dress
= ADG_DRESS_TABLE
;
178 data
->has_frame
= TRUE
;
180 data
->table_style
= NULL
;
184 data
->cell_names
= NULL
;
190 dispose(GObject
*object
)
192 AdgTablePrivate
*data
= ((AdgTable
*) object
)->data
;
194 if (data
->grid
!= NULL
) {
195 g_object_unref(data
->grid
);
199 if (data
->frame
!= NULL
) {
200 g_object_unref(data
->frame
);
204 /* The rows finalization will happen in the finalize() method */
205 if (data
->rows
!= NULL
)
206 g_slist_foreach(data
->rows
, (GFunc
) row_dispose
, NULL
);
208 if (PARENT_OBJECT_CLASS
->dispose
!= NULL
)
209 PARENT_OBJECT_CLASS
->dispose(object
);
213 finalize(GObject
*object
)
216 AdgTablePrivate
*data
;
218 table
= (AdgTable
*) object
;
221 if (data
->rows
!= NULL
) {
222 g_slist_foreach(data
->rows
, (GFunc
) row_free
, NULL
);
223 g_slist_free(data
->rows
);
226 if (data
->cell_names
!= NULL
)
227 g_hash_table_destroy(data
->cell_names
);
229 if (PARENT_OBJECT_CLASS
->finalize
!= NULL
)
230 PARENT_OBJECT_CLASS
->finalize(object
);
234 get_property(GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
236 AdgTablePrivate
*data
= ((AdgTable
*) object
)->data
;
239 case PROP_TABLE_DRESS
:
240 g_value_set_int(value
, data
->table_dress
);
243 g_value_set_boolean(value
, data
->has_frame
);
246 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
252 set_property(GObject
*object
, guint prop_id
,
253 const GValue
*value
, GParamSpec
*pspec
)
256 AdgTablePrivate
*data
;
258 table
= (AdgTable
*) object
;
262 case PROP_TABLE_DRESS
:
263 adg_dress_set(&data
->table_dress
, g_value_get_int(value
));
266 switch_frame(table
, g_value_get_boolean(value
));
269 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
278 * Creates a new empty table entity. The #AdgEntity:local-method
279 * property is set by default to #ADG_MIX_DISABLED, that is the
280 * table is not subject to any local transformations.
282 * Returns: the newly created table entity
287 return g_object_new(ADG_TYPE_TABLE
,
288 "local-method", ADG_MIX_DISABLED
, NULL
);
292 * adg_table_get_table_dress:
293 * @table: an #AdgTable
295 * Gets the table dress to be used in rendering @table.
297 * Returns: the current table dress
300 adg_table_get_table_dress(AdgTable
*table
)
302 AdgTablePrivate
*data
;
304 g_return_val_if_fail(ADG_IS_TABLE(table
), ADG_DRESS_UNDEFINED
);
308 return data
->table_dress
;
312 * adg_table_set_table_dress:
313 * @table: an #AdgTable
314 * @dress: the new #AdgDress to use
316 * Sets a new table dress for rendering @table. The new dress
317 * must be related to the original dress for this property:
318 * you cannot set a dress used for line styles to a dress
321 * The check is done by calling adg_dress_are_related() with
322 * @dress and the previous dress as arguments. Check out its
323 * documentation for details on what is a related dress.
326 adg_table_set_table_dress(AdgTable
*table
, AdgDress dress
)
328 AdgTablePrivate
*data
;
330 g_return_if_fail(ADG_IS_TABLE(table
));
334 if (adg_dress_set(&data
->table_dress
, dress
))
335 g_object_notify((GObject
*) table
, "table-dress");
339 * adg_table_has_frame:
340 * @table: an #AdgTable
342 * Returns the state of the #AdgTable:has-frame property.
344 * Returns: the current state
347 adg_table_has_frame(AdgTable
*table
)
349 AdgTablePrivate
*data
;
351 g_return_val_if_fail(ADG_IS_TABLE(table
), FALSE
);
355 return data
->has_frame
;
359 * adg_table_switch_frame:
360 * @table: an #AdgTable
361 * @state: the new state of the frame
363 * Sets the #AdgTable:has-frame property: %TRUE will draw a
364 * frame around the whole table using the #AdgTableStyle:frame-dress
365 * dress of the table style.
368 adg_table_switch_frame(AdgTable
*table
, gboolean state
)
370 g_return_if_fail(ADG_IS_TABLE(table
));
372 if (switch_frame(table
, state
))
373 g_object_notify((GObject
*) table
, "has-frame");
377 * adg_table_get_n_rows:
378 * @table: an #AdgTable
380 * Gets the number of rows stored in @table.
382 * Returns: the number of rows or %0 on empty @table or errors
385 adg_table_get_n_rows(AdgTable
*table
)
387 AdgTablePrivate
*data
;
389 g_return_val_if_fail(ADG_IS_TABLE(table
), 0);
393 if (data
->rows
== NULL
)
396 return g_slist_length(data
->rows
);
401 * @table: an #AdgTable
403 * Creates a new empty row and appends it at the end of the rows
404 * yet present in @table. By default, the height of this new
405 * row will be the fallback value provided by the table style:
406 * you can override it by using adg_table_row_set_height().
408 * Returns: the newly created row or %NULL on errors
411 adg_table_row_new(AdgTable
*table
)
413 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
415 return row_new(table
, NULL
);
419 * adg_table_row_new_before:
420 * @row: a valid #AdgTableRow
422 * Creates a new empty row with default height and inserts it
425 * Returns: the newly created row or %NULL on errors
428 adg_table_row_new_before(AdgTableRow
*row
)
430 g_return_val_if_fail(row
!= NULL
, NULL
);
431 g_return_val_if_fail(ADG_IS_TABLE(row
->table
), NULL
);
433 return row_new(row
->table
, row
);
437 * adg_table_row_delete:
438 * @row: a valid #AdgTableRow
440 * Removes @row from its owner table and frees every resources allocated
441 * by it. This means also the eventual cells owned by @row will be freed.
444 adg_table_row_delete(AdgTableRow
*row
)
447 AdgTablePrivate
*data
;
449 g_return_if_fail(row
!= NULL
);
453 g_return_if_fail(ADG_IS_TABLE(table
));
457 g_slist_foreach(row
->cells
, (GFunc
) cell_free
, NULL
);
458 g_slist_free(row
->cells
);
459 data
->rows
= g_slist_remove(data
->rows
, row
);
465 * adg_table_row_get_n_cells:
466 * @row: a valid #AdgTableRow
468 * Gets the number of cells stored in @row.
470 * Returns: the number of cells or %0 on empty row or errors
473 adg_table_row_get_n_cells(const AdgTableRow
*row
)
475 g_return_val_if_fail(row
!= NULL
, 0);
477 if (row
->cells
== NULL
)
480 return g_slist_length(row
->cells
);
484 * adg_table_row_set_height:
485 * @row: a valid #AdgTableRow
486 * @height: the new height
488 * Sets a new height on @row. The extents will be invalidated to
489 * recompute the whole layout of the table. Specifying %0 in
490 * @height will use the default height set in the table style.
493 adg_table_row_set_height(AdgTableRow
*row
, gdouble height
)
495 g_return_if_fail(row
!= NULL
);
497 row
->height
= height
;
499 adg_entity_invalidate((AdgEntity
*) row
->table
);
503 * adg_table_row_extents:
504 * @row: a valid #AdgTableRow
506 * Gets the extents of @row. This function is useful only after the
507 * arrange() phase as in the other situation the extents will likely
510 * Returns: the extents of @row or %NULL on errors
513 adg_table_row_extents(AdgTableRow
*row
)
515 g_return_val_if_fail(row
!= NULL
, NULL
);
517 return &row
->extents
;
521 * adg_table_get_cell:
522 * @table: an #AdgTable
523 * @name: the name of a cell
525 * Gets the cell named @name inside @table. Only named cells can be
526 * retrieved by this method.
528 * Returns: the requested cell or %NULL if not found
531 adg_table_get_cell(AdgTable
*table
, const gchar
*name
)
533 AdgTablePrivate
*data
;
535 g_return_val_if_fail(ADG_IS_TABLE(table
), NULL
);
539 if (data
->cell_names
== NULL
)
542 return g_hash_table_lookup(data
->cell_names
, name
);
546 * adg_table_cell_new:
547 * @row: a valid #AdgTableRow
548 * @width: width of the cell
550 * Creates a new empty cell without a frame and appends it at the
551 * end of the cells yet present in @row. You can add content to the
552 * cell by using adg_table_cell_set_title() and
553 * adg_table_cell_set_value() or enable the frame with
554 * adg_table_cell_switch_frame().
556 * A positive @width value specifies the width of this cell in global
557 * space: if the width of its content (that is, either the title or the
558 * value entity) will be greater than @width, it will be rendered
559 * outside the cell boundary box, luckely overwriting the adiacent
562 * Using %0 as @width means the width of the cell will be automatically
563 * adjusted to the maximum width of its content.
565 * Negative width values are not allowed: this condition will raise
566 * a warning without any further processing.
568 * Returns: the newly created cell or %NULL on errors
571 adg_table_cell_new(AdgTableRow
*row
, gdouble width
)
573 g_return_val_if_fail(row
!= NULL
, NULL
);
574 g_return_val_if_fail(width
>= 0, NULL
);
576 return cell_new(row
, NULL
, width
, FALSE
, NULL
, NULL
, NULL
);
580 * adg_table_cell_new_before:
581 * @cell: a valid #AdgTableCell
582 * @width: width of the cell
584 * Creates a new cell and inserts it rigthly before the @cell cell.
585 * This works similarily and accepts the same parameters as the
586 * adg_table_cell_new() function.
588 * Returns: the newly created cell or %NULL on errors
591 adg_table_cell_new_before(AdgTableCell
*cell
, gdouble width
)
593 g_return_val_if_fail(cell
!= NULL
, NULL
);
594 g_return_val_if_fail(cell
->row
!= NULL
, NULL
);
595 g_return_val_if_fail(width
>= 0, NULL
);
597 return cell_new(cell
->row
, cell
, width
, FALSE
, NULL
, NULL
, NULL
);
601 * adg_table_cell_new_full:
602 * @row: a valid #AdgTableRow
603 * @width: width of the cell
604 * @name: name to associate
605 * @title: title to render
606 * @value: value to render
608 * A convenient function to append a framed cell to @row with a
609 * specific title and value text. The font to use for rendering
610 * @title and @value will be picked up from the table style, so
611 * be sure to have the correct table dress set before calling
614 * @row and @width have the same meanings as in adg_table_cell_new():
615 * check its documentation for details.
617 * @name is an optional identifier to univoquely access this cell
618 * by using adg_table_get_cell(). The identifier must be univoque:
619 * if there is yet a cell with the same name a warning message will
620 * be raised and the function will fail.
622 * Returns: the newly created cell or %NULL on errors
625 adg_table_cell_new_full(AdgTableRow
*row
, gdouble width
, const gchar
*name
,
626 const gchar
*title
, const gchar
*value
)
628 AdgEntity
*table_entity
, *title_entity
, *value_entity
;
629 AdgTableStyle
*table_style
;
630 AdgDress table_dress
, font_dress
;
632 g_return_val_if_fail(row
!= NULL
, NULL
);
634 table_entity
= (AdgEntity
*) row
->table
;
635 table_dress
= adg_table_get_table_dress(row
->table
);
636 table_style
= (AdgTableStyle
*) adg_entity_style(table_entity
, table_dress
);
639 font_dress
= adg_table_style_get_title_dress(table_style
);
640 title_entity
= g_object_new(ADG_TYPE_TOY_TEXT
,
642 "font-dress", font_dress
, NULL
);
648 font_dress
= adg_table_style_get_value_dress(table_style
);
649 value_entity
= g_object_new(ADG_TYPE_TOY_TEXT
,
651 "font-dress", font_dress
, NULL
);
656 return cell_new(row
, NULL
, width
, TRUE
, name
, title_entity
, value_entity
);
660 * adg_table_cell_delete:
661 * @cell: a valid #AdgTableCell
663 * Deletes @cell removing it from the container row and freeing
664 * any resource associated to it.
667 adg_table_cell_delete(AdgTableCell
*cell
)
671 g_return_if_fail(cell
!= NULL
);
675 g_return_if_fail(row
!= NULL
);
678 row
->cells
= g_slist_remove(row
->cells
, cell
);
682 * adg_table_cell_get_name:
683 * @cell: a valid #AdgTableCell
685 * Gets the name assigned to @cell. This function is highly inefficient
686 * as the cell names are stored in a hash table optimized to get a cell
687 * from a name. Getting the name from a cell involves a full hash table
690 * Returns: the name bound of @cell or %NULL on no name or errors
693 adg_table_cell_get_name(AdgTableCell
*cell
)
695 AdgTablePrivate
*data
;
697 g_return_val_if_fail(cell
!= NULL
, NULL
);
699 data
= cell
->row
->table
->data
;
701 return g_hash_table_find(data
->cell_names
, value_match
, cell
);
705 * adg_table_cell_set_name:
706 * @cell: a valid #AdgTableCell
707 * @name: the new name of @cell
709 * Sets a new name on @cell: this will allow to access @cell by
710 * name at a later time using the adg_table_get_cell() API.
713 adg_table_cell_set_name(AdgTableCell
*cell
, const gchar
*name
)
715 AdgTablePrivate
*data
;
717 g_return_if_fail(cell
!= NULL
);
719 data
= cell
->row
->table
->data
;
721 cell_set_name(cell
, NULL
);
722 cell_set_name(cell
, name
);
726 * adg_table_cell_get_title:
727 * @cell: a valid #AdgTableCell
729 * Gets the current title of @cell. The returned string is owned
730 * by @cell and must not be modified or freed.
732 * Returns: the title entity or %NULL for undefined title
735 adg_table_cell_get_title(AdgTableCell
*cell
)
737 g_return_val_if_fail(cell
!= NULL
, NULL
);
743 * adg_table_cell_set_title:
744 * @cell: a valid #AdgTableCell
745 * @title: the new title entity
747 * Sets @title as the new title entity of @cell. The top left
748 * corner of the bounding box of @title will be cohincident to
749 * the top left corner of the cell extents, taking into accounts
750 * eventual padding spaces specified by the table style.
752 * The old internal entity is unrefenrenced while the @title (if
753 * not %NULL) is refenenced with g_object_ref_sink().
755 * @title can be %NULL, in which case the old entity is removed.
758 adg_table_cell_set_title(AdgTableCell
*cell
, AdgEntity
*title
)
760 g_return_if_fail(cell
!= NULL
);
761 g_return_if_fail(title
== NULL
|| ADG_IS_ENTITY(title
));
763 if (cell_set_title(cell
, title
))
764 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
768 * adg_table_cell_set_text_title:
769 * @cell: a valid #AdgTableCell
770 * @title: a text string
772 * Convenient function to set a the title of a cell using an #AdgToyText
773 * entity with the font dress picked from #AdgTable:table-dress with
774 * a call to adg_table_style_get_title_dress().
777 adg_table_cell_set_text_title(AdgTableCell
*cell
, const gchar
*title
)
781 AdgTableStyle
*table_style
;
782 AdgDress table_dress
, font_dress
;
784 g_return_if_fail(cell
!= NULL
);
787 adg_table_cell_set_title(cell
, NULL
);
789 if (cell
->title
!= NULL
) {
790 const gchar
*old_title
;
792 if (ADG_IS_TOY_TEXT(cell
->title
))
793 old_title
= adg_toy_text_get_label((AdgToyText
*) cell
->title
);
797 if (adg_strcmp(title
, old_title
) == 0)
801 table
= cell
->row
->table
;
802 table_dress
= adg_table_get_table_dress(table
);
803 table_style
= (AdgTableStyle
*) adg_entity_style((AdgEntity
*) table
,
805 font_dress
= adg_table_style_get_title_dress(table_style
);
806 entity
= g_object_new(ADG_TYPE_TOY_TEXT
,
807 "local-method", ADG_MIX_PARENT
,
809 "font-dress", font_dress
, NULL
);
811 adg_table_cell_set_title(cell
, entity
);
815 * adg_table_cell_get_value:
816 * @cell: a valid #AdgTableCell
818 * Gets the current value of @cell. The returned string is owned
819 * by @cell and must not be modified or freed.
821 * Returns: the value entity or %NULL for undefined value
824 adg_table_cell_get_value(AdgTableCell
*cell
)
826 g_return_val_if_fail(cell
!= NULL
, NULL
);
832 * adg_table_cell_set_value:
833 * @cell: a valid #AdgTableCell
834 * @value: the new value entity
836 * Sets @value as the new value entity of @cell. The bottom middle
837 * point of the bounding box of @value will be cohincident to the
838 * bottom middle point of the cell extents, taking into accounts
839 * eventual padding spaces specified by the table style.
841 * The old internal entity is unrefenrenced while the @value (if
842 * not %NULL) is refenenced with g_object_ref_sink().
844 * @value can be %NULL, in which case the old entity is removed.
847 adg_table_cell_set_value(AdgTableCell
*cell
, AdgEntity
*value
)
849 g_return_if_fail(cell
!= NULL
);
850 g_return_if_fail(value
== NULL
|| ADG_IS_ENTITY(value
));
852 if (cell_set_value(cell
, value
))
853 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
857 * adg_table_cell_set_text_value:
858 * @cell: a valid #AdgTableCell
859 * @value: a text string
861 * Convenient function to set a the value of a cell using an #AdgToyText
862 * entity with a value font dress picked from #AdgTable:table-dress with
863 * a call to adg_table_style_get_value_dress().
866 adg_table_cell_set_text_value(AdgTableCell
*cell
, const gchar
*value
)
870 AdgTableStyle
*table_style
;
871 AdgDress table_dress
, font_dress
;
873 g_return_if_fail(cell
!= NULL
);
876 adg_table_cell_set_value(cell
, NULL
);
878 if (cell
->value
!= NULL
) {
879 const gchar
*old_value
;
881 if (ADG_IS_TOY_TEXT(cell
->value
))
882 old_value
= adg_toy_text_get_label((AdgToyText
*) cell
->value
);
886 if (adg_strcmp(value
, old_value
) == 0)
890 table
= cell
->row
->table
;
891 table_dress
= adg_table_get_table_dress(table
);
892 table_style
= (AdgTableStyle
*) adg_entity_style((AdgEntity
*) table
,
894 font_dress
= adg_table_style_get_value_dress(table_style
);
895 entity
= g_object_new(ADG_TYPE_TOY_TEXT
,
896 "local-method", ADG_MIX_PARENT
,
898 "font-dress", font_dress
, NULL
);
900 adg_table_cell_set_value(cell
, entity
);
904 * adg_table_cell_set_width:
905 * @cell: a valid #AdgTableCell
906 * @width: the new width
908 * Sets a new width on @cell. The extents on the whole table
909 * will be invalidated, so will be recomputed in the next
913 adg_table_cell_set_width(AdgTableCell
*cell
, gdouble width
)
915 g_return_if_fail(cell
!= NULL
);
919 adg_entity_invalidate((AdgEntity
*) cell
->row
->table
);
923 * adg_table_cell_has_frame:
924 * @cell: a valid #AdgTableCell
926 * Gets the frame flag of @cell.
928 * Returns: the frame flag
931 adg_table_cell_has_frame(AdgTableCell
*cell
)
933 g_return_val_if_fail(cell
!= NULL
, FALSE
);
935 return cell
->has_frame
;
939 * adg_table_cell_switch_frame:
940 * @cell: a valid #AdgTableCell
941 * @state: the new frame state
943 * Sets the frame flag of @cell: if @state is %TRUE, a frame around
944 * @cell will be rendered using the #AdgTableStyle:cell-dress dress
945 * of the table style.
948 adg_table_cell_switch_frame(AdgTableCell
*cell
, gboolean state
)
950 AdgTablePrivate
*data
;
952 g_return_if_fail(cell
!= NULL
);
954 if (cell
->has_frame
== state
)
957 data
= cell
->row
->table
->data
;
958 cell
->has_frame
= state
;
960 if (data
->grid
!= NULL
) {
961 g_object_unref(data
->grid
);
967 * adg_table_cell_extents:
968 * @cell: a valid #AdgTableCell
970 * Gets the extents of @cell. This function is useful only after the
971 * arrange() phase as in the other situation the extents will likely
974 * Returns: the extents of @cell or %NULL on errors
977 adg_table_cell_extents(AdgTableCell
*cell
)
979 g_return_val_if_fail(cell
!= NULL
, NULL
);
981 return &cell
->extents
;
986 global_changed(AdgEntity
*entity
)
988 PARENT_ENTITY_CLASS
->global_changed(entity
);
989 propagate((AdgTable
*) entity
, "global-changed");
993 local_changed(AdgEntity
*entity
)
995 PARENT_ENTITY_CLASS
->local_changed(entity
);
996 propagate((AdgTable
*) entity
, "local-changed");
1000 invalidate(AdgEntity
*entity
)
1002 propagate((AdgTable
*) entity
, "invalidate");
1006 arrange(AdgEntity
*entity
)
1009 AdgTablePrivate
*data
;
1010 CpmlExtents extents
;
1011 const AdgPair
*spacing
;
1016 table
= (AdgTable
*) entity
;
1018 cpml_extents_copy(&extents
, adg_entity_extents(entity
));
1020 /* Resolve the table style */
1021 if (data
->table_style
== NULL
)
1022 data
->table_style
= (AdgTableStyle
*)
1023 adg_entity_style(entity
, data
->table_dress
);
1025 if (extents
.is_defined
) {
1026 if (data
->grid
!= NULL
)
1027 adg_entity_arrange((AdgEntity
*) data
->grid
);
1028 if (data
->frame
!= NULL
)
1029 adg_entity_arrange((AdgEntity
*) data
->frame
);
1033 spacing
= adg_table_style_get_cell_spacing(data
->table_style
);
1037 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1038 row
= row_node
->data
;
1040 row_arrange_size(row
);
1042 if (row
->extents
.size
.x
> extents
.size
.x
)
1043 extents
.size
.x
= row
->extents
.size
.x
;
1044 extents
.size
.y
+= row
->extents
.size
.y
;
1047 /* TODO: update the org according to the table alignments */
1049 y
= extents
.org
.y
+ spacing
->y
;
1050 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1051 row
= row_node
->data
;
1053 row
->extents
.org
.x
= extents
.org
.x
;
1054 row
->extents
.org
.y
= y
;
1058 y
+= row
->extents
.size
.y
+ spacing
->y
;
1061 extents
.is_defined
= TRUE
;
1062 adg_entity_set_extents(entity
, &extents
);
1064 arrange_grid(entity
);
1065 arrange_frame(entity
);
1069 arrange_grid(AdgEntity
*entity
)
1071 AdgTablePrivate
*data
;
1074 GSList
*row_node
, *cell_node
;
1080 data
= ((AdgTable
*) entity
)->data
;
1082 if (data
->grid
!= NULL
)
1085 path
= adg_path_new();
1086 trail
= (AdgTrail
*) path
;
1088 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1089 row
= row_node
->data
;
1091 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1092 cell
= cell_node
->data
;
1094 if (!cell
->has_frame
)
1097 cpml_pair_copy(&pair
, &cell
->extents
.org
);
1098 adg_path_move_to(path
, &pair
);
1099 pair
.x
+= cell
->extents
.size
.x
;
1100 adg_path_line_to(path
, &pair
);
1101 pair
.y
+= cell
->extents
.size
.y
;
1102 adg_path_line_to(path
, &pair
);
1103 pair
.x
-= cell
->extents
.size
.x
;
1104 adg_path_line_to(path
, &pair
);
1105 adg_path_close(path
);
1109 if (!adg_trail_extents(trail
)->is_defined
)
1112 dress
= adg_table_style_get_grid_dress(data
->table_style
);
1113 data
->grid
= g_object_new(ADG_TYPE_STROKE
,
1114 "local-method", ADG_MIX_PARENT
,
1115 "line-dress", dress
,
1117 "parent", entity
, NULL
);
1118 adg_entity_arrange((AdgEntity
*) data
->grid
);
1122 arrange_frame(AdgEntity
*entity
)
1124 AdgTablePrivate
*data
;
1126 const CpmlExtents
*extents
;
1130 data
= ((AdgTable
*) entity
)->data
;
1132 if (data
->frame
!= NULL
|| !data
->has_frame
)
1135 path
= adg_path_new();
1136 extents
= adg_entity_extents(entity
);
1138 cpml_pair_copy(&pair
, &extents
->org
);
1139 adg_path_move_to(path
, &pair
);
1140 pair
.x
+= extents
->size
.x
;
1141 adg_path_line_to(path
, &pair
);
1142 pair
.y
+= extents
->size
.y
;
1143 adg_path_line_to(path
, &pair
);
1144 pair
.x
-= extents
->size
.x
;
1145 adg_path_line_to(path
, &pair
);
1146 adg_path_close(path
);
1148 dress
= adg_table_style_get_frame_dress(data
->table_style
);
1150 data
->frame
= g_object_new(ADG_TYPE_STROKE
,
1151 "local-method", ADG_MIX_PARENT
,
1152 "line-dress", dress
,
1153 "trail", (AdgTrail
*) path
,
1154 "parent", entity
, NULL
);
1155 adg_entity_arrange((AdgEntity
*) data
->frame
);
1159 render(AdgEntity
*entity
, cairo_t
*cr
)
1161 cairo_set_matrix(cr
, adg_entity_ctm(entity
));
1163 propagate((AdgTable
*) entity
, "render", cr
);
1167 switch_frame(AdgTable
*table
, gboolean state
)
1169 AdgTablePrivate
*data
= table
->data
;
1171 if (data
->has_frame
== state
)
1174 data
->has_frame
= state
;
1176 if (data
->frame
!= NULL
) {
1177 g_object_unref(data
->frame
);
1185 propagate(AdgTable
*table
, const gchar
*detailed_signal
, ...)
1189 va_list var_args
, var_copy
;
1190 AdgTablePrivate
*data
;
1196 if (!g_signal_parse_name(detailed_signal
, G_TYPE_FROM_INSTANCE(table
),
1197 &signal_id
, &detail
, FALSE
)) {
1198 g_assert_not_reached();
1201 va_start(var_args
, detailed_signal
);
1204 if (data
->frame
!= NULL
) {
1205 G_VA_COPY(var_copy
, var_args
);
1206 g_signal_emit_valist(data
->frame
, signal_id
, detail
, var_copy
);
1209 if (data
->grid
!= NULL
) {
1210 G_VA_COPY(var_copy
, var_args
);
1211 g_signal_emit_valist(data
->grid
, signal_id
, detail
, var_copy
);
1214 for (row_node
= data
->rows
; row_node
; row_node
= row_node
->next
) {
1215 row
= row_node
->data
;
1217 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1218 cell
= cell_node
->data
;
1220 if (cell
->title
!= NULL
) {
1221 G_VA_COPY(var_copy
, var_args
);
1222 g_signal_emit_valist(cell
->title
, signal_id
, detail
, var_copy
);
1225 if (cell
->value
!= NULL
) {
1226 G_VA_COPY(var_copy
, var_args
);
1227 g_signal_emit_valist(cell
->value
, signal_id
, detail
, var_copy
);
1235 static AdgTableRow
*
1236 row_new(AdgTable
*table
, AdgTableRow
*before_row
)
1238 AdgTablePrivate
*data
;
1239 AdgTableRow
*new_row
;
1242 new_row
= g_new(AdgTableRow
, 1);
1243 new_row
->table
= table
;
1244 new_row
->cells
= NULL
;
1245 new_row
->height
= 0;
1246 new_row
->extents
.is_defined
= FALSE
;
1248 if (before_row
== NULL
) {
1249 data
->rows
= g_slist_append(data
->rows
, new_row
);
1251 GSList
*before_node
= g_slist_find(data
->rows
, before_row
);
1253 /* This MUST be present, otherwise something really bad happened */
1254 g_assert(before_node
!= NULL
);
1256 data
->rows
= g_slist_insert_before(data
->rows
, before_node
, new_row
);
1259 invalidate((AdgEntity
*) table
);
1265 row_arrange_size(AdgTableRow
*row
)
1267 AdgTableStyle
*table_style
;
1268 const AdgPair
*spacing
;
1273 table_style
= GET_TABLE_STYLE(row
->table
);
1274 spacing
= adg_table_style_get_cell_spacing(table_style
);
1275 size
= &row
->extents
.size
;
1278 if (row
->height
== 0)
1279 size
->y
= adg_table_style_get_row_height(table_style
);
1281 size
->y
= row
->height
;
1283 /* Compute the row width by summing every cell width */
1284 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1285 cell
= cell_node
->data
;
1287 cell_arrange_size(cell
);
1289 size
->x
+= cell
->extents
.size
.x
+ spacing
->x
;
1293 size
->x
+= spacing
->x
;
1296 /* Before calling this function, row->extents should be updated */
1298 row_arrange(AdgTableRow
*row
)
1300 AdgTableStyle
*table_style
;
1301 const AdgPair
*spacing
;
1307 table_style
= GET_TABLE_STYLE(row
->table
);
1308 spacing
= adg_table_style_get_cell_spacing(table_style
);
1309 org
= &row
->extents
.org
;
1310 x
= org
->x
+ spacing
->x
;
1312 for (cell_node
= row
->cells
; cell_node
; cell_node
= cell_node
->next
) {
1313 cell
= cell_node
->data
;
1315 cell
->extents
.org
.x
= x
;
1316 cell
->extents
.org
.y
= org
->y
;
1320 x
+= cell
->extents
.size
.x
+ spacing
->x
;
1323 row
->extents
.is_defined
= TRUE
;
1327 row_dispose(AdgTableRow
*row
)
1329 g_slist_foreach(row
->cells
, (GFunc
) cell_dispose
, NULL
);
1333 row_free(AdgTableRow
*row
)
1335 g_slist_foreach(row
->cells
, (GFunc
) cell_free
, NULL
);
1336 g_slist_free(row
->cells
);
1341 static AdgTableCell
*
1342 cell_new(AdgTableRow
*row
, AdgTableCell
*before_cell
,
1343 gdouble width
, gboolean has_frame
,
1344 const gchar
*name
, AdgEntity
*title
, AdgEntity
*value
)
1346 AdgTablePrivate
*data
;
1347 AdgTableCell
*new_cell
;
1349 data
= row
->table
->data
;
1351 if (name
!= NULL
&& data
->cell_names
!= NULL
&&
1352 g_hash_table_lookup(data
->cell_names
, name
) != NULL
) {
1353 g_warning(_("%s: `%s' cell name is yet used"), G_STRLOC
, name
);
1357 new_cell
= g_new(AdgTableCell
, 1);
1358 new_cell
->row
= row
;
1359 new_cell
->width
= width
;
1360 new_cell
->has_frame
= has_frame
;
1361 new_cell
->title
= NULL
;
1362 new_cell
->value
= NULL
;
1363 new_cell
->extents
.is_defined
= FALSE
;
1365 cell_set_title(new_cell
, title
);
1366 cell_set_value(new_cell
, value
);
1368 if (before_cell
== NULL
) {
1369 row
->cells
= g_slist_append(row
->cells
, new_cell
);
1371 GSList
*before_node
= g_slist_find(row
->cells
, before_cell
);
1373 /* This MUST be not null, otherwise something really bad happened */
1374 g_assert(before_node
!= NULL
);
1376 row
->cells
= g_slist_insert_before(row
->cells
, before_node
, new_cell
);
1380 cell_set_name(new_cell
, name
);
1386 cell_set_name(AdgTableCell
*cell
, const gchar
*name
)
1388 AdgTablePrivate
*data
= cell
->row
->table
->data
;
1390 if (data
->cell_names
== NULL
&& name
== NULL
)
1393 if (data
->cell_names
== NULL
)
1394 data
->cell_names
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1398 g_hash_table_foreach_remove(data
->cell_names
, value_match
, cell
);
1400 g_hash_table_insert(data
->cell_names
, g_strdup(name
), cell
);
1404 cell_set_title(AdgTableCell
*cell
, AdgEntity
*title
)
1406 if (cell
->title
== title
)
1409 if (cell
->title
!= NULL
)
1410 g_object_unref(cell
->title
);
1412 cell
->title
= title
;
1414 if (title
!= NULL
) {
1415 g_object_ref_sink(title
);
1416 adg_entity_set_parent(title
, (AdgEntity
*) cell
->row
->table
);
1417 adg_entity_set_local_method(title
, ADG_MIX_PARENT
);
1424 cell_set_value(AdgTableCell
*cell
, AdgEntity
*value
)
1426 if (cell
->value
== value
)
1429 if (cell
->value
!= NULL
)
1430 g_object_unref(cell
->value
);
1432 cell
->value
= value
;
1434 if (value
!= NULL
) {
1435 g_object_ref_sink(value
);
1436 adg_entity_set_parent(value
, (AdgEntity
*) cell
->row
->table
);
1437 adg_entity_set_local_method(value
, ADG_MIX_PARENT
);
1444 cell_arrange_size(AdgTableCell
*cell
)
1446 AdgTableStyle
*table_style
;
1449 table_style
= GET_TABLE_STYLE(cell
->row
->table
);
1450 size
= &cell
->extents
.size
;
1452 if (cell
->title
!= NULL
)
1453 adg_entity_arrange(cell
->title
);
1455 if (cell
->value
!= NULL
)
1456 adg_entity_arrange(cell
->value
);
1458 size
->y
= cell
->row
->extents
.size
.y
;
1460 if (cell
->width
== 0) {
1461 const CpmlVector
*content_size
;
1463 /* The width depends on the cell content (default: 0) */
1466 if (cell
->title
!= NULL
) {
1467 content_size
= &adg_entity_extents(cell
->title
)->size
;
1468 size
->x
= content_size
->x
;
1471 if (cell
->value
!= NULL
) {
1472 content_size
= &adg_entity_extents(cell
->value
)->size
;
1473 if (content_size
->x
> size
->x
)
1474 size
->x
= content_size
->x
;
1477 size
->x
+= adg_table_style_get_cell_padding(table_style
)->x
* 2;
1479 size
->x
= cell
->width
;
1483 /* Before calling this function, cell->extents should be updated */
1485 cell_arrange(AdgTableCell
*cell
)
1487 AdgTableStyle
*table_style
;
1488 const AdgPair
*padding
;
1489 const CpmlPair
*org
;
1490 const CpmlVector
*size
, *content_size
;
1493 table_style
= GET_TABLE_STYLE(cell
->row
->table
);
1494 padding
= adg_table_style_get_cell_padding(table_style
);
1495 org
= &cell
->extents
.org
;
1496 size
= &cell
->extents
.size
;
1498 if (cell
->title
!= NULL
) {
1499 content_size
= &adg_entity_extents(cell
->title
)->size
;
1501 cairo_matrix_init_translate(&map
,
1502 org
->x
+ padding
->x
,
1503 org
->y
+ content_size
->y
+ padding
->y
);
1505 adg_entity_set_global_map(cell
->title
, &map
);
1508 if (cell
->value
!= NULL
) {
1509 AdgMatrix global_map
;
1510 CpmlExtents extents
;
1512 adg_entity_get_global_map(cell
->value
, &global_map
);
1513 cpml_extents_copy(&extents
, adg_entity_extents(cell
->value
));
1514 cpml_extents_transform(&extents
, &global_map
);
1516 cairo_matrix_init_translate(&map
,
1517 org
->x
+ (size
->x
- extents
.size
.x
) / 2,
1518 org
->y
+ size
->y
/* - padding->y */);
1520 adg_entity_set_local_map(cell
->value
, &map
);
1523 cell
->extents
.is_defined
= TRUE
;
1527 cell_dispose(AdgTableCell
*cell
)
1529 cell_set_title(cell
, NULL
);
1530 cell_set_value(cell
, NULL
);
1534 cell_free(AdgTableCell
*cell
)
1536 cell_set_name(cell
, NULL
);
1542 value_match(gpointer key
, gpointer value
, gpointer user_data
)
1544 return value
== user_data
;