diff --git a/src/adg/adg-cairo-fallback.c b/src/adg/adg-cairo-fallback.c index 6ddfbe6a..b9b04161 100644 --- a/src/adg/adg-cairo-fallback.c +++ b/src/adg/adg-cairo-fallback.c @@ -1,78 +1,78 @@ /* ADG - Automatic Drawing Generation * Copyright (C) 2007-2020 Nicola Fontana * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "adg-internal.h" #include #include #include "adg-cairo-fallback.h" #if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 14, 0) void cairo_surface_set_device_scale(cairo_surface_t *surface, double x_scale, double y_scale) { } #endif /* cairo < 1.14.0 */ #ifdef ADG_MISSING_GBOXED_MATRIX GType cairo_gobject_matrix_get_type(void) { static GType matrix_type = 0; if (G_UNLIKELY(matrix_type == 0)) matrix_type = g_boxed_type_register_static("CairoMatrix", (GBoxedCopyFunc) cairo_gobject_cairo_matrix_copy, g_free); return matrix_type; } cairo_matrix_t * cairo_gobject_cairo_matrix_copy(const cairo_matrix_t *matrix) { - return g_memdup(matrix, sizeof(cairo_matrix_t)); + return cpml_memdup(matrix, sizeof(cairo_matrix_t)); } #endif /* ADG_MISSING_GBOXED_MATRIX */ #ifdef ADG_MISSING_GBOXED_PATTERN GType cairo_gobject_pattern_get_type(void) { static GType pattern_type = 0; if (G_UNLIKELY(pattern_type == 0)) pattern_type = g_boxed_type_register_static("CairoPattern", (GBoxedCopyFunc) cairo_pattern_reference, (GBoxedFreeFunc) cairo_pattern_destroy); return pattern_type; } #endif /* ADG_MISSING_GBOXED_PATTERN */ diff --git a/src/adg/adg-dash.c b/src/adg/adg-dash.c index 559d712e..905230b2 100644 --- a/src/adg/adg-dash.c +++ b/src/adg/adg-dash.c @@ -1,373 +1,373 @@ /* ADG - Automatic Drawing Generation * Copyright (C) 2007-2020 Nicola Fontana * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:adg-dash * @Section_Id:AdgDash * @title: AdgDash * @short_description: Dash pattern for line stroking * * The #AdgDash boxed type wraps the values needed by cairo to * univoquely identify a dash pattern, an array of positive values. Each * value provides the length of alternate "on" and "off" portions of the * stroke. The offset specifies an offset into the pattern at which the * stroke begins. * * Each "on" segment will have caps applied as if the segment were a * separate sub-path. In particular, it is valid to use an "on" length of * 0 with %CAIRO_LINE_CAP_ROUND or %CAIRO_LINE_CAP_SQUARE in order to * distribute dots or squares along a path. * * Check the cairo_set_dash() documentation for further details: * http://www.cairographics.org/manual/cairo-cairo-t.html#cairo-set-dash * * Since: 1.0 **/ /** * AdgDash: * * All fields are private and should not be used directly. * Use its public methods instead. * * Since: 1.0 **/ #include "adg-internal.h" #include "adg-dash.h" #include "adg-dash-private.h" #include GType adg_dash_get_type(void) { static GType dash_type = 0; if (G_UNLIKELY(dash_type == 0)) dash_type = g_boxed_type_register_static("AdgDash", (GBoxedCopyFunc) adg_dash_dup, (GBoxedFreeFunc) adg_dash_destroy); return dash_type; } /** * adg_dash_dup: * @src: an #AdgDash instance * * Duplicates @src. The returned value must be freed with adg_dash_destroy() * when no longer needed. * * Returns: (transfer full): the duplicate of @src. * * Since: 1.0 **/ AdgDash * adg_dash_dup(const AdgDash *src) { AdgDash *dash; g_return_val_if_fail(src != NULL, NULL); - dash = g_memdup(src, sizeof(AdgDash)); - dash->dashes = g_memdup(src->dashes, sizeof(gdouble) * src->num_dashes); + dash = cpml_memdup(src, sizeof(AdgDash)); + dash->dashes = cpml_memdup(src->dashes, sizeof(gdouble) * src->num_dashes); return dash; } /** * adg_dash_new: * * Creates a new empty dash pattern. * * Returns: (transfer full): the newly created dash pattern. * * Since: 1.0 **/ AdgDash * adg_dash_new(void) { AdgDash *dash = g_new(AdgDash, 1); dash->dashes = NULL; dash->num_dashes = 0; dash->offset = 0; return dash; } /** * adg_dash_new_with_dashes: * @num_dashes: the number of dashes to set * @...: lengths (a list of double values) of each dash * * Creates a new dash pattern, presetting some dashes on it. * * Returns: (transfer full): the newly created dash pattern. * * Since: 1.0 **/ AdgDash * adg_dash_new_with_dashes(gint num_dashes, ...) { AdgDash *dash; va_list var_args; va_start(var_args, num_dashes); dash = adg_dash_new_with_dashes_valist(num_dashes, var_args); va_end(var_args); return dash; } /** * adg_dash_new_with_dashes_valist: * @num_dashes: number of dashes to append * @var_args: a va_list containing @num_dashes list of #gdouble * * Variadic version of adg_dash_new_with_dashes(). * * Returns: (transfer full): the newly created dash pattern. * * Since: 1.0 **/ AdgDash * adg_dash_new_with_dashes_valist(gint num_dashes, va_list var_args) { AdgDash *dash = adg_dash_new(); adg_dash_append_dashes_valist(dash, num_dashes, var_args); return dash; } /** * adg_dash_new_with_dashes_array: (rename-to adg_dash_new_with_dashes) * @num_dashes: number of dashes to append * @dashes: (array length=num_dashes): array of @num_dashes gdoubles * * Array version of adg_dash_new_with_dashes(). * * Returns: (transfer full): the newly created dash pattern. * * Since: 1.0 **/ AdgDash * adg_dash_new_with_dashes_array(gint num_dashes, const gdouble *dashes) { AdgDash *dash = adg_dash_new(); adg_dash_append_dashes_array(dash, num_dashes, dashes); return dash; } /** * adg_dash_append_dash: * @dash: an #AdgDash instance * @length: the length value * * Appends to the @dash pattern a new dash of the specified @length value. * * Since: 1.0 **/ void adg_dash_append_dash(AdgDash *dash, gdouble length) { g_return_if_fail(dash != NULL); ++ dash->num_dashes; dash->dashes = g_realloc(dash->dashes, sizeof(gdouble) * dash->num_dashes); dash->dashes[dash->num_dashes - 1] = length; } /** * adg_dash_append_dashes: * @dash: an #AdgDash instance * @num_dashes: number of dashes to append * @...: a @num_dashes list of #gdouble * * Appends to the current @dash pattern @num_dashes number of dashes. * The length of each dash must be specified as gdouble in the arguments. * * Since: 1.0 **/ void adg_dash_append_dashes(AdgDash *dash, gint num_dashes, ...) { va_list var_args; va_start(var_args, num_dashes); adg_dash_append_dashes_valist(dash, num_dashes, var_args); va_end(var_args); } /** * adg_dash_append_dashes_valist: * @dash: an #AdgDash instance * @num_dashes: number of dashes to append * @var_args: a va_list containing @num_dashes list of #gdouble * * Variadic version of adg_dash_append_dashes(). * * Since: 1.0 **/ void adg_dash_append_dashes_valist(AdgDash *dash, gint num_dashes, va_list var_args) { gdouble length; g_return_if_fail(dash != NULL); g_return_if_fail(num_dashes > 0); while (num_dashes --) { length = va_arg(var_args, gdouble); adg_dash_append_dash(dash, length); } } /** * adg_dash_append_dashes_array: (rename-to adg_dash_append_dashes) * @dash: an #AdgDash instance * @num_dashes: number of dashes to append * @dashes: (array length=num_dashes): array of @num_dashes gdoubles * * Array version of adg_dash_append_dashes(). * * Since: 1.0 **/ void adg_dash_append_dashes_array(AdgDash *dash, gint num_dashes, const gdouble *dashes) { g_return_if_fail(dash != NULL); if (num_dashes > 0) { gint old_dashes = dash->num_dashes; dash->num_dashes += num_dashes; dash->dashes = g_realloc(dash->dashes, sizeof(gdouble) * dash->num_dashes); memcpy(dash->dashes + old_dashes, dashes, sizeof(gdouble) * num_dashes); } } /** * adg_dash_get_num_dashes: * @dash: an #AdgDash instance * * Gets the number of dashes stored inside this dash pattern. * * Returns: the number of dashes or -1 if @dash is invalid. * * Since: 1.0 **/ gint adg_dash_get_num_dashes(const AdgDash *dash) { g_return_val_if_fail(dash != NULL, -1); return dash->num_dashes; } /** * adg_dash_get_dashes: * @dash: an #AdgDash instance * * Gets the array of gdoubles containing the length of each dash of the * pattern of @dash. * * Returns: the array of lengths or NULL on invalid @dash. The array is owned by @dash and must not be modified or freed. * * Since: 1.0 **/ const gdouble * adg_dash_get_dashes(const AdgDash *dash) { g_return_val_if_fail(dash != NULL, NULL); return dash->dashes; } /** * adg_dash_clear_dashes: * @dash: an #AdgDash instance * * Resets the dashes of @dash, effectively clearing the pattern. * * Since: 1.0 **/ void adg_dash_clear_dashes(AdgDash *dash) { g_return_if_fail(dash != NULL); g_free(dash->dashes); dash->dashes = NULL; dash->num_dashes = 0; } /** * adg_dash_set_offset: * @dash: an #AdgDash instance * @offset: the new offset value * * Sets the pattern offset of @dash to @offset. * * Since: 1.0 **/ void adg_dash_set_offset(AdgDash *dash, gdouble offset) { g_return_if_fail(dash != NULL); dash->offset = offset; } /** * adg_dash_get_offset: * @dash: an #AdgDash instance * * Gets the offset of the pattern in @dash. * * Returns: the offset of @dash or 0 on invalid @dash. * * Since: 1.0 **/ gdouble adg_dash_get_offset(const AdgDash *dash) { g_return_val_if_fail(dash != NULL, 0); return dash->offset; } /** * adg_dash_destroy: * @dash: an #AdgDash instance * * Destroys @dash, freeing every resource owned by it. After the destruction * @dash cannot be used anymore. * * Since: 1.0 **/ void adg_dash_destroy(AdgDash *dash) { g_return_if_fail(dash != NULL); g_free(dash->dashes); g_free(dash); } diff --git a/src/adg/adg-dress.c b/src/adg/adg-dress.c index 6dacc64a..95afce48 100644 --- a/src/adg/adg-dress.c +++ b/src/adg/adg-dress.c @@ -1,650 +1,650 @@ /* ADG - Automatic Drawing Generation * Copyright (C) 2007-2020 Nicola Fontana * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:adg-dress * @Section_Id:Dresses * @title: AdgDress * @short_description: The ADG way to associate styles to entities * * The dress is a virtualization of an #AdgStyle instance. #AdgEntity * objects do not directly refer to #AdgStyle but use #AdgDress values * instead. This allows some advanced operations, such as overriding * a dress only in a specific entity branch of the hierarchy or * customize multiple entities at once. * * Since: 1.0 **/ /** * AdgDress: * @ADG_DRESS_UNDEFINED: undefined dress, used for notifying invalid * dresses. * @ADG_DRESS_COLOR: default built-in color. This is a * pass-through dress, that is it does not * change the cairo context when it is * applied. This dress will be resolved to an * #AdgColorStyle instance. * @ADG_DRESS_COLOR_BACKGROUND: default built-in color to be used as the * #AdgCanvas background. This dress will be * resolved to an #AdgColorStyle instance. * @ADG_DRESS_COLOR_STROKE: default built-in color for #AdgStroke * entities. This dress will be resolved to * an #AdgColorStyle instance. * @ADG_DRESS_COLOR_DIMENSION: built-in color used by default in * #AdgDimStyle. This dress will be resolved * to an #AdgColorStyle instance. * @ADG_DRESS_COLOR_ANNOTATION: built-in color used for rendering * helper entities such as #AdgToyText, * #AdgTable and #AdgTitleBlock. This dress * will be resolved to an #AdgColorStyle * instance. * @ADG_DRESS_COLOR_FILL: built-in color used by default by * #AdgFillStyle based styles. This dress * will be resolved to an #AdgColorStyle * instance. * @ADG_DRESS_COLOR_AXIS: default built-in color for stroking * #ADG_DRESS_LINE_AXIS lines. This dress * will be resolved to an #AdgColorStyle * instance. * @ADG_DRESS_COLOR_HIDDEN: default built-in color for stroking * #ADG_DRESS_LINE_HIDDEN lines. This dress * will be resolved to an #AdgColorStyle * instance. * @ADG_DRESS_LINE: default built-in line. This is a * pass-through dress, that is it does not * change the cairo context when it is * applied. This dress will be resolved to * an #AdgLineStyle instance. * @ADG_DRESS_LINE_STROKE: built-in line type to be used by default * for rendering #AdgStroke entities. * This dress will be resolved to an * #AdgLineStyle instance. * @ADG_DRESS_LINE_DIMENSION: built-in line type used by default for * rendering base and extension lines of * dimensions. This dress will be resolved * to an #AdgLineStyle instance. * @ADG_DRESS_LINE_FILL: built-in line type used by #AdgFillStyle. * This dress will be resolved to an * #AdgLineStyle instance. * @ADG_DRESS_LINE_GRID: built-in line type used for rendering * the grid of #AdgTable entities, that is * the frame of the cells. This dress will * be resolved to an #AdgLineStyle instance. * @ADG_DRESS_LINE_FRAME: built-in line type used for rendering the * frame of #AdgTable entities, that is the * frame around the whole table. This dress * will be resolved to an #AdgLineStyle * instance. * @ADG_DRESS_LINE_AXIS: built-in line type used for rendering axis * and centerlines. This dress will be * resolved to an #AdgLineStyle instance. * @ADG_DRESS_LINE_HIDDEN: built-in line type used for rendering * hidden lines and edges. This dress will be * resolved to an #AdgLineStyle instance. * @ADG_DRESS_FONT: default built-in font. This dress will be * resolved to an #AdgFontStyle instance. * @ADG_DRESS_FONT_TEXT: built-in font used by default for * rendering common text such as #AdgToyText * or the value of #AdgTable entities. This * dress will be resolved to an #AdgFontStyle * instance. * @ADG_DRESS_FONT_ANNOTATION: built-in font used for rendering auxiliary * text, such as the titles on #AdgTable * entities. This dress will be resolved to * an #AdgFontStyle instance. * @ADG_DRESS_FONT_QUOTE_TEXT: built-in font used for rendering regular * text on dimension entities, such as the * nominal value and the notes of a quote. * This dress will be resolved to an * #AdgFontStyle instance. * @ADG_DRESS_FONT_QUOTE_ANNOTATION: built-in font used for rendering auxiliary * text on dimension entities, such as the * min and max limits of a quote. This dress * will be resolved to an #AdgFontStyle * instance. * @ADG_DRESS_DIMENSION: default built-in for dimensions. This * dress will be resolved to an #AdgDimStyle * instance. * @ADG_DRESS_FILL: default built-in for filling. This is a * pass-through dress, that is it does not * change the cairo context when it is * applied. This dress will be resolved to an * #AdgFillStyle derived instance. * @ADG_DRESS_FILL_HATCH: built-in dress used by default by * #AdgHatch instances. This dress will be * resolved to an #AdgFillStyle derived * instance. * @ADG_DRESS_TABLE: default built-in for tables. This dress * will be resolved to an #AdgTableStyle * derived instance. * * An index representing a virtual #AdgStyle. The ADG comes equipped * with some built-in dress. * * Since: 1.0 **/ /** * ADG_TYPE_DRESS: * * The type used to express a dress index. It is defined only for GObject * internal and should not be used directly (at least, as far as I know). * * Since: 1.0 **/ /** * ADG_VALUE_HOLDS_DRESS: * @value: a #GValue * * Checks whether a #GValue is actually holding an #AdgDress value or not. * * Returns: TRUE is @value is holding an #AdgDress, FALSE otherwise. * * Since: 1.0 **/ #include "adg-internal.h" #include "adg-dash.h" #include "adg-model.h" #include "adg-trail.h" #include "adg-marker.h" #include "adg-arrow.h" #include "adg-style.h" #include "adg-color-style.h" #include "adg-line-style.h" #include "adg-text-internal.h" #include "adg-dim-style.h" #include "adg-fill-style.h" #include "adg-ruled-fill.h" #include "adg-table-style.h" #include "adg-dress.h" #include "adg-dress-private.h" #define MM *2.83464566927 static GArray * _adg_data_array (void); static void _adg_data_register (AdgDress dress, AdgStyle *fallback, GType ancestor_type); static void _adg_data_register_builtins (void); static AdgDressPrivate *_adg_data_lookup (AdgDress dress); /** * adg_dress_from_name: * @name: the name of a dress * * Gets the dress bound to a @name string. No warnings are raised * if the dress is not found. * * Returns: the #AdgDress value or #ADG_DRESS_UNDEFINED if not found. * * Since: 1.0 **/ AdgDress adg_dress_from_name(const gchar *name) { GEnumClass *dress_class = g_type_class_ref(ADG_TYPE_DRESS); GEnumValue *enum_value = g_enum_get_value_by_name(dress_class, name); g_type_class_unref(dress_class); return enum_value != NULL ? enum_value->value : ADG_DRESS_UNDEFINED; } /** * adg_dress_are_related: * @dress1: an #AdgDress * @dress2: another #AdgDress * * Checks whether @dress1 and @dress2 are related, that is * if they have the same ancestor type as returned by * adg_dress_get_ancestor_type(). * * Returns: TRUE if the dresses are related, FALSE otherwise. * * Since: 1.0 **/ gboolean adg_dress_are_related(AdgDress dress1, AdgDress dress2) { GType ancestor_type1, ancestor_type2; ancestor_type1 = adg_dress_get_ancestor_type(dress1); if (ancestor_type1 <= 0) return FALSE; ancestor_type2 = adg_dress_get_ancestor_type(dress2); if (ancestor_type2 <= 0) return FALSE; return ancestor_type1 == ancestor_type2; } /** * adg_dress_set: * @dress: a pointer to an #AdgDress * @src: the source dress * * Copies @src in @dress. This operation can be successful only if * @dress is #ADG_DRESS_UNDEFINED or if it contains a dress related * to @src, i.e. adg_dress_are_related() returns TRUE. * * Returns: TRUE on copy done, FALSE on copy failed or not needed. * * Since: 1.0 **/ gboolean adg_dress_set(AdgDress *dress, AdgDress src) { if (*dress == src) return FALSE; if (*dress != ADG_DRESS_UNDEFINED && !adg_dress_are_related(*dress, src)) return FALSE; *dress = src; return TRUE; } /** * adg_dress_get_name: * @dress: an #AdgDress * * Gets the name associated to @dress. No warnings are raised if * @dress is not found. * * Returns: the requested name or NULL if not found. * * Since: 1.0 **/ const gchar * adg_dress_get_name(AdgDress dress) { GEnumClass *dress_class = g_type_class_ref(ADG_TYPE_DRESS); GEnumValue *enum_value = g_enum_get_value(dress_class, dress); g_type_class_unref(dress_class); return enum_value != NULL ? enum_value->value_name : NULL; } /** * adg_dress_get_ancestor_type: * @dress: an #AdgDress * * Gets the base type that should be present in every #AdgStyle * acceptable by @dress. No warnings are raised if @dress * is not found. * * Returns: the ancestor type or 0 on errors. * * Since: 1.0 **/ GType adg_dress_get_ancestor_type(AdgDress dress) { AdgDressPrivate *data = _adg_data_lookup(dress); return data != NULL ? data->ancestor_type : 0; } /** * adg_dress_set_fallback: * @dress: an #AdgDress * @fallback: (transfer full): the new fallback style * * Associates a new @fallback style to @dress. If the dress is * not valid, a warning message is raised and the function fails. * * @fallback is checked for compatibily with @dress. Any dress holds * an ancestor type: if this type is not found in the @fallback * hierarchy, a warning message is raised and the function fails. * * After a succesfull call, the reference to the previous fallback * (if any) is dropped while a new reference to @fallback is added. * * Since: 1.0 **/ void adg_dress_set_fallback(AdgDress dress, AdgStyle *fallback) { AdgDressPrivate *data = _adg_data_lookup(dress); g_return_if_fail(data != NULL); if (data->fallback == fallback) return; /* Check if the new fallback style is compatible with this dress */ if (fallback != NULL && !adg_dress_style_is_compatible(dress, fallback)) { g_warning(_("%s: the fallback style of '%s' dress (%d) must be a '%s' derived type, but a '%s' has been provided"), G_STRLOC, adg_dress_get_name(dress), dress, g_type_name(data->ancestor_type), g_type_name(G_TYPE_FROM_INSTANCE(fallback))); return; } if (data->fallback != NULL) g_object_unref(data->fallback); data->fallback = fallback; if (data->fallback != NULL) g_object_ref(data->fallback); } /** * adg_dress_get_fallback: * @dress: an #AdgDress * * Gets the fallback style associated to @dress. No warnings * are raised if the dress is not found. The returned style * is owned by dress and should not be freed or modified. * * Returns: (transfer none): the requested #AdgStyle derived instance or NULL if not set. * * Since: 1.0 **/ AdgStyle * adg_dress_get_fallback(AdgDress dress) { AdgDressPrivate *data = _adg_data_lookup(dress); return data != NULL ? data->fallback : NULL; } /** * adg_dress_style_is_compatible: * @dress: an #AdgDress * @style: (transfer none): the #AdgStyle to check * * Checks whether @style is compatible with @dress, that is if * @style has the ancestor style type (as returned by * adg_dress_get_ancestor_type()) in its hierarchy. * * Returns: TRUE if @dress can accept @style, FALSE otherwise. * * Since: 1.0 **/ gboolean adg_dress_style_is_compatible(AdgDress dress, AdgStyle *style) { GType ancestor_type = adg_dress_get_ancestor_type(dress); g_return_val_if_fail(ancestor_type > 0, FALSE); g_return_val_if_fail(ADG_IS_STYLE(style), FALSE); return G_TYPE_CHECK_INSTANCE_TYPE(style, ancestor_type); } static GArray * _adg_data_array(void) { /* The following register keeps track of the metadata bound to every * #AdgDress value, such as the fallback style and the ancestor type. * * The AdgDress value is cohincident with the index of its metadata * inside this register, that is if %ADG_DRESS_COLOR_BACKGROUND is 2, * array->data[2] will contain its metadata. */ static GArray *array = NULL; if (G_UNLIKELY(array == NULL)) { array = g_array_new(FALSE, FALSE, sizeof(AdgDressPrivate)); _adg_data_register_builtins(); } return array; } static void _adg_data_register(AdgDress dress, AdgStyle *fallback, GType ancestor_type) { GArray *array = _adg_data_array(); AdgDressPrivate data; data.fallback = fallback; data.ancestor_type = ancestor_type; - g_array_insert_vals(array, dress, g_memdup(&data, sizeof(data)), 1); + g_array_insert_vals(array, dress, cpml_memdup(&data, sizeof(data)), 1); } static void _adg_data_register_builtins(void) { AdgDash *dash; AdgMarker *arrow1, *arrow2; _adg_data_register(ADG_DRESS_UNDEFINED, NULL, G_TYPE_INVALID); /* Predefined colors */ _adg_data_register(ADG_DRESS_COLOR, NULL, ADG_TYPE_COLOR_STYLE); _adg_data_register(ADG_DRESS_COLOR_BACKGROUND, g_object_new(ADG_TYPE_COLOR_STYLE, "blue", 1., "green", 1., "red", 1., NULL), ADG_TYPE_COLOR_STYLE); _adg_data_register(ADG_DRESS_COLOR_STROKE, g_object_new(ADG_TYPE_COLOR_STYLE, NULL), ADG_TYPE_COLOR_STYLE); _adg_data_register(ADG_DRESS_COLOR_DIMENSION, g_object_new(ADG_TYPE_COLOR_STYLE, "red", 0., "green", 0.4, "blue", 0.6, NULL), ADG_TYPE_COLOR_STYLE); _adg_data_register(ADG_DRESS_COLOR_ANNOTATION, g_object_new(ADG_TYPE_COLOR_STYLE, "red", 0.4, "green", 0.4, "blue", 0.2, NULL), ADG_TYPE_COLOR_STYLE); _adg_data_register(ADG_DRESS_COLOR_FILL, g_object_new(ADG_TYPE_COLOR_STYLE, "red", 0.25, "green", 0.25, "blue", 0.25, NULL), ADG_TYPE_COLOR_STYLE); _adg_data_register(ADG_DRESS_COLOR_AXIS, g_object_new(ADG_TYPE_COLOR_STYLE, "red", 0., "green", 0.75, "blue", 0.25, NULL), ADG_TYPE_COLOR_STYLE); _adg_data_register(ADG_DRESS_COLOR_HIDDEN, g_object_new(ADG_TYPE_COLOR_STYLE, "red", 0.5, "green", 0.5, "blue", 0.5, NULL), ADG_TYPE_COLOR_STYLE); /* Predefined lines */ _adg_data_register(ADG_DRESS_LINE, NULL, ADG_TYPE_LINE_STYLE); _adg_data_register(ADG_DRESS_LINE_STROKE, g_object_new(ADG_TYPE_LINE_STYLE, "color-dress", ADG_DRESS_COLOR_STROKE, "width", 1.5, NULL), ADG_TYPE_LINE_STYLE); _adg_data_register(ADG_DRESS_LINE_DIMENSION, g_object_new(ADG_TYPE_LINE_STYLE, "width", 0.5, NULL), ADG_TYPE_LINE_STYLE); _adg_data_register(ADG_DRESS_LINE_FILL, g_object_new(ADG_TYPE_LINE_STYLE, "color-dress", ADG_DRESS_COLOR_FILL, "width", 0.5, NULL), ADG_TYPE_LINE_STYLE); _adg_data_register(ADG_DRESS_LINE_GRID, g_object_new(ADG_TYPE_LINE_STYLE, "antialias", CAIRO_ANTIALIAS_NONE, "width", 1., NULL), ADG_TYPE_LINE_STYLE); _adg_data_register(ADG_DRESS_LINE_FRAME, g_object_new(ADG_TYPE_LINE_STYLE, "color-dress", ADG_DRESS_COLOR_ANNOTATION, "antialias", CAIRO_ANTIALIAS_NONE, "width", 2., NULL), ADG_TYPE_LINE_STYLE); dash = adg_dash_new_with_dashes(4, 2 MM, 2 MM, 10 MM, 2 MM); _adg_data_register(ADG_DRESS_LINE_AXIS, g_object_new(ADG_TYPE_LINE_STYLE, "color-dress", ADG_DRESS_COLOR_AXIS, "width", 0.25 MM, "dash", dash, NULL), ADG_TYPE_LINE_STYLE); adg_dash_destroy(dash); dash = adg_dash_new_with_dashes(2, 6 MM, 3 MM); _adg_data_register(ADG_DRESS_LINE_HIDDEN, g_object_new(ADG_TYPE_LINE_STYLE, "color-dress", ADG_DRESS_COLOR_HIDDEN, "width", 0.25 MM, "dash", dash, NULL), ADG_TYPE_LINE_STYLE); adg_dash_destroy(dash); /* Predefined fonts */ _adg_data_register(ADG_DRESS_FONT, g_object_new(ADG_TYPE_BEST_FONT_STYLE, "family", "Serif", "size", 14., NULL), ADG_TYPE_BEST_FONT_STYLE); _adg_data_register(ADG_DRESS_FONT_TEXT, g_object_new(ADG_TYPE_BEST_FONT_STYLE, "color-dress", ADG_DRESS_COLOR_ANNOTATION, "family", "Sans", "weight", CAIRO_FONT_WEIGHT_BOLD, "size", 12., NULL), ADG_TYPE_BEST_FONT_STYLE); _adg_data_register(ADG_DRESS_FONT_ANNOTATION, g_object_new(ADG_TYPE_BEST_FONT_STYLE, "color-dress", ADG_DRESS_COLOR_ANNOTATION, "family", "Sans", "size", 8., NULL), ADG_TYPE_BEST_FONT_STYLE); _adg_data_register(ADG_DRESS_FONT_QUOTE_TEXT, g_object_new(ADG_TYPE_BEST_FONT_STYLE, "family", "Sans", "weight", CAIRO_FONT_WEIGHT_BOLD, "size", 12., NULL), ADG_TYPE_BEST_FONT_STYLE); _adg_data_register(ADG_DRESS_FONT_QUOTE_ANNOTATION, g_object_new(ADG_TYPE_BEST_FONT_STYLE, "family", "Sans", "size", 8., NULL), ADG_TYPE_BEST_FONT_STYLE); /* Predefined dimension styles */ arrow1 = (AdgMarker *) adg_arrow_new(); arrow2 = (AdgMarker *) adg_arrow_new(); adg_marker_set_pos(arrow2, 1); _adg_data_register(ADG_DRESS_DIMENSION, g_object_new(ADG_TYPE_DIM_STYLE, "marker1", arrow1, "marker2", arrow2, NULL), ADG_TYPE_DIM_STYLE); g_object_unref(arrow1); g_object_unref(arrow2); /* Predefined fill styles */ _adg_data_register(ADG_DRESS_FILL, NULL, ADG_TYPE_FILL_STYLE); _adg_data_register(ADG_DRESS_FILL_HATCH, g_object_new(ADG_TYPE_RULED_FILL, "line-dress", ADG_DRESS_LINE_FILL, NULL), ADG_TYPE_FILL_STYLE); /* Predefined table styles */ _adg_data_register(ADG_DRESS_TABLE, g_object_new(ADG_TYPE_TABLE_STYLE, NULL), ADG_TYPE_TABLE_STYLE); } static AdgDressPrivate * _adg_data_lookup(AdgDress dress) { GArray *array = _adg_data_array(); if (dress >= array->len) return NULL; return ((AdgDressPrivate *) array->data) + dress; } diff --git a/src/adg/adg-matrix.c b/src/adg/adg-matrix.c index 0d42a2e9..de2755d7 100644 --- a/src/adg/adg-matrix.c +++ b/src/adg/adg-matrix.c @@ -1,247 +1,247 @@ /* ADG - Automatic Drawing Generation * Copyright (C) 2007-2020 Nicola Fontana * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:adg-matrix * @Section_Id:Matrix * @title: Matrix * @short_description: cairo matrix enhancements and utilities * * The following functions augment the cairo_matrix_t available methods * providing some useful addition. * * Since: 1.0 **/ #include "adg-internal.h" #include #include /** * adg_matrix_identity: * * A convenient constant providing an identity matrix. * * Returns: (transfer none): a pointer to the identity matrix * * Since: 1.0 **/ const cairo_matrix_t * adg_matrix_identity(void) { static cairo_matrix_t *identity_matrix = NULL; if (G_UNLIKELY(identity_matrix == NULL)) { identity_matrix = g_new(cairo_matrix_t, 1); cairo_matrix_init_identity(identity_matrix); } return identity_matrix; } /** * adg_matrix_null: * * A convenient constant providing an null matrix, that is a matrix * where all components are 0. * * Returns: (transfer none): a pointer to the null matrix * * Since: 1.0 **/ const cairo_matrix_t * adg_matrix_null(void) { static cairo_matrix_t *null_matrix = NULL; if (G_UNLIKELY(null_matrix == NULL)) null_matrix = g_new0(cairo_matrix_t, 1); return null_matrix; } /** * adg_matrix_copy: * @matrix: (out caller-allocates): the destination #cairo_matrix_t * @src: the source #cairo_matrix_t * * Copies @src to @matrix. * * Since: 1.0 **/ void adg_matrix_copy(cairo_matrix_t *matrix, const cairo_matrix_t *src) { g_return_if_fail(matrix != NULL); g_return_if_fail(src != NULL); memcpy(matrix, src, sizeof(cairo_matrix_t)); } /** * adg_matrix_dup: * @matrix: the souce #cairo_matrix_t * * Duplicates @matrix. * * Returns: (transfer full): a duplicate of @matrix that must be freed with g_free() when no longer needed. * * Since: 1.0 **/ cairo_matrix_t * adg_matrix_dup(const cairo_matrix_t *matrix) { g_return_val_if_fail(matrix != NULL, NULL); - return g_memdup(matrix, sizeof(cairo_matrix_t)); + return cpml_memdup(matrix, sizeof(cairo_matrix_t)); } /** * adg_matrix_equal: * @matrix1: the first operand * @matrix2: the second operand * * Compares @matrix1 and @matrix2 and returns TRUE * if the matrices are equal. * * Returns: TRUE if @matrix1 is equal to @matrix2, FALSE otherwise. * * Since: 1.0 **/ gboolean adg_matrix_equal(const cairo_matrix_t *matrix1, const cairo_matrix_t *matrix2) { g_return_val_if_fail(matrix1 != NULL, FALSE); g_return_val_if_fail(matrix2 != NULL, FALSE); return matrix1->xx == matrix2->xx && matrix1->yx == matrix2->yx && matrix1->xy == matrix2->xy && matrix1->yy == matrix2->yy && matrix1->x0 == matrix2->x0 && matrix1->y0 == matrix2->y0; } /** * adg_matrix_normalize: * @matrix: (inout): the source/destination #cairo_matrix_t * * Gets rid of the scaling component of a matrix. * * Returns: TRUE on success, FALSE on errors. * * Since: 1.0 **/ gboolean adg_matrix_normalize(cairo_matrix_t *matrix) { gdouble k; g_return_val_if_fail(matrix != NULL, FALSE); if (matrix->xx != matrix->yy || matrix->xy != -matrix->yx) { g_warning(_("%s: normalization of anamorphic matrices not supported"), G_STRLOC); return FALSE; } if (matrix->xy == 0) { k = matrix->xx; } else if (matrix->xx == 0) { k = matrix->xy; } else { k = sqrt(matrix->xx * matrix->xx + matrix->xy * matrix->xy); } g_return_val_if_fail(k != 0, FALSE); matrix->xx /= k; matrix->xy /= k; matrix->yy /= k; matrix->yx /= k; return TRUE; } /** * adg_matrix_transform: * @matrix: (inout): the source/destination #cairo_matrix_t * @transformation: the transformation to apply * @mode: (in): how @transformation should be applied * * Modifies @matrix applying @transformation in the way specified by * @mode. * * Since: 1.0 **/ void adg_matrix_transform(cairo_matrix_t *matrix, const cairo_matrix_t *transformation, AdgTransformMode mode) { cairo_matrix_t normalized; g_return_if_fail(matrix != NULL); g_return_if_fail(transformation != NULL); switch (mode) { case ADG_TRANSFORM_NONE: break; case ADG_TRANSFORM_BEFORE: cairo_matrix_multiply(matrix, transformation, matrix); break; case ADG_TRANSFORM_AFTER: cairo_matrix_multiply(matrix, matrix, transformation); break; case ADG_TRANSFORM_BEFORE_NORMALIZED: adg_matrix_copy(&normalized, transformation); adg_matrix_normalize(&normalized); cairo_matrix_multiply(matrix, &normalized, matrix); break; case ADG_TRANSFORM_AFTER_NORMALIZED: adg_matrix_copy(&normalized, transformation); adg_matrix_normalize(&normalized); cairo_matrix_multiply(matrix, matrix, &normalized); break; default: g_return_if_reached(); } } /** * adg_matrix_dump: * @matrix: an #cairo_matrix_t * * Dumps the specified @matrix to stdout. Useful for debugging purposes. * * Since: 1.0 **/ void adg_matrix_dump(const cairo_matrix_t *matrix) { g_return_if_fail(matrix != NULL); g_print("[%8.3lf %8.3lf] [%8.3lf]\n" "[%8.3lf %8.3lf] [%8.3lf]\n", matrix->xx, matrix->xy, matrix->x0, matrix->yx, matrix->yy, matrix->y0); } diff --git a/src/adg/adg-point.c b/src/adg/adg-point.c index 102c9b9c..9ec875e8 100644 --- a/src/adg/adg-point.c +++ b/src/adg/adg-point.c @@ -1,454 +1,454 @@ /* ADG - Automatic Drawing Generation * Copyright (C) 2007-2020 Nicola Fontana * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:adg-point * @Section_Id:AdgPoint * @title: AdgPoint * @short_description: A struct holding x, y coordinates * (either named or explicit) * * AdgPoint is an opaque structure that manages 2D coordinates, * either set explicitely through adg_point_set_pair() and * adg_point_set_pair_explicit() or taken from a model with * adg_point_set_pair_from_model(). It can be thought as an * #CpmlPair on steroid, because it adds named pair support to * a simple pair, enabling coordinates depending on #AdgModel. * * Since: 1.0 **/ /** * AdgPoint: * * This is an opaque struct: all its fields are privates. * * Since: 1.0 **/ #include "adg-internal.h" #include "adg-model.h" #include #include "adg-point.h" struct _AdgPoint { CpmlPair pair; AdgModel *model; gchar *name; gboolean up_to_date; }; GType adg_point_get_type(void) { static GType type = 0; if (G_UNLIKELY(type == 0)) type = g_boxed_type_register_static("AdgPoint", (GBoxedCopyFunc) adg_point_dup, (GBoxedFreeFunc) adg_point_destroy); return type; } /** * adg_point_new: * * Creates a new empty #AdgPoint. The returned pointer * should be freed with adg_point_destroy() when no longer needed. * * Returns: a newly created #AdgPoint * * Since: 1.0 **/ AdgPoint * adg_point_new(void) { return g_new0(AdgPoint, 1); } /** * adg_point_dup: * @src: an #AdgPoint * * Duplicates @src. This operation also adds a new reference * to the internal model if @src is linked to a named pair. * * The returned value should be freed with adg_point_destroy() * when no longer needed. * * Returns: the duplicated #AdgPoint struct or NULL on errors. * * Since: 1.0 **/ AdgPoint * adg_point_dup(const AdgPoint *src) { AdgPoint *point; g_return_val_if_fail(src != NULL, NULL); if (src->model) g_object_ref(src->model); - point = g_memdup(src, sizeof(AdgPoint)); + point = cpml_memdup(src, sizeof(AdgPoint)); point->name = g_strdup(src->name); return point; } /** * adg_point_destroy: * @point: an #AdgPoint * * Destroys the @point instance, unreferencing the internal model if * @point is linked to a named pair. * * Since: 1.0 **/ void adg_point_destroy(AdgPoint *point) { g_return_if_fail(point != NULL); adg_point_unset(point); g_free(point); } /** * adg_point_copy: * @point: an #AdgPoint * @src: the source point to copy * * Copies @src into @point. If the old content of @point was linked * to the named pair of a model, the reference to that model is * dropped. Similary, if @src is a named pair, a new reference to * the new model is added. * * Since: 1.0 **/ void adg_point_copy(AdgPoint *point, const AdgPoint *src) { g_return_if_fail(point != NULL); g_return_if_fail(src != NULL); if (src->model != NULL) g_object_ref(src->model); if (point->model != NULL) g_object_unref(point->model); if (point->name != NULL) g_free(point->name); memcpy(point, src, sizeof(AdgPoint)); point->name = g_strdup(src->name); } /** * adg_point_set_pair: * @point: an #AdgPoint * @pair: the #CpmlPair to use * * Sets an explicit pair in @point by using the given @pair. If * @point was linked to a named pair in a model, this link is * dropped before setting the pair. * * Since: 1.0 **/ void adg_point_set_pair(AdgPoint *point, const CpmlPair *pair) { g_return_if_fail(point != NULL); g_return_if_fail(pair != NULL); adg_point_set_pair_explicit(point, pair->x, pair->y); } /** * adg_point_set_pair_explicit: * @point: an #AdgPoint * @x: the x coordinate of the point * @y: the y coordinate of the point * * Works in the same way of adg_point_set_pair() but accept direct numbers * instead of an #CpmlPair structure. * * Since: 1.0 **/ void adg_point_set_pair_explicit(AdgPoint *point, gdouble x, gdouble y) { g_return_if_fail(point != NULL); adg_point_unset(point); point->pair.x = x; point->pair.y = y; point->up_to_date = TRUE; } /** * adg_point_set_pair_from_model: * @point: an #AdgPoint * @model: the #AdgModel * @name: the id of a named pair in @model * * Links the @name named pair of @model to @point, so any subsequent * call to adg_point_get_pair() will return the named pair value. * A new reference is added to @model while the previous model (if any) * is unreferenced. * * Since: 1.0 **/ void adg_point_set_pair_from_model(AdgPoint *point, AdgModel *model, const gchar *name) { g_return_if_fail(point != NULL); g_return_if_fail(ADG_IS_MODEL(model)); g_return_if_fail(name != NULL); /* Return if the new named pair is the same of the old one */ if (model == point->model && strcmp(point->name, name) == 0) return; g_object_ref(model); if (point->model) { /* Remove the old named pair */ g_object_unref(point->model); g_free(point->name); } /* Set the new named pair */ point->up_to_date = FALSE; point->model = model; point->name = g_strdup(name); } /** * adg_point_invalidate: * @point: an #AdgPoint * * Invalidates @point, forcing a refresh of its internal #CpmlPair if * the point is linked to a named pair. If @point is explicitely set, * this function has no effect. * * Since: 1.0 **/ void adg_point_invalidate(AdgPoint *point) { g_return_if_fail(point != NULL); if (point->model != NULL) point->up_to_date = FALSE; } /** * adg_point_unset: * @point: a pointer to an #AdgPoint * * Unsets @point by resetting the internal up_to_date * flag and (eventually) unlinking it from the named pair it is bound * to. After this call the content of @point is undefined, so a * subsequent call to adg_point_get_pair() will * return NULL raising a warning. * * Since: 1.0 **/ void adg_point_unset(AdgPoint *point) { g_return_if_fail(point != NULL); if (point->model) { /* Remove the old named pair */ g_object_unref(point->model); g_free(point->name); } point->up_to_date = FALSE; point->model = NULL; point->name = NULL; } /** * adg_point_update: * @point: a pointer to an #AdgPoint * * Updates the internal #CpmlPair of @point. The internal * implementation is protected against multiple calls so it * can be called more times without harms. * * Returns: TRUE if @point has been updated or FALSE on errors, i.e. when it is bound to a non-existent named pair. * * Since: 1.0 **/ gboolean adg_point_update(AdgPoint *point) { AdgModel *model; const CpmlPair *pair; g_return_val_if_fail(point != NULL, FALSE); if (point->up_to_date) return TRUE; model = point->model; if (model == NULL) { /* A point with explicit coordinates not up to date * is an unexpected condition */ g_warning(_("%s: trying to get a pair from an undefined point"), G_STRLOC); return FALSE; } pair = adg_model_get_named_pair(model, point->name); if (pair == NULL) return FALSE; cpml_pair_copy(&point->pair, pair); point->up_to_date = TRUE; return TRUE; } /** * adg_point_get_pair: * @point: an #AdgPoint * * #AdgPoint is an evolution of the pair concept but internally the * relevant data is still stored in an #CpmlPair struct. This function * returns a copy of the internally owned pair. * * * The #CpmlPair is the first field of an #AdgPoint struct so casting * is allowed between them and, in fact, it is often more convenient * than calling this function. Just remember to update the internal * pair by using adg_point_update() before. * * * Returns: (transfer full): the pair of @point or NULL if the named pair does not exist. * * Since: 1.0 **/ CpmlPair * adg_point_get_pair(AdgPoint *point) { g_return_val_if_fail(point != NULL, NULL); if (! adg_point_update(point)) return NULL; return cpml_pair_dup(& point->pair); } /** * adg_point_get_model: * @point: an #AdgPoint * * Gets the source model of the named pair bound to @point, or * returns NULL if @point is an explicit * pair. The returned value is owned by @point. * * Returns: (transfer none): an #AdgModel or NULL. * * Since: 1.0 **/ AdgModel * adg_point_get_model(const AdgPoint *point) { g_return_val_if_fail(point != NULL, NULL); return point->model; } /** * adg_point_get_name: * @point: an #AdgPoint * * Gets the name of the named pair bound to @point, or * returns NULL if @point is an explicit * pair. The returned value is owned by @point and should not * be modified or freed. * * Returns: the name of the named pair or NULL. * * Since: 1.0 **/ const gchar * adg_point_get_name(const AdgPoint *point) { g_return_val_if_fail(point != NULL, NULL); return point->name; } /** * adg_point_equal: * @point1: the first point to compare * @point2: the second point to compare * * Compares @point1 and @point2 and returns TRUE * if the points are equals. The comparison is made by checking also * the named pairs they are bound to. If you want to compare only * their coordinates, use cpml_pair_equal() directly on the * #AdgPoint structs: * * * if (adg_point_update(point1) && * adg_point_update(point2) && * cpml_pair_equal((CpmlPair *) point1, (CpmlPair *) point2)) * { * ... * } * * * NULL points are handled gracefully. * * Returns: TRUE if @point1 is equal to @point2, FALSE otherwise. * * Since: 1.0 **/ gboolean adg_point_equal(const AdgPoint *point1, const AdgPoint *point2) { if (point1 == point2) return TRUE; if (point1 == NULL || point2 == NULL) return FALSE; /* Check if the points are not bound to the same model * or if only one of the two points is explicit */ if (point1->model != point2->model) return FALSE; /* Handle points bound to named pairs */ if (point1->model != NULL) return g_strcmp0(point1->name, point2->name) == 0; /* Handle points with explicit coordinates */ return cpml_pair_equal(&point1->pair, &point2->pair); } diff --git a/src/adg/adg-table-cell.c b/src/adg/adg-table-cell.c index 372dd7a9..2c746412 100644 --- a/src/adg/adg-table-cell.c +++ b/src/adg/adg-table-cell.c @@ -1,950 +1,950 @@ /* ADG - Automatic Drawing Generation * Copyright (C) 2007-2020 Nicola Fontana * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:adg-table-cell * @Section_Id:AdgTableCell * @title: AdgTableCell * @short_description: A single cell of a table * * The #AdgTableCell is a boxed type, the basic component of an * #AdgTable entity. It must be added to an #AdgTableRow that, * in cascade, will be added to an #AdgTable entity. * * Any cell can be filled with a title and a value: the font to be * used will be picked up from the #AdgTableStyle got by resolving * the #AdgTable:table-dress property. * * The default title is placed at the upper left corner of the cell * while the value is centered up to the bottom edge of the cell. * Anyway the text positioning can be customized by using the * adg_table_cell_set_value_pos() method. * * Some convenient functions to easily create title and value entities * with plain text are provided: adg_table_cell_new_full(), * adg_table_cell_set_text_title() and adg_table_cell_set_text_value(). * When using these methods keep in mind the underlying #AdgToyText * entities will be displaced accordingly to the * #AdgTableStyle:cell-padding value, not used when setting the * entities through other APIs. * * Since: 1.0 **/ /** * AdgTableCell: * * An opaque structure referring to the cell of an #AdgTableRow. * Any row can have an unlimited number of cells. * * Since: 1.0 **/ #include "adg-internal.h" #include "adg-text-internal.h" #include "adg-container.h" #include "adg-alignment.h" #include "adg-textual.h" #include "adg-style.h" #include "adg-table-style.h" #include "adg-table.h" #include "adg-table-row.h" #include "adg-table-cell.h" struct _AdgTableCell { AdgTableRow *row; gdouble width; gboolean has_frame; AdgEntity *title; AdgEntity *value; CpmlPair value_factor; CpmlExtents extents; }; static AdgTableCell * _adg_cell_new (void); static void _adg_cell_invalidate (AdgTableCell *table_cell); static gboolean _adg_cell_set_title (AdgTableCell *table_cell, AdgEntity *title); static gboolean _adg_cell_set_value (AdgTableCell *table_cell, AdgEntity *value); static void _adg_cell_set_value_pos (AdgTableCell *table_cell, const CpmlPair *from_factor, const CpmlPair *to_factor); GType adg_table_cell_get_type(void) { static GType cell_type = 0; if (G_UNLIKELY(cell_type == 0)) cell_type = g_boxed_type_register_static("AdgTableCell", (GBoxedCopyFunc) adg_table_cell_dup, (GBoxedFreeFunc) adg_table_cell_free); return cell_type; } /** * adg_table_cell_dup: * @src: an #AdgTableCell structure * * Duplicates @src. The returned duplicate should be freed * with adg_table_cell_free() when no longer needed. * * Returns: (transfer full): a duplicate of @src. * * Since: 1.0 **/ AdgTableCell * adg_table_cell_dup(const AdgTableCell *src) { g_return_val_if_fail(src != NULL, NULL); - return g_memdup(src, sizeof(AdgTableCell)); + return cpml_memdup(src, sizeof(AdgTableCell)); } /** * adg_table_cell_new: * @table_row: a valid #AdgTableRow * * Creates a new empty cell without a frame and appends it at the * end of the cells yet present in @table_row. You can add content * to the cell by using adg_table_cell_set_title() and * adg_table_cell_set_value() or enable the frame with * adg_table_cell_switch_frame(). * * Returns: (transfer full): the newly created cell or NULL on errors. * * Since: 1.0 **/ AdgTableCell * adg_table_cell_new(AdgTableRow *table_row) { AdgTableCell *table_cell; g_return_val_if_fail(table_row != NULL, NULL); table_cell = _adg_cell_new(); table_cell->row = table_row; adg_table_row_insert(table_row, table_cell, NULL); _adg_cell_invalidate(table_cell); return table_cell; } /** * adg_table_cell_new_before: * @before_cell: a valid #AdgTableCell * * Creates a new cell and inserts it rigthly before the @table_cell cell. * * Returns: (transfer full): the newly created cell or NULL on errors. * * Since: 1.0 **/ AdgTableCell * adg_table_cell_new_before(AdgTableCell *before_cell) { AdgTableRow *table_row; AdgTableCell *table_cell; g_return_val_if_fail(before_cell != NULL, NULL); table_row = before_cell->row; g_return_val_if_fail(table_row != NULL, NULL); table_cell = _adg_cell_new(); table_cell->row = table_row; adg_table_row_insert(table_row, table_cell, NULL); _adg_cell_invalidate(table_cell); return table_cell; } /** * adg_table_cell_new_with_width: * @table_row: a valid #AdgTableRow * @width: the cell width * * A convenient wrapper to adg_table_cell_new() that allows to * specify the @width of @table_row all at once. * * Returns: (transfer full): the newly created cell or NULL on errors. * * Since: 1.0 **/ AdgTableCell * adg_table_cell_new_with_width(AdgTableRow *table_row, gdouble width) { AdgTableCell *table_cell; g_return_val_if_fail(width >= 0, NULL); table_cell = adg_table_cell_new(table_row); if (table_cell != NULL) adg_table_cell_set_width(table_cell, width); return table_cell; } /** * adg_table_cell_new_full: * @table_row: a valid #AdgTableRow * @width: the cell width * @name: (nullable): the name to bound to this cell * @title: (nullable): the title text * @has_frame: whether to draw or not the frame * * A convenient function to add a cell and specifies some common * used properties at once. * * If @name is NULL, the created cell will * not be a named cell. Check adg_table_set_cell() for further * details on what a named cell is supposed to be. * * @title can be NULL, in which case no * title entity will be created. * * Returns: (transfer full): the newly created cell or NULL on errors. * * Since: 1.0 **/ AdgTableCell * adg_table_cell_new_full(AdgTableRow *table_row, gdouble width, const gchar *name, const gchar *title, gboolean has_frame) { AdgTableCell *table_cell; g_return_val_if_fail(width >= 0, NULL); table_cell = adg_table_cell_new(table_row); if (table_cell == NULL) return NULL; adg_table_cell_set_width(table_cell, width); adg_table_cell_switch_frame(table_cell, has_frame); if (title) { adg_table_cell_set_text_title(table_cell, title); } if (name) { AdgTable *table = adg_table_row_get_table(table_row); adg_table_set_cell(table, name, table_cell); } return table_cell; } /** * adg_table_cell_dispose: * @table_cell: a valid #AdgTableCell struct * * Disposes @table_cell. * * Since: 1.0 **/ void adg_table_cell_dispose(AdgTableCell *table_cell) { g_return_if_fail(table_cell != NULL); _adg_cell_set_title(table_cell, NULL); _adg_cell_set_value(table_cell, NULL); } /** * adg_table_cell_free: * @table_cell: an #AdgTableCell structure * * Releases all the memory allocated by @table_cell, itself included. * * Since: 1.0 **/ void adg_table_cell_free(AdgTableCell *table_cell) { AdgTableRow *table_row; g_return_if_fail(table_cell != NULL); table_row = table_cell->row; if (table_cell->row != NULL) { AdgTable *table = adg_table_row_get_table(table_row); adg_table_row_remove(table_row, table_cell); if (table) adg_table_set_cell(table, NULL, table_cell); } adg_table_cell_dispose(table_cell); g_free(table_cell); } /** * adg_table_cell_get_row: * @table_cell: a valid #AdgTableCell * * Gets the row container of @table_cell. The returned #AdgTableRow * is owned by @table_cell and must not be modified or freed. * * Returns: (transfer none): the container row. * * Since: 1.0 **/ AdgTableRow * adg_table_cell_get_row(AdgTableCell *table_cell) { g_return_val_if_fail(table_cell != NULL, NULL); return table_cell->row; } /** * adg_table_cell_get_table: * @table_cell: a valid #AdgTableCell * * A convenient function that gets the table that contains * @table_cell. The returned #AdgTable is owned by @table_cell * and must not be modified or freed. * * Returns: (transfer none): the container table. * * Since: 1.0 **/ AdgTable * adg_table_cell_get_table(AdgTableCell *table_cell) { AdgTableRow *table_row = adg_table_cell_get_row(table_cell); if (table_row == NULL) return NULL; return adg_table_row_get_table(table_row); } /** * adg_table_cell_set_title: * @table_cell: a valid #AdgTableCell * @title: (nullable): the new title entity * * Sets @title as the new title entity of @table_cell. The top left * corner of the bounding box of @title will be cohincident to * the top left corner of the cell extents, taking into accounts * eventual padding spaces specified by the table style. * * The old internal entity is unrefenrenced while the @title (if * not NULL) is refenenced with * g_object_ref_sink(). * * @title can be NULL, in which case the old * entity is removed. * * Since: 1.0 **/ void adg_table_cell_set_title(AdgTableCell *table_cell, AdgEntity *title) { g_return_if_fail(table_cell != NULL); g_return_if_fail(title == NULL || ADG_IS_ENTITY(title)); if (_adg_cell_set_title(table_cell, title)) _adg_cell_invalidate(table_cell); } /** * adg_table_cell_set_text_title: * @table_cell: a valid #AdgTableCell * @title: a text string * * Convenient function to set a the title of a cell using an #AdgToyText * entity with the font dress picked from #AdgTable:table-dress with * a call to adg_table_style_get_title_dress(). * * Since: 1.0 **/ void adg_table_cell_set_text_title(AdgTableCell *table_cell, const gchar *title) { AdgTable *table; AdgEntity *entity; AdgTableStyle *table_style; const CpmlPair *padding; AdgDress table_dress, font_dress; cairo_matrix_t map; g_return_if_fail(table_cell != NULL); if (title == NULL) adg_table_cell_set_title(table_cell, NULL); if (table_cell->title) { gchar *old_title; gboolean unchanged; if (ADG_IS_TEXTUAL(table_cell->title)) old_title = adg_textual_dup_text((AdgTextual *) table_cell->title); else old_title = NULL; unchanged = g_strcmp0(title, old_title) == 0; g_free(old_title); if (unchanged) return; } table = adg_table_cell_get_table(table_cell); table_dress = adg_table_get_table_dress(table); table_style = (AdgTableStyle *) adg_entity_style((AdgEntity *) table, table_dress); padding = adg_table_style_get_cell_padding(table_style); font_dress = adg_table_style_get_title_dress(table_style); entity = g_object_new(ADG_TYPE_BEST_TEXT, "text", title, "font-dress", font_dress, NULL); cairo_matrix_init_translate(&map, padding->x, padding->y); adg_entity_set_global_map(entity, &map); adg_table_cell_set_title(table_cell, entity); } /** * adg_table_cell_title: * @table_cell: a valid #AdgTableCell * * Gets the current title of @table_cell. The returned string is owned * by @table_cell and must not be modified or freed. * * Returns: (transfer none): the title entity or NULL for undefined title. * * Since: 1.0 **/ AdgEntity * adg_table_cell_title(AdgTableCell *table_cell) { g_return_val_if_fail(table_cell != NULL, NULL); return table_cell->title; } /** * adg_table_cell_set_value: * @table_cell: a valid #AdgTableCell * @value: the new value entity * * Sets @value as the new value entity of @table_cell. The bottom middle * point of the bounding box of @value will be cohincident to the * bottom middle point of the cell extents, taking into accounts * eventual padding spaces specified by the table style. * * The old internal entity is unrefenrenced while the @value (if * not NULL) is referenced with * g_object_ref_sink(). * * @value can be NULL, in which case the old entity * is removed. * * Since: 1.0 **/ void adg_table_cell_set_value(AdgTableCell *table_cell, AdgEntity *value) { g_return_if_fail(table_cell != NULL); g_return_if_fail(value == NULL || ADG_IS_ENTITY(value)); if (_adg_cell_set_value(table_cell, value)) _adg_cell_invalidate(table_cell); } /** * adg_table_cell_set_text_value: * @table_cell: a valid #AdgTableCell * @value: (nullable): a text string * * Convenient function to set a the value of a cell using an #AdgToyText * entity with a value font dress picked from #AdgTable:table-dress with * a call to adg_table_style_get_value_dress(). * * @value can be NULL, in which case the old entity * is removed. * * Since: 1.0 **/ void adg_table_cell_set_text_value(AdgTableCell *table_cell, const gchar *value) { AdgTable *table; AdgEntity *entity; AdgTableStyle *table_style; const CpmlPair *padding; AdgDress table_dress, font_dress; cairo_matrix_t map; g_return_if_fail(table_cell != NULL); if (value == NULL) { adg_table_cell_set_value(table_cell, NULL); return; } if (table_cell->value) { gchar *old_value; gboolean unchanged; if (ADG_IS_TEXTUAL(table_cell->value)) old_value = adg_textual_dup_text((AdgTextual *) table_cell->value); else old_value = NULL; unchanged = g_strcmp0(value, old_value) == 0; g_free(old_value); if (unchanged) return; } table = adg_table_cell_get_table(table_cell); table_dress = adg_table_get_table_dress(table); table_style = (AdgTableStyle *) adg_entity_style((AdgEntity *) table, table_dress); padding = adg_table_style_get_cell_padding(table_style); font_dress = adg_table_style_get_value_dress(table_style); entity = g_object_new(ADG_TYPE_BEST_TEXT, "text", value, "font-dress", font_dress, NULL); cairo_matrix_init_translate(&map, 0, -padding->y); adg_entity_set_global_map(entity, &map); adg_table_cell_set_value(table_cell, entity); } /** * adg_table_cell_value: * @table_cell: a valid #AdgTableCell * * Gets the current value of @table_cell. The returned string is owned * by @table_cell and must not be modified or freed. * * Returns: (transfer none): the value entity or NULL for undefined value. * * Since: 1.0 **/ AdgEntity * adg_table_cell_value(AdgTableCell *table_cell) { g_return_val_if_fail(table_cell != NULL, NULL); return table_cell->value; } /** * adg_table_cell_set_value_pos: * @table_cell: a valid #AdgTableCell * @from_factor: the alignment factor on the value entity * @to_factor: the alignment factor on the cell * * Sets a new custom position for the value entity of @table_cell. The * @from_factor specifies the source point (as a fraction of the * value extents) while the @to_factor is the destination point * (specified as a fraction of the cell extents) the source point * must be moved to. * * Since: 1.0 **/ void adg_table_cell_set_value_pos(AdgTableCell *table_cell, const CpmlPair *from_factor, const CpmlPair *to_factor) { g_return_if_fail(table_cell != NULL); _adg_cell_set_value_pos(table_cell, from_factor, to_factor); } /** * adg_table_cell_set_value_pos_explicit: * @table_cell: a valid #AdgTableCell * @from_x: the x alignment factor on the entity * @from_y: the y alignment factor on the entity * @to_x: the x alignment factor on the cell * @to_y: the y alignment factor on the cell * * A convenient wrapper around adg_table_cell_set_value_pos() * that uses explicit factors instead of #CpmlPair. * * Since: 1.0 **/ void adg_table_cell_set_value_pos_explicit(AdgTableCell *table_cell, gdouble from_x, gdouble from_y, gdouble to_x, gdouble to_y) { CpmlPair from, to; from.x = from_x; from.y = from_y; to.x = to_x; to.y = to_y; adg_table_cell_set_value_pos(table_cell, &from, &to); } /** * adg_table_cell_set_width: * @table_cell: a valid #AdgTableCell * @width: the new width * * Sets a new width on @table_cell. The extents on the whole table * will be invalidated, so will be recomputed in the next * arrange() phase. * * A positive @width value specifies the width of this cell in global * space: if the width of its content (that is, either the title or the * value entity) will be greater than @width, it will be rendered * outside the cell boundary box, luckely overwriting the adiacent * cells. * * Using 0 as @width means the width of the cell will be automatically * adjusted to the maximum width of its content. * * Negative width values are not allowed: this condition will raise * a warning without any further processing. * * Since: 1.0 **/ void adg_table_cell_set_width(AdgTableCell *table_cell, gdouble width) { g_return_if_fail(table_cell != NULL); g_return_if_fail(width >= 0); if (table_cell->width != width) { table_cell->width = width; _adg_cell_invalidate(table_cell); } } /** * adg_table_cell_get_width: * @table_cell: a valid #AdgTableCell * * Gets the width of @table_cell. * * Returns: the requested width or 0 on errors. * * Since: 1.0 **/ gdouble adg_table_cell_get_width(AdgTableCell *table_cell) { g_return_val_if_fail(table_cell != NULL, 0.); return table_cell->width; } /** * adg_table_cell_switch_frame: * @table_cell: a valid #AdgTableCell * @has_frame: whether to draw or not the frame * * Sets the frame flag of @table_cell: if @has_frame is * TRUE, a frame around @table_cell * will be rendered using the #AdgTableStyle:frame-dress * dress of the table style. * * Since: 1.0 **/ void adg_table_cell_switch_frame(AdgTableCell *table_cell, gboolean has_frame) { g_return_if_fail(table_cell != NULL); if (table_cell->has_frame != has_frame) { AdgTable *table = adg_table_cell_get_table(table_cell); table_cell->has_frame = has_frame; adg_table_invalidate_grid(table); } } /** * adg_table_cell_has_frame: * @table_cell: a valid #AdgTableCell * * Gets the frame flag of @table_cell. * * Returns: the frame flag. * * Since: 1.0 **/ gboolean adg_table_cell_has_frame(AdgTableCell *table_cell) { g_return_val_if_fail(table_cell != NULL, FALSE); return table_cell->has_frame; } /** * adg_table_cell_get_extents: * @table_cell: a valid #AdgTableCell * * Gets the extents of @table_cell. This function is useful only after the * arrange() phase as in the other situation the extents will likely * be not up to date. * * Returns: the extents of @table_cell or NULL on errors. * * Since: 1.0 **/ const CpmlExtents * adg_table_cell_get_extents(AdgTableCell *table_cell) { g_return_val_if_fail(table_cell != NULL, NULL); return &table_cell->extents; } /** * adg_table_cell_size_request: * @table_cell: a valid #AdgTableCell * @row_extents: the extents of the container #AdgTableRow * * Computes the minimum space needed to properly render @table_cell * and updates the size component of the internal #CpmlExtents struct, * returning it to the caller. The returned #CpmlPair is owned by * @table_cell and should not be modified or freed. * * Returns: (transfer none): the minimum size required. * * Since: 1.0 **/ const CpmlPair * adg_table_cell_size_request(AdgTableCell *table_cell, const CpmlExtents *row_extents) { CpmlVector *size; AdgAlignment *title_alignment; AdgAlignment *value_alignment; AdgTable *table; size = &table_cell->extents.size; if (table_cell->title) { title_alignment = (AdgAlignment *) adg_entity_get_parent(table_cell->title); adg_entity_arrange((AdgEntity *) title_alignment); } else { title_alignment = NULL; } if (table_cell->value) { value_alignment = (AdgAlignment *) adg_entity_get_parent(table_cell->value); adg_entity_arrange((AdgEntity *) value_alignment); } else { value_alignment = NULL; } table = adg_table_cell_get_table(table_cell); size->y = row_extents->size.y; if (table_cell->width == 0) { AdgTableStyle *table_style = (AdgTableStyle *) adg_table_get_table_style(table); const CpmlExtents *extents; /* The width depends on the cell content (default = 0) */ size->x = 0; if (title_alignment) { extents = adg_entity_get_extents((AdgEntity *) title_alignment); size->x = extents->size.x; } if (value_alignment) { extents = adg_entity_get_extents((AdgEntity *) value_alignment); if (extents->size.x > size->x) size->x = extents->size.x; } size->x += adg_table_style_get_cell_spacing(table_style)->x * 2; } else { size->x = table_cell->width; } return size; } /** * adg_table_cell_arrange: * @table_cell: an #AdgTableCell * @layout: the new extents to use * * Rearranges the underlying #AdgTableCell owned by @table_cell using * the new extents provided in @layout. If the x or y size component * of @layout is negative, the value holded by the internal extents * struct is not overriden. * * * table_cell->extents must be up to date if @layout->size.x or * @layout->size.y is negative in order to have a valid size. * * * Returns: the extents of @table_cell or NULL on errors. * * Since: 1.0 **/ const CpmlExtents * adg_table_cell_arrange(AdgTableCell *table_cell, const CpmlExtents *layout) { CpmlExtents *extents; AdgAlignment *alignment; cairo_matrix_t map; /* Set the new extents */ extents = &table_cell->extents; extents->org = layout->org; if (layout->size.x > 0) extents->size.x = layout->size.x; if (layout->size.y > 0) extents->size.y = layout->size.y; extents->is_defined = TRUE; if (table_cell->title) { alignment = (AdgAlignment *) adg_entity_get_parent(table_cell->title); cairo_matrix_init_translate(&map, extents->org.x, extents->org.y); adg_entity_set_global_map((AdgEntity *) alignment, &map); } if (table_cell->value) { CpmlPair to; alignment = (AdgAlignment *) adg_entity_get_parent(table_cell->value); to.x = extents->size.x * table_cell->value_factor.x + extents->org.x; to.y = extents->size.y * table_cell->value_factor.y + extents->org.y; cairo_matrix_init_translate(&map, to.x, to.y); adg_entity_set_global_map((AdgEntity *) alignment, &map); } return extents; } static AdgTableCell * _adg_cell_new(void) { AdgTableCell *table_cell; table_cell = g_new(AdgTableCell, 1); table_cell->row = NULL; table_cell->width = 0.; table_cell->has_frame = FALSE; table_cell->title = NULL; table_cell->value = NULL; table_cell->extents.is_defined = FALSE; table_cell->value_factor.x = 0.5; table_cell->value_factor.y = 1; return table_cell; } static void _adg_cell_invalidate(AdgTableCell *table_cell) { AdgTable *table = adg_table_cell_get_table(table_cell); if (table) adg_entity_invalidate((AdgEntity *) table); } static gboolean _adg_cell_set_title(AdgTableCell *table_cell, AdgEntity *title) { AdgEntity *alignment; if (table_cell->title == title) return FALSE; if (table_cell->title) { alignment = adg_entity_get_parent(table_cell->title); g_object_unref(alignment); } table_cell->title = title; if (title) { AdgEntity *table = (AdgEntity *) adg_table_cell_get_table(table_cell); alignment = (AdgEntity *) adg_alignment_new_explicit(0, -1); g_object_ref_sink(alignment); adg_entity_set_parent(alignment, table); adg_container_add((AdgContainer *) alignment, title); } return TRUE; } static gboolean _adg_cell_set_value(AdgTableCell *table_cell, AdgEntity *value) { AdgEntity *alignment; if (table_cell->value == value) return FALSE; if (table_cell->value) { alignment = adg_entity_get_parent(table_cell->value); adg_container_remove((AdgContainer *) alignment, table_cell->value); g_object_unref(alignment); } table_cell->value = value; if (value) { AdgEntity *table = (AdgEntity *) adg_table_cell_get_table(table_cell); alignment = (AdgEntity *) adg_alignment_new_explicit(0.5, 0); g_object_ref_sink(alignment); adg_entity_set_parent(alignment, table); adg_container_add((AdgContainer *) alignment, value); } return TRUE; } static void _adg_cell_set_value_pos(AdgTableCell *table_cell, const CpmlPair *from_factor, const CpmlPair *to_factor) { AdgAlignment *alignment; if (table_cell->value == NULL) return; alignment = (AdgAlignment *) adg_entity_get_parent(table_cell->value); if (from_factor) adg_alignment_set_factor(alignment, from_factor); if (to_factor) table_cell->value_factor = *to_factor; } diff --git a/src/adg/adg-table-row.c b/src/adg/adg-table-row.c index 17b43374..19c2a1ae 100644 --- a/src/adg/adg-table-row.c +++ b/src/adg/adg-table-row.c @@ -1,461 +1,461 @@ /* ADG - Automatic Drawing Generation * Copyright (C) 2007-2020 Nicola Fontana * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:adg-table-row * @Section_Id:AdgTableRow * @title: AdgTableRow * @short_description: A boxed type representing a table row * * The #AdgTableRow is a boxed type containing a single row of cells * of an #AdgTable object. * * Every row is segmented into different cells. It must be populated * by using the #AdgTableCell APIs, such as adg_table_cell_new() or * adg_table_cell_new_before(). * * Since: 1.0 **/ /** * AdgTableRow: * * An opaque structure referring to a row of an #AdgTable. Any * table can have an unlimited number of rows. * * Since: 1.0 **/ #include "adg-internal.h" #include "adg-style.h" #include "adg-table-style.h" #include "adg-table.h" #include "adg-table-row.h" #include "adg-table-cell.h" struct _AdgTableRow { AdgTable *table; GSList *cells; gdouble height; CpmlExtents extents; }; static AdgTableRow * _adg_row_new (AdgTable *table); GType adg_table_row_get_type(void) { static GType row_type = 0; if (G_UNLIKELY(row_type == 0)) row_type = g_boxed_type_register_static("AdgTableRow", (GBoxedCopyFunc) adg_table_row_dup, (GBoxedFreeFunc) adg_table_row_free); return row_type; } /** * adg_table_row_dup: * @src: an #AdgTableRow structure * * Duplicates @src. The returned duplicate should be freed * with adg_table_row_free() when no longer needed. * * Returns: (transfer full): a duplicate of @src. * * Since: 1.0 **/ AdgTableRow * adg_table_row_dup(const AdgTableRow *src) { g_return_val_if_fail(src != NULL, NULL); - return g_memdup(src, sizeof(AdgTableRow)); + return cpml_memdup(src, sizeof(AdgTableRow)); } /** * adg_table_row_new: * @table: an #AdgTable * * Creates a new empty row and appends it at the end of the rows * already present in @table. By default, the height of this new * row will be the fallback value provided by the table style: * you can override it by using adg_table_row_set_height(). * * Returns: (transfer full): the newly created row or NULL on errors. * * Since: 1.0 **/ AdgTableRow * adg_table_row_new(AdgTable *table) { AdgTableRow *table_row; g_return_val_if_fail(ADG_IS_TABLE(table), NULL); table_row = _adg_row_new(table); adg_table_insert(table, table_row, NULL); adg_entity_invalidate((AdgEntity *) table); return table_row; } /** * adg_table_row_new_before: * @before_row: a valid #AdgTableRow * * Creates a new empty row with default height and inserts it * just before @before_row. * * Returns: (transfer full): the newly created row or NULL on errors. * * Since: 1.0 **/ AdgTableRow * adg_table_row_new_before(AdgTableRow *before_row) { AdgTableRow *table_row; AdgTable *table; g_return_val_if_fail(before_row != NULL, NULL); table = (AdgTable *) before_row->table; g_return_val_if_fail(ADG_IS_TABLE(table), NULL); table_row = _adg_row_new(table); adg_table_insert(table, table_row, before_row); adg_entity_invalidate((AdgEntity *) before_row->table); return table_row; } /** * adg_table_row_free: * @table_row: an #AdgTableRow structure * * Releases all the memory allocated by @table_row, itself included. * * Since: 1.0 **/ void adg_table_row_free(AdgTableRow *table_row) { AdgTable *table; g_return_if_fail(table_row != NULL); g_slist_foreach(table_row->cells, (GFunc) adg_table_cell_free, NULL); g_slist_free(table_row->cells); table = table_row->table; if (table != NULL) adg_table_remove(table, table_row); g_free(table_row); } /** * adg_table_row_insert: * @table_row: a valid #AdgTableRow * @table_cell: the #AdgTableCell to insert * @before_cell: (allow-none): an #AdgTableRow * * Inserts @table_cell inside @table_row. If @before_cell * is specified, @table_cell is inserted before it. * * Since: 1.0 **/ void adg_table_row_insert(AdgTableRow *table_row, AdgTableCell *table_cell, AdgTableCell *before_cell) { g_return_if_fail(table_row != NULL); g_return_if_fail(table_cell != NULL); if (before_cell == NULL) { table_row->cells = g_slist_append(table_row->cells, table_cell); } else { GSList *before = g_slist_find(table_row->cells, before_cell); /* This MUST be present, otherwise something really bad happened */ g_return_if_fail(before != NULL); table_row->cells = g_slist_insert_before(table_row->cells, before, table_cell); } } /** * adg_table_row_remove: * @table_row: a valid #AdgTableRow * @table_cell: the #AdgTableCell to remove * * Removes @table_cell from list of cells of @table_row. * * Since: 1.0 **/ void adg_table_row_remove(AdgTableRow *table_row, AdgTableCell *table_cell) { g_return_if_fail(table_row != NULL); g_return_if_fail(table_cell != NULL); table_row->cells = g_slist_remove(table_row->cells, table_cell); } /** * adg_table_row_foreach: * @table_row: an #AdgTableRow * @callback: (scope call): a callback * @user_data: callback user data * * Invokes @callback on each cell of @table_row. * The callback should be declared as: * * * void callback(AdgTableCell *table_cell, gpointer user_data); * * * Since: 1.0 **/ void adg_table_row_foreach(AdgTableRow *table_row, GCallback callback, gpointer user_data) { g_return_if_fail(table_row != NULL); g_return_if_fail(callback != NULL); g_slist_foreach(table_row->cells, (GFunc) callback, user_data); } /** * adg_table_row_get_table: * @table_row: a valid #AdgTableRow * * Returns the container table of @table_row. The returned table * is owned by @table_row and must not be modified or freed. * * Returns: (transfer none): the requested table or NULL on errors. * * Since: 1.0 **/ AdgTable * adg_table_row_get_table(AdgTableRow *table_row) { g_return_val_if_fail(table_row != NULL, NULL); return table_row->table; } /** * adg_table_row_set_height: * @table_row: a valid #AdgTableRow * @height: the new height * * Sets a new height on @table_row. The extents will be invalidated to * recompute the whole layout of the table. Specifying 0 in * @height will use the default height set in the table style. * * Since: 1.0 **/ void adg_table_row_set_height(AdgTableRow *table_row, gdouble height) { g_return_if_fail(table_row != NULL); table_row->height = height; adg_entity_invalidate((AdgEntity *) table_row->table); } /** * adg_table_row_get_height: * @table_row: a valid #AdgTableRow * * Gets the height of @table_row. * * Returns: the requested height or 0 on errors. * * Since: 1.0 **/ gdouble adg_table_row_get_height(AdgTableRow *table_row) { g_return_val_if_fail(table_row != NULL, 0.); return table_row->height; } /** * adg_table_row_get_extents: * @table_row: a valid #AdgTableRow * * Gets the extents of @table_row. This function is useful only after * the arrange() phase as in the other situation the extents will * likely be not up to date. * * Returns: (transfer none): the extents of @table_row or NULL on errors. * * Since: 1.0 **/ const CpmlExtents * adg_table_row_get_extents(AdgTableRow *table_row) { g_return_val_if_fail(table_row != NULL, NULL); return &table_row->extents; } /** * adg_table_row_size_request: * @table_row: a valid #AdgTableRow * * Computes the minimum space needed to properly render @table_row * and updates the size component of the internal #CpmlExtents struct, * returning it to the caller. The returned #CpmlPair is owned by * @table_row and should not be modified or freed. * * Returns: (transfer none): the minimum size required. * * Since: 1.0 **/ const CpmlPair * adg_table_row_size_request(AdgTableRow *table_row) { AdgTableStyle *table_style; const CpmlPair *spacing; gdouble xpad; CpmlExtents *extents; CpmlVector *size; AdgTableCell *cell; GSList *cell_node; const CpmlPair *cell_size; g_return_val_if_fail(table_row != NULL, NULL); table_style = (AdgTableStyle *) adg_table_get_table_style(table_row->table); spacing = adg_table_style_get_cell_spacing(table_style); xpad = spacing ? spacing->x : 0; extents = &table_row->extents; size = &extents->size; size->x = 0; if (table_row->height == 0) size->y = adg_table_style_get_row_height(table_style); else size->y = table_row->height; /* Compute the row width by summing every cell width */ for (cell_node = table_row->cells; cell_node; cell_node = cell_node->next) { cell = cell_node->data; cell_size = adg_table_cell_size_request(cell, extents); size->x += cell_size->x + xpad; } if (size->x > 0) size->x += xpad; return size; } /** * adg_table_row_arrange: * @table_row: an #AdgTableRow * @layout: the new extents to use * * Rearranges the underlying #AdgTableCell owned by @table_row using * the new extents provided in @layout. If the x or y size component * of @layout is negative, the value holded by the internal extents * struct is not overriden. * * * table_row->extents must be up to date if @layout->size.x or * @layout->size.y is negative in order to have a valid size. * * * Returns: (transfer none): the extents of @table_row or NULL on errors. * * Since: 1.0 **/ const CpmlExtents * adg_table_row_arrange(AdgTableRow *table_row, const CpmlExtents *layout) { CpmlExtents *extents; CpmlExtents cell_layout; const CpmlExtents *cell_extents; AdgTableStyle *table_style; const CpmlPair *spacing; gdouble xpad; AdgTableCell *cell; GSList *cell_node; g_return_val_if_fail(table_row != NULL, NULL); g_return_val_if_fail(layout != NULL, NULL); g_return_val_if_fail(layout->is_defined, NULL); /* Set the new extents */ extents = &table_row->extents; extents->org = layout->org; if (layout->size.x > 0) extents->size.x = layout->size.x; if (layout->size.y > 0) extents->size.y = layout->size.y; extents->is_defined = TRUE; table_style = (AdgTableStyle *) adg_table_get_table_style(table_row->table); spacing = adg_table_style_get_cell_spacing(table_style); xpad = spacing ? spacing->x : 0; /* Propagate the arrange to the table cells */ cell_layout.org.x = extents->org.x + xpad; cell_layout.org.y = extents->org.y; cell_layout.size.x = -1; cell_layout.size.y = extents->size.y; for (cell_node = table_row->cells; cell_node; cell_node = cell_node->next) { cell = cell_node->data; cell_extents = adg_table_cell_arrange(cell, &cell_layout); cell_layout.org.x += cell_extents->size.x + xpad; } return extents; } static AdgTableRow * _adg_row_new(AdgTable *table) { AdgTableRow *table_row = g_new(AdgTableRow, 1); table_row->table = table; table_row->cells = NULL; table_row->height = 0; table_row->extents.is_defined = FALSE; return table_row; } diff --git a/src/cpml/cpml-gobject.c b/src/cpml/cpml-gobject.c index 4dcd9e63..2b4ddfdf 100644 --- a/src/cpml/cpml-gobject.c +++ b/src/cpml/cpml-gobject.c @@ -1,311 +1,310 @@ /* CPML - Cairo Path Manipulation Library * Copyright (C) 2007-2020 Nicola Fontana * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** * SECTION:cpml-gobject * @Section_Id:GObject * @title: GObject wrappers * @short_description: Collection of boxed wrappers for CPML structs * * These wrappers are supposed to make bindings development easier. * This file defines the wrappers and the functions needed for * implementing the CPML primitives as #GBoxed type. * * Since: 1.0 **/ #include "cpml-internal.h" #include #include #include "cpml-extents.h" #include "cpml-segment.h" #include "cpml-primitive.h" #include "cpml-curve.h" #include "cpml-gobject.h" GType cpml_pair_get_type(void) { static GType pair_type = 0; if (G_UNLIKELY(pair_type == 0)) pair_type = g_boxed_type_register_static("CpmlPair", (GBoxedCopyFunc) cpml_pair_dup, g_free); return pair_type; } /** * cpml_pair_dup: * @pair: a #CpmlPair structure * * Duplicates @pair. * * Returns: (transfer full): the duplicate of @pair: must be freed with g_free() when no longer needed. * * Since: 1.0 **/ CpmlPair * cpml_pair_dup(const CpmlPair *pair) { - /* g_memdup() returns NULL if pair is NULL */ - return g_memdup(pair, sizeof(CpmlPair)); + return cpml_memdup(pair, sizeof(CpmlPair)); } GType cpml_primitive_get_type(void) { static GType primitive_type = 0; if (G_UNLIKELY(primitive_type == 0)) primitive_type = g_boxed_type_register_static("CpmlPrimitive", (GBoxedCopyFunc) cpml_primitive_dup, g_free); return primitive_type; } /** * cpml_primitive_dup: * @primitive: a #CpmlPrimitive structure * * Duplicates @primitive. This function makes a shallow duplication of * @primitives, that is the internal pointers of the resulting primitive * struct refer to the same memory as the original @primitive. Check * out cpml_primitive_deep_dup() if it is required also the content * duplication. * * Returns: (transfer full): a shallow duplicate of @primitive: must be * freed with g_free() when no longer needed. * * Since: 1.0 **/ CpmlPrimitive * cpml_primitive_dup(const CpmlPrimitive *primitive) { - return g_memdup(primitive, sizeof(CpmlPrimitive)); + return cpml_memdup(primitive, sizeof(CpmlPrimitive)); } /** * cpml_primitive_deep_dup: * @primitive: a #CpmlPrimitive structure * * Duplicates @primitive. This function makes a deep duplication of * @primitive, that is it duplicates also the definition data (both * org and data). * * Furthermore, the new segment field will * point to a fake duplicated segment with only its first primitive * set (the first primitive of a segment should be a #CPML_MOVE). * This is needed in order to let a #CPML_CLOSE work as expected. * * All the data is allocated in the same chunk of memory so freeing * the returned pointer releases all the occupied memory. * * Returns: (transfer full): a deep duplicate of @primitive: must be * freed with g_free() when no longer needed * * Since: 1.0 **/ CpmlPrimitive * cpml_primitive_deep_dup(const CpmlPrimitive *primitive) { const CpmlPrimitive *src; CpmlPrimitive *dst; gsize primitive_size, org_size, data_size, segment_size; gchar *ptr; g_return_val_if_fail(primitive != NULL, NULL); src = primitive; primitive_size = sizeof(CpmlPrimitive); if (src->org != NULL) org_size = sizeof(cairo_path_data_t); else org_size = 0; if (src->data != NULL) data_size = sizeof(cairo_path_data_t) * src->data->header.length; else data_size = 0; if (src->segment != NULL && src->segment->data != NULL) segment_size = sizeof(CpmlSegment) + sizeof(cairo_path_data_t) * src->segment->data[0].header.length; else segment_size = 0; dst = g_malloc(primitive_size + org_size + data_size + segment_size); ptr = (gchar *) dst + primitive_size; if (org_size > 0) { dst->org = memcpy(ptr, src->org, org_size); ptr += org_size; } else { dst->org = NULL; } if (data_size > 0) { dst->data = memcpy(ptr, src->data, data_size); ptr += data_size; } else { dst->data = NULL; } if (segment_size > 0) { dst->segment = memcpy(ptr, src->segment, sizeof(CpmlSegment)); ptr += sizeof(CpmlSegment); dst->segment->data = memcpy(ptr, src->segment->data, sizeof(cairo_path_data_t) * src->segment->data[0].header.length); } else { dst->segment = NULL; } return dst; } GType cpml_segment_get_type(void) { static GType segment_type = 0; if (G_UNLIKELY(segment_type == 0)) segment_type = g_boxed_type_register_static("CpmlSegment", (GBoxedCopyFunc) cpml_segment_dup, g_free); return segment_type; } /** * cpml_segment_dup: * @segment: a #CpmlSegment structure * * Duplicates @segment. This function makes a shallow duplication, * that is the internal pointers of the resulting segment struct * refer to the same memory as the original @segment. Check out * cpml_segment_deep_dup() if it is required also the content * duplication. * * Returns: (transfer full): a shallow duplicate of @segment: must be freed with g_free() when no longer needed. * * Since: 1.0 **/ CpmlSegment * cpml_segment_dup(const CpmlSegment *segment) { - return g_memdup(segment, sizeof(CpmlSegment)); + return cpml_memdup(segment, sizeof(CpmlSegment)); } /** * cpml_segment_deep_dup: * @segment: a #CpmlSegment structure * * Duplicates @segment. This function makes a deep duplication, * that is it duplicates also the underlying data that defines * the segment. The path field * is set to NULL as * data is no more referring to the * original cairo path. * * All the data is allocated in the same chunk of memory so freeing * the returned pointer releases all the occupied memory. * * Returns: (transfer full): a deep duplicate of @segment: must be freed with g_free() when no longer needed. * * Since: 1.0 **/ CpmlSegment * cpml_segment_deep_dup(const CpmlSegment *segment) { CpmlSegment *dest; int num_data; gsize segment_size, data_size; cairo_path_data_t *p_data; g_return_val_if_fail(segment != NULL, NULL); num_data = segment->num_data; segment_size = sizeof(CpmlSegment); data_size = segment->data ? sizeof(cairo_path_data_t) * num_data : 0; dest = (CpmlSegment *) g_malloc(segment_size + data_size); p_data = (cairo_path_data_t *) ((gchar *) dest + segment_size); dest->path = NULL; if (data_size > 0) { dest->data = memcpy(p_data, segment->data, data_size); dest->num_data = num_data; } else { dest->data = NULL; dest->num_data = 0; } return dest; } GType cpml_curve_offset_algorithm_get_type(void) { static GType etype = 0; if (G_UNLIKELY(etype == 0)) { static const GEnumValue values[] = { { CPML_CURVE_OFFSET_ALGORITHM_NONE, "CPML_CURVE_OFFSET_ALGORITHM_NONE", "none" }, { CPML_CURVE_OFFSET_ALGORITHM_DEFAULT, "CPML_CURVE_OFFSET_ALGORITHM_DEFAULT", "default" }, { CPML_CURVE_OFFSET_ALGORITHM_HANDCRAFT, "CPML_CURVE_OFFSET_ALGORITHM_HANDCRAFT", "handcraft" }, { CPML_CURVE_OFFSET_ALGORITHM_BAIOCA, "CPML_CURVE_OFFSET_ALGORITHM_BAIOCA", "baioca" }, { 0, NULL, NULL } }; etype = g_enum_register_static("CpmlCurveOffsetAlgorithm", values); } return etype; } GType cpml_primitive_type_get_type(void) { static GType etype = 0; if (G_UNLIKELY(etype == 0)) { static const GEnumValue values[] = { { CPML_MOVE, "CPML_MOVE", "move" }, { CPML_LINE, "CPML_LINE", "line" }, { CPML_ARC, "CPML_ARC", "arc" }, { CPML_CURVE, "CPML_CURVE", "curve" }, { CPML_CLOSE, "CPML_CLOSE", "close" }, { 0, NULL, NULL } }; etype = g_enum_register_static("CpmlPrimitiveType", values); } return etype; } diff --git a/src/cpml/cpml-gobject.h b/src/cpml/cpml-gobject.h index 8bb3a1b8..fcbbda8e 100644 --- a/src/cpml/cpml-gobject.h +++ b/src/cpml/cpml-gobject.h @@ -1,57 +1,64 @@ /* CPML - Cairo Path Manipulation Library * Copyright (C) 2007-2020 Nicola Fontana * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #if !defined(__CPML_H__) #error "Only can be included directly." #endif #ifndef __CPML_GOBJECT_H__ #define __CPML_GOBJECT_H__ G_BEGIN_DECLS +/* See https://nvd.nist.gov/vuln/detail/CVE-2021-27219 */ +#if GLIB_CHECK_VERSION(2, 68, 0) +#define cpml_memdup g_memdup2 +#else +#define cpml_memdup g_memdup +#endif + #define CPML_TYPE_PAIR (cpml_pair_get_type()) GType cpml_pair_get_type (void); CpmlPair * cpml_pair_dup (const CpmlPair *pair); #define CPML_TYPE_PRIMITIVE_TYPE (cpml_primitive_type_get_type()) GType cpml_primitive_type_get_type(void); #define CPML_TYPE_PRIMITIVE (cpml_primitive_get_type()) GType cpml_primitive_get_type (void); CpmlPrimitive * cpml_primitive_dup (const CpmlPrimitive *primitive); CpmlPrimitive * cpml_primitive_deep_dup (const CpmlPrimitive *primitive); #define CPML_TYPE_SEGMENT (cpml_segment_get_type()) GType cpml_segment_get_type (void); CpmlSegment * cpml_segment_dup (const CpmlSegment *segment); CpmlSegment * cpml_segment_deep_dup (const CpmlSegment *segment); #define CPML_TYPE_CURVE_OFFSET_ALGORITHM \ (cpml_curve_offset_algorithm_get_type()) GType cpml_curve_offset_algorithm_get_type (void); G_END_DECLS #endif /* __CPML_GOBJECT_H__ */