/* * GooCanvas. Copyright (C) 2005 Damon Chaplin. * Released under the GNU LGPL license. See COPYING for details. * * goocanvasitemsimple.c - abstract base class for simple items with styles. */ /** * SECTION:goocanvasitemsimple * @Title: GooCanvasItemSimple * @Short_Description: the base class for the standard canvas items. * * #GooCanvasItemSimple is used as a base class for the standard canvas items. * * It supports a number of style properties, such as "stroke-color", * "fill-color" and "line-width". * * It also provides a number of utility functions that subclasses can use. */ #include #include #include #include "goocanvasprivate.h" #include "goocanvasitemsimple.h" #include "goocanvasutils.h" #include "goocanvasmarshal.h" enum { PROP_0, /* Basic drawing properties. */ PROP_STROKE_PATTERN, PROP_FILL_PATTERN, PROP_FILL_RULE, PROP_OPERATOR, PROP_ANTIALIAS, /* Line style & width properties. */ PROP_LINE_WIDTH, PROP_LINE_CAP, PROP_LINE_JOIN, PROP_LINE_JOIN_MITER_LIMIT, PROP_LINE_DASH, /* Convenience properties. */ PROP_STROKE_COLOR, PROP_STROKE_COLOR_RGBA, PROP_STROKE_PIXBUF, PROP_FILL_COLOR, PROP_FILL_COLOR_RGBA, PROP_FILL_PIXBUF, /* Other properties. Note that the order here is important PROP_TRANSFORM must be the first non-style property. see set_property(). */ PROP_TRANSFORM, PROP_VISIBILITY, PROP_VISIBILITY_THRESHOLD, PROP_POINTER_EVENTS, PROP_TITLE, PROP_DESCRIPTION }; static void goo_canvas_item_simple_finalize (GObject *object); static void item_interface_init (GooCanvasItemIface *iface); static void goo_canvas_item_simple_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec); static void goo_canvas_item_simple_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec); G_DEFINE_TYPE_WITH_CODE (GooCanvasItemSimple, goo_canvas_item_simple, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GOO_TYPE_CANVAS_ITEM, item_interface_init)) /* * GooCanvasStyle. */ /** * goo_canvas_style_new: * * Creates a new #GooCanvasStyle, using the default settings. * * Returns: a new #GooCanvasStyle. **/ GooCanvasStyle* goo_canvas_style_new (void) { GooCanvasStyle *style; style = g_new0 (GooCanvasStyle, 1); style->ref_count = 1; style->mask = 0; style->line_width = 2.0; style->line_join_miter_limit = 10.0; style->fill_rule = CAIRO_FILL_RULE_WINDING; style->op = CAIRO_OPERATOR_OVER; style->antialias = CAIRO_ANTIALIAS_DEFAULT; style->line_cap = CAIRO_LINE_CAP_BUTT; style->line_join = CAIRO_LINE_JOIN_MITER; return style; } /** * goo_canvas_style_copy: * @orig_style: the style to copy. * * Copies a #GooCanvasStyle. * * Returns: a copy of the #GooCanvasStyle. **/ GooCanvasStyle* goo_canvas_style_copy (GooCanvasStyle *orig_style) { GooCanvasStyle *style; style = g_new0 (GooCanvasStyle, 1); style->ref_count = 1; style->mask = orig_style->mask; style->stroke_pattern = cairo_pattern_reference (orig_style->stroke_pattern); style->fill_pattern = cairo_pattern_reference (orig_style->fill_pattern); style->line_width = orig_style->line_width; style->line_join_miter_limit = orig_style->line_join_miter_limit; style->dash = goo_canvas_line_dash_ref (orig_style->dash); style->fill_rule = orig_style->fill_rule; style->op = orig_style->op; style->antialias = orig_style->antialias; style->line_cap = orig_style->line_cap; style->line_join = orig_style->line_join; return style; } /** * goo_canvas_style_ref: * @style: a #GooCanvasStyle. * * Increments the reference count of a #GooCanvasStyle. * * Returns: the #GooCanvasStyle. **/ GooCanvasStyle* goo_canvas_style_ref (GooCanvasStyle *style) { if (style) style->ref_count++; return style; } /** * goo_canvas_style_unref: * @style: a #GooCanvasStyle. * * Decrements the reference count of the #GooCanvasStyle. * If the reference count falls to 0, the #GooCanvasStyle is freed. **/ void goo_canvas_style_unref (GooCanvasStyle *style) { if (style && --style->ref_count == 0) { /* Free all the resources used by the style. */ cairo_pattern_destroy (style->stroke_pattern); cairo_pattern_destroy (style->fill_pattern); goo_canvas_line_dash_unref (style->dash); g_free (style); } } GType goo_canvas_style_get_type (void) { static GType our_type = 0; if (our_type == 0) our_type = g_boxed_type_register_static ("GooCanvasStyle", (GBoxedCopyFunc) goo_canvas_style_copy, (GBoxedFreeFunc) goo_canvas_style_unref); return our_type; } /* * GooCanvasItemSimple. */ static void goo_canvas_item_simple_class_init (GooCanvasItemSimpleClass *klass) { GObjectClass *gobject_class = (GObjectClass*) klass; gobject_class->finalize = goo_canvas_item_simple_finalize; gobject_class->get_property = goo_canvas_item_simple_get_property; gobject_class->set_property = goo_canvas_item_simple_set_property; /* Basic drawing properties. */ g_object_class_install_property (gobject_class, PROP_STROKE_PATTERN, g_param_spec_boxed ("stroke-pattern", _("Stroke Pattern"), _("The pattern to use to paint the perimeter of the item"), GOO_TYPE_CAIRO_PATTERN, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FILL_PATTERN, g_param_spec_boxed ("fill-pattern", _("Fill Pattern"), _("The pattern to use to paint the interior of the item"), GOO_TYPE_CAIRO_PATTERN, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_FILL_RULE, g_param_spec_enum ("fill-rule", _("Fill Rule"), _("The fill rule used to determine which parts of the item are filled"), GOO_TYPE_CAIRO_FILL_RULE, CAIRO_FILL_RULE_WINDING, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_OPERATOR, g_param_spec_enum ("operator", _("Operator"), _("The compositing operator to use"), GOO_TYPE_CAIRO_OPERATOR, CAIRO_OPERATOR_OVER, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_ANTIALIAS, g_param_spec_enum ("antialias", _("Antialias"), _("The antialiasing mode to use"), GOO_TYPE_CAIRO_ANTIALIAS, CAIRO_ANTIALIAS_DEFAULT, G_PARAM_READWRITE)); /* Line style & width properties. */ g_object_class_install_property (gobject_class, PROP_LINE_WIDTH, g_param_spec_double ("line-width", _("Line Width"), _("The line width to use for the item's perimeter"), 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_LINE_CAP, g_param_spec_enum ("line-cap", _("Line Cap"), _("The line cap style to use"), GOO_TYPE_CAIRO_LINE_CAP, CAIRO_LINE_CAP_BUTT, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_LINE_JOIN, g_param_spec_enum ("line-join", _("Line Join"), _("The line join style to use"), GOO_TYPE_CAIRO_LINE_JOIN, CAIRO_LINE_JOIN_MITER, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_LINE_JOIN_MITER_LIMIT, g_param_spec_double ("line-join-miter-limit", _("Miter Limit"), _("The smallest angle to use with miter joins, in degrees. Bevel joins will be used below this limit"), 0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_LINE_DASH, g_param_spec_boxed ("line-dash", _("Line Dash"), _("The dash pattern to use"), GOO_TYPE_CANVAS_LINE_DASH, G_PARAM_READWRITE)); /* Convenience properties - writable only. */ g_object_class_install_property (gobject_class, PROP_STROKE_COLOR, g_param_spec_string ("stroke-color", _("Stroke Color"), _("The color to use for the item's perimeter"), NULL, G_PARAM_WRITABLE)); g_object_class_install_property (gobject_class, PROP_STROKE_COLOR_RGBA, g_param_spec_uint ("stroke-color-rgba", _("Stroke Color RGBA"), _("The color to use for the item's perimeter, specified as a 32-bit integer value"), 0, G_MAXUINT, 0, G_PARAM_WRITABLE)); g_object_class_install_property (gobject_class, PROP_STROKE_PIXBUF, g_param_spec_object ("stroke-pixbuf", _("Stroke Pixbuf"), _("The pixbuf to use to draw the item's perimeter"), GDK_TYPE_PIXBUF, G_PARAM_WRITABLE)); g_object_class_install_property (gobject_class, PROP_FILL_COLOR, g_param_spec_string ("fill-color", _("Fill Color"), _("The color to use to paint the interior of the item"), NULL, G_PARAM_WRITABLE)); g_object_class_install_property (gobject_class, PROP_FILL_COLOR_RGBA, g_param_spec_uint ("fill-color-rgba", _("Fill Color RGBA"), _("The color to use to paint the interior of the item, specified as a 32-bit integer value"), 0, G_MAXUINT, 0, G_PARAM_WRITABLE)); g_object_class_install_property (gobject_class, PROP_FILL_PIXBUF, g_param_spec_object ("fill-pixbuf", _("Fill Pixbuf"), _("The pixbuf to use to paint the interior of the item"), GDK_TYPE_PIXBUF, G_PARAM_WRITABLE)); /* Other properties. */ g_object_class_override_property (gobject_class, PROP_VISIBILITY, "visibility"); g_object_class_override_property (gobject_class, PROP_VISIBILITY_THRESHOLD, "visibility-threshold"); g_object_class_override_property (gobject_class, PROP_TRANSFORM, "transform"); g_object_class_override_property (gobject_class, PROP_POINTER_EVENTS, "pointer-events"); g_object_class_override_property (gobject_class, PROP_TITLE, "title"); g_object_class_override_property (gobject_class, PROP_DESCRIPTION, "description"); } static void goo_canvas_item_simple_init (GooCanvasItemSimple *item) { item->pointer_events = GOO_CANVAS_EVENTS_VISIBLE_PAINTED; } static void goo_canvas_item_simple_finalize (GObject *object) { GooCanvasItemSimple *item = (GooCanvasItemSimple*) object; if (item->style) goo_canvas_style_unref (item->style); if (item->transform) g_free (item->transform); G_OBJECT_CLASS (goo_canvas_item_simple_parent_class)->finalize (object); } static void goo_canvas_item_simple_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GooCanvasItemSimple *item = (GooCanvasItemSimple*) object; GooCanvasStyle *style = item->style; GooCanvasStyleValuesMask mask = style ? style->mask : 0; switch (prop_id) { /* Basic drawing properties. */ case PROP_STROKE_PATTERN: g_value_set_boxed (value, mask & GOO_CANVAS_STYLE_STROKE_PATTERN ? style->stroke_pattern : NULL); break; case PROP_FILL_PATTERN: g_value_set_boxed (value, mask & GOO_CANVAS_STYLE_FILL_PATTERN ? style->fill_pattern : NULL); break; case PROP_FILL_RULE: g_value_set_enum (value, mask & GOO_CANVAS_STYLE_FILL_RULE ? style->fill_rule : CAIRO_FILL_RULE_WINDING); break; case PROP_OPERATOR: g_value_set_enum (value, mask & GOO_CANVAS_STYLE_OPERATOR ? style->op : CAIRO_OPERATOR_OVER); break; case PROP_ANTIALIAS: g_value_set_enum (value, mask & GOO_CANVAS_STYLE_ANTIALIAS ? style->antialias : CAIRO_ANTIALIAS_DEFAULT); break; /* Line style & width properties. */ case PROP_LINE_WIDTH: g_value_set_double (value, mask & GOO_CANVAS_STYLE_LINE_WIDTH ? style->line_width : 2.0); break; case PROP_LINE_CAP: g_value_set_enum (value, mask & GOO_CANVAS_STYLE_LINE_CAP ? style->line_cap : CAIRO_LINE_CAP_BUTT); break; case PROP_LINE_JOIN: g_value_set_enum (value, mask & GOO_CANVAS_STYLE_LINE_JOIN ? style->line_join : CAIRO_LINE_JOIN_MITER); break; case PROP_LINE_JOIN_MITER_LIMIT: g_value_set_double (value, mask & PROP_LINE_JOIN_MITER_LIMIT ? style->line_join_miter_limit : 10.0); break; case PROP_LINE_DASH: g_value_set_boxed (value, mask & PROP_LINE_DASH ? style->dash : NULL); break; /* Other properties. */ case PROP_TRANSFORM: g_value_set_boxed (value, item->transform); break; case PROP_VISIBILITY: g_value_set_enum (value, item->visibility); break; case PROP_VISIBILITY_THRESHOLD: g_value_set_double (value, item->visibility_threshold); break; case PROP_POINTER_EVENTS: g_value_set_flags (value, item->pointer_events); break; case PROP_TITLE: g_value_set_string (value, item->title); break; case PROP_DESCRIPTION: g_value_set_string (value, item->description); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void goo_canvas_item_simple_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GooCanvasItemSimple *item = (GooCanvasItemSimple*) object; GooCanvasStyle *style; GdkColor color = { 0, 0, 0, 0, }; guint rgba, red, green, blue, alpha; GdkPixbuf *pixbuf; cairo_surface_t *surface; gboolean recompute_bounds = FALSE; cairo_matrix_t *transform; /* See if we need to create our own style or copy a shared style. */ if (prop_id < PROP_TRANSFORM) { if (!item->style) { item->style = goo_canvas_style_new (); } else if (item->style->ref_count > 1) { goo_canvas_style_unref (item->style); item->style = goo_canvas_style_copy (item->style); } } style = item->style; switch (prop_id) { /* Basic drawing properties. */ case PROP_STROKE_PATTERN: cairo_pattern_destroy (style->stroke_pattern); style->stroke_pattern = g_value_get_boxed (value); cairo_pattern_reference (style->stroke_pattern); style->mask |= GOO_CANVAS_STYLE_STROKE_PATTERN; break; case PROP_FILL_PATTERN: cairo_pattern_destroy (style->fill_pattern); style->fill_pattern = g_value_get_boxed (value); cairo_pattern_reference (style->fill_pattern); style->mask |= GOO_CANVAS_STYLE_FILL_PATTERN; break; case PROP_FILL_RULE: style->fill_rule = g_value_get_enum (value); style->mask |= GOO_CANVAS_STYLE_FILL_RULE; break; case PROP_OPERATOR: style->op = g_value_get_enum (value); style->mask |= GOO_CANVAS_STYLE_OPERATOR; break; case PROP_ANTIALIAS: style->antialias = g_value_get_enum (value); style->mask |= GOO_CANVAS_STYLE_ANTIALIAS; break; /* Line style & width properties. */ case PROP_LINE_WIDTH: style->line_width = g_value_get_double (value); style->mask |= GOO_CANVAS_STYLE_LINE_WIDTH; recompute_bounds = TRUE; break; case PROP_LINE_CAP: style->line_cap = g_value_get_enum (value); style->mask |= GOO_CANVAS_STYLE_LINE_CAP; recompute_bounds = TRUE; break; case PROP_LINE_JOIN: style->line_join = g_value_get_enum (value); style->mask |= GOO_CANVAS_STYLE_LINE_JOIN; recompute_bounds = TRUE; break; case PROP_LINE_JOIN_MITER_LIMIT: style->line_join_miter_limit = g_value_get_double (value); style->mask |= GOO_CANVAS_STYLE_LINE_JOIN_MITER_LIMIT; recompute_bounds = TRUE; break; case PROP_LINE_DASH: goo_canvas_line_dash_unref (style->dash); style->dash = g_value_get_boxed (value); goo_canvas_line_dash_ref (style->dash); style->mask |= GOO_CANVAS_STYLE_LINE_DASH; recompute_bounds = TRUE; break; /* Convenience properties. */ case PROP_STROKE_COLOR: if (g_value_get_string (value)) gdk_color_parse (g_value_get_string (value), &color); cairo_pattern_destroy (style->stroke_pattern); style->stroke_pattern = cairo_pattern_create_rgb (color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0); style->mask |= GOO_CANVAS_STYLE_STROKE_PATTERN; break; case PROP_STROKE_COLOR_RGBA: rgba = g_value_get_uint (value); red = (rgba >> 24) & 0xFF; green = (rgba >> 16) & 0xFF; blue = (rgba >> 8) & 0xFF; alpha = (rgba) & 0xFF; cairo_pattern_destroy (style->stroke_pattern); style->stroke_pattern = cairo_pattern_create_rgba (red / 255.0, green / 255.0, blue / 255.0, alpha / 255.0); style->mask |= GOO_CANVAS_STYLE_STROKE_PATTERN; break; case PROP_STROKE_PIXBUF: cairo_pattern_destroy (style->stroke_pattern); pixbuf = g_value_get_object (value); surface = goo_canvas_cairo_surface_from_pixbuf (pixbuf); style->stroke_pattern = cairo_pattern_create_for_surface (surface); cairo_surface_destroy (surface); cairo_pattern_set_extend (style->stroke_pattern, CAIRO_EXTEND_REPEAT); style->mask |= GOO_CANVAS_STYLE_STROKE_PATTERN; break; case PROP_FILL_COLOR: if (g_value_get_string (value)) gdk_color_parse (g_value_get_string (value), &color); cairo_pattern_destroy (style->fill_pattern); style->fill_pattern = cairo_pattern_create_rgb (color.red / 65535.0, color.green / 65535.0, color.blue / 65535.0); style->mask |= GOO_CANVAS_STYLE_FILL_PATTERN; break; case PROP_FILL_COLOR_RGBA: rgba = g_value_get_uint (value); red = (rgba >> 24) & 0xFF; green = (rgba >> 16) & 0xFF; blue = (rgba >> 8) & 0xFF; alpha = (rgba) & 0xFF; cairo_pattern_destroy (style->fill_pattern); style->fill_pattern = cairo_pattern_create_rgba (red / 255.0, green / 255.0, blue / 255.0, alpha / 255.0); style->mask |= GOO_CANVAS_STYLE_FILL_PATTERN; break; case PROP_FILL_PIXBUF: cairo_pattern_destroy (style->fill_pattern); pixbuf = g_value_get_object (value); surface = goo_canvas_cairo_surface_from_pixbuf (pixbuf); style->fill_pattern = cairo_pattern_create_for_surface (surface); cairo_surface_destroy (surface); cairo_pattern_set_extend (style->fill_pattern, CAIRO_EXTEND_REPEAT); style->mask |= GOO_CANVAS_STYLE_FILL_PATTERN; break; /* Other properties. */ case PROP_TRANSFORM: if (item->transform) g_free (item->transform); transform = g_value_get_boxed (value); item->transform = goo_cairo_matrix_copy (transform); recompute_bounds = TRUE; break; case PROP_VISIBILITY: item->visibility = g_value_get_enum (value); break; case PROP_VISIBILITY_THRESHOLD: item->visibility_threshold = g_value_get_double (value); break; case PROP_POINTER_EVENTS: item->pointer_events = g_value_get_flags (value); break; case PROP_TITLE: g_free (item->title); item->title = g_value_dup_string (value); break; case PROP_DESCRIPTION: g_free (item->description); item->description = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } g_signal_emit_by_name (item, "changed", recompute_bounds); } /** * goo_canvas_item_simple_set_style: * @item: a #GooCanvasItemSimple. * @style: a #GooCanvasStyle. * * Sets the style of the item. **/ void goo_canvas_item_simple_set_style (GooCanvasItemSimple *item, GooCanvasStyle *style) { if (item->style == style) return; if (item->style) goo_canvas_style_unref (item->style); item->style = goo_canvas_style_ref (style); g_signal_emit_by_name (item, "changed", TRUE); } static GooCanvasItem* goo_canvas_item_simple_get_parent (GooCanvasItem *item) { return GOO_CANVAS_ITEM_SIMPLE (item)->parent; } static void goo_canvas_item_simple_set_parent (GooCanvasItem *item, GooCanvasItem *parent) { GOO_CANVAS_ITEM_SIMPLE (item)->parent = parent; } static cairo_matrix_t* goo_canvas_item_simple_get_transform (GooCanvasItem *item) { return GOO_CANVAS_ITEM_SIMPLE (item)->transform; } static void goo_canvas_item_simple_set_transform (GooCanvasItem *item, cairo_matrix_t *transform) { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) item; if (transform) { if (!simple->transform) simple->transform = g_new (cairo_matrix_t, 1); *simple->transform = *transform; } else { g_free (simple->transform); simple->transform = NULL; } g_signal_emit_by_name (item, "changed", TRUE); } static void item_interface_init (GooCanvasItemIface *iface) { iface->get_parent = goo_canvas_item_simple_get_parent; iface->set_parent = goo_canvas_item_simple_set_parent; iface->get_transform = goo_canvas_item_simple_get_transform; iface->set_transform = goo_canvas_item_simple_set_transform; } /** * goo_canvas_item_simple_set_fill_options: * @item: a #GooCanvasItemSimple. * @cr: a cairo context. * * Sets the fill options of the cairo context, according to the item's style * settings. **/ void goo_canvas_item_simple_set_fill_options (GooCanvasItemSimple *item, cairo_t *cr) { GooCanvasStyle *style = item->style; GooCanvasStyleValuesMask mask; if (!style) return; mask = style->mask; if (mask & GOO_CANVAS_STYLE_OPERATOR) cairo_set_operator (cr, style->op); if (mask & GOO_CANVAS_STYLE_ANTIALIAS) cairo_set_antialias (cr, style->antialias); if (mask & GOO_CANVAS_STYLE_FILL_RULE) cairo_set_fill_rule (cr, style->fill_rule); if ((mask & GOO_CANVAS_STYLE_FILL_PATTERN) && style->fill_pattern) cairo_set_source (cr, style->fill_pattern); } /** * goo_canvas_item_simple_set_stroke_options: * @item: a #GooCanvasItemSimple. * @cr: a cairo context. * * Sets the stroke options of the cairo context, according to the item's * style settings. **/ void goo_canvas_item_simple_set_stroke_options (GooCanvasItemSimple *item, cairo_t *cr) { GooCanvasStyle *style = item->style; GooCanvasStyleValuesMask mask; if (!style) return; mask = style->mask; if (mask & GOO_CANVAS_STYLE_OPERATOR) cairo_set_operator (cr, style->op); if (mask & GOO_CANVAS_STYLE_ANTIALIAS) cairo_set_antialias (cr, style->antialias); /* Note that if a stroke pattern hasn't been set in the style we reset the source to black, just in case a fill pattern was used. */ if ((mask & GOO_CANVAS_STYLE_STROKE_PATTERN) && style->stroke_pattern) cairo_set_source (cr, style->stroke_pattern); else cairo_set_source_rgb (cr, 0, 0, 0); if (mask & GOO_CANVAS_STYLE_LINE_WIDTH) cairo_set_line_width (cr, style->line_width); if (mask & GOO_CANVAS_STYLE_LINE_CAP) cairo_set_line_cap (cr, style->line_cap); if (mask & GOO_CANVAS_STYLE_LINE_JOIN) cairo_set_line_join (cr, style->line_join); if (mask & GOO_CANVAS_STYLE_LINE_JOIN_MITER_LIMIT) cairo_set_miter_limit (cr, style->line_join_miter_limit); if ((mask & GOO_CANVAS_STYLE_LINE_DASH) && style->dash) cairo_set_dash (cr, style->dash->dashes, style->dash->num_dashes, style->dash->dash_offset); } /** * goo_canvas_item_simple_paint_path: * @item: a #GooCanvasItemSimple. * @cr: a cairo context. * * Paints the current path, using the item's style settings. **/ void goo_canvas_item_simple_paint_path (GooCanvasItemSimple *item, cairo_t *cr) { GooCanvasStyle *style = item->style; GooCanvasStyleValuesMask mask = 0; gboolean do_stroke = TRUE, do_fill = FALSE; if (style) mask = style->mask; /* Determine whether we are going to stroke and/or fill the path. */ if (style && (mask & GOO_CANVAS_STYLE_STROKE_PATTERN) && style->stroke_pattern == NULL) do_stroke = FALSE; if ((mask & GOO_CANVAS_STYLE_FILL_PATTERN) && style->fill_pattern) do_fill = TRUE; /* Fill the path, if required. */ if (do_fill) { goo_canvas_item_simple_set_fill_options (item, cr); if (do_stroke) cairo_fill_preserve (cr); else cairo_fill (cr); } /* Draw the stroke, if required. */ if (do_stroke) { goo_canvas_item_simple_set_stroke_options (item, cr); cairo_stroke (cr); } } /* Returns the bounds of the path, using the item's stroke and fill options, in device coords. Note that the bounds include both the stroke and the fill extents, even if they will not be painted. (We need this to handle the "pointer-events" property.) */ /** * goo_canvas_item_simple_get_path_bounds: * @item: a #GooCanvasItemSimple. * @cr: a cairo context. * @bounds: the #GooCanvasBounds struct to store the resulting bounding box. * * Calculates the bounds of the current path in device space, storing the * results in the given #GooCanvasBounds struct. **/ void goo_canvas_item_simple_get_path_bounds (GooCanvasItemSimple *item, cairo_t *cr, GooCanvasBounds *bounds) { GooCanvasBounds tmp_bounds, tmp_bounds2; /* Calculate the filled extents. */ goo_canvas_item_simple_set_fill_options (item, cr); cairo_fill_extents (cr, &tmp_bounds.x1, &tmp_bounds.y1, &tmp_bounds.x2, &tmp_bounds.y2); /* Check the stroke. */ goo_canvas_item_simple_set_stroke_options (item, cr); cairo_stroke_extents (cr, &tmp_bounds2.x1, &tmp_bounds2.y1, &tmp_bounds2.x2, &tmp_bounds2.y2); bounds->x1 = MIN (tmp_bounds.x1, tmp_bounds.x2); bounds->x1 = MIN (bounds->x1, tmp_bounds2.x1); bounds->x1 = MIN (bounds->x1, tmp_bounds2.x2); bounds->x2 = MAX (tmp_bounds.x1, tmp_bounds.x2); bounds->x2 = MAX (bounds->x2, tmp_bounds2.x1); bounds->x2 = MAX (bounds->x2, tmp_bounds2.x2); bounds->y1 = MIN (tmp_bounds.y1, tmp_bounds.y2); bounds->y1 = MIN (bounds->y1, tmp_bounds2.y1); bounds->y1 = MIN (bounds->y1, tmp_bounds2.y2); bounds->y2 = MAX (tmp_bounds.y1, tmp_bounds.y2); bounds->y2 = MAX (bounds->y2, tmp_bounds2.y1); bounds->y2 = MAX (bounds->y2, tmp_bounds2.y2); goo_canvas_item_simple_user_bounds_to_device (item, cr, bounds); } /** * goo_canvas_item_simple_user_bounds_to_device: * @item: a #GooCanvasItemSimple. * @cr: a cairo context. * @bounds: the bounds of the item, in the item's coordinate space. * * Converts the item's bounds to a bounding box in device space. **/ void goo_canvas_item_simple_user_bounds_to_device (GooCanvasItemSimple *item, cairo_t *cr, GooCanvasBounds *bounds) { GooCanvasBounds tmp_bounds = *bounds, tmp_bounds2 = *bounds; /* Convert the top-left and bottom-right corners to device coords. */ cairo_user_to_device (cr, &tmp_bounds.x1, &tmp_bounds.y1); cairo_user_to_device (cr, &tmp_bounds.x2, &tmp_bounds.y2); /* Now convert the top-right and bottom-left corners. */ cairo_user_to_device (cr, &tmp_bounds2.x1, &tmp_bounds2.y2); cairo_user_to_device (cr, &tmp_bounds2.x2, &tmp_bounds2.y1); /* Calculate the minimum x coordinate seen and put in x1. */ bounds->x1 = MIN (tmp_bounds.x1, tmp_bounds.x2); bounds->x1 = MIN (bounds->x1, tmp_bounds2.x1); bounds->x1 = MIN (bounds->x1, tmp_bounds2.x2); /* Calculate the maximum x coordinate seen and put in x2. */ bounds->x2 = MAX (tmp_bounds.x1, tmp_bounds.x2); bounds->x2 = MAX (bounds->x2, tmp_bounds2.x1); bounds->x2 = MAX (bounds->x2, tmp_bounds2.x2); /* Calculate the minimum y coordinate seen and put in y1. */ bounds->y1 = MIN (tmp_bounds.y1, tmp_bounds.y2); bounds->y1 = MIN (bounds->y1, tmp_bounds2.y1); bounds->y1 = MIN (bounds->y1, tmp_bounds2.y2); /* Calculate the maximum y coordinate seen and put in y2. */ bounds->y2 = MAX (tmp_bounds.y1, tmp_bounds.y2); bounds->y2 = MAX (bounds->y2, tmp_bounds2.y1); bounds->y2 = MAX (bounds->y2, tmp_bounds2.y2); } /** * goo_canvas_item_simple_check_in_path: * @item: a #GooCanvasItemSimple. * @x: the x coordinate of the point. * @y: the y coordinate of the point. * @cr: a cairo context. * @pointer_events: specifies which parts of the path to check. * * Checks if the given point is in the current path. * * Returns: %TRUE if the given point is in the current path. **/ gboolean goo_canvas_item_simple_check_in_path (GooCanvasItemSimple *item, gdouble x, gdouble y, cairo_t *cr, GooCanvasPointerEvents pointer_events) { GooCanvasStyle *style = item->style; GooCanvasStyleValuesMask mask = 0; gboolean do_stroke = TRUE, do_fill = FALSE; if (style) mask = style->mask; /* Determine whether we are going to stroke and/or fill the path. */ if (style && (mask & GOO_CANVAS_STYLE_STROKE_PATTERN) && style->stroke_pattern == NULL) do_stroke = FALSE; if ((mask & GOO_CANVAS_STYLE_FILL_PATTERN) && style->fill_pattern) do_fill = TRUE; /* Check the filled path, if required. */ if (pointer_events & GOO_CANVAS_EVENTS_FILL_MASK && (!(pointer_events & GOO_CANVAS_EVENTS_PAINTED_MASK) || do_fill)) { goo_canvas_item_simple_set_fill_options (item, cr); if (cairo_in_fill (cr, x, y)) return TRUE; } /* Check the stroke, if required. */ if (pointer_events & GOO_CANVAS_EVENTS_STROKE_MASK && (!(pointer_events & GOO_CANVAS_EVENTS_PAINTED_MASK) || do_stroke)) { goo_canvas_item_simple_set_stroke_options (item, cr); if (cairo_in_stroke (cr, x, y)) return TRUE; } return FALSE; }