summaryrefslogtreecommitdiffstats
path: root/src/sp-shape.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sp-shape.cpp')
-rw-r--r--src/sp-shape.cpp919
1 files changed, 919 insertions, 0 deletions
diff --git a/src/sp-shape.cpp b/src/sp-shape.cpp
new file mode 100644
index 000000000..2ae76ef2e
--- /dev/null
+++ b/src/sp-shape.cpp
@@ -0,0 +1,919 @@
+#define __SP_SHAPE_C__
+
+/*
+ * Base class for shapes, including <path> element
+ *
+ * Author:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ *
+ * Copyright (C) 1999-2002 Lauris Kaplinski
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ * Copyright (C) 2004 John Cliff
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+
+#include <libnr/n-art-bpath.h>
+#include <libnr/nr-matrix-fns.h>
+#include <libnr/nr-matrix-ops.h>
+#include <libnr/nr-matrix-translate-ops.h>
+#include <libnr/nr-scale-matrix-ops.h>
+
+
+#include "macros.h"
+#include "display/nr-arena-shape.h"
+#include "print.h"
+#include "document.h"
+#include "marker-status.h"
+#include "style.h"
+#include "sp-marker.h"
+#include "sp-path.h"
+#include "prefs-utils.h"
+
+#define noSHAPE_VERBOSE
+
+static void sp_shape_class_init (SPShapeClass *klass);
+static void sp_shape_init (SPShape *shape);
+
+static void sp_shape_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
+static void sp_shape_release (SPObject *object);
+
+static void sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags);
+static void sp_shape_modified (SPObject *object, unsigned int flags);
+
+static void sp_shape_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
+void sp_shape_print (SPItem * item, SPPrintContext * ctx);
+static NRArenaItem *sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
+static void sp_shape_hide (SPItem *item, unsigned int key);
+static void sp_shape_snappoints (SPItem const *item, SnapPointsIter p);
+
+static void sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai);
+static int sp_shape_has_markers (SPShape const *shape);
+static int sp_shape_number_of_markers (SPShape* Shape, int type);
+
+static SPItemClass *parent_class;
+
+GType
+sp_shape_get_type (void)
+{
+ static GType type = 0;
+ if (!type) {
+ GTypeInfo info = {
+ sizeof (SPShapeClass),
+ NULL, NULL,
+ (GClassInitFunc) sp_shape_class_init,
+ NULL, NULL,
+ sizeof (SPShape),
+ 16,
+ (GInstanceInitFunc) sp_shape_init,
+ NULL, /* value_table */
+ };
+ type = g_type_register_static (SP_TYPE_ITEM, "SPShape", &info, (GTypeFlags)0);
+ }
+ return type;
+}
+
+static void
+sp_shape_class_init (SPShapeClass *klass)
+{
+ SPObjectClass *sp_object_class;
+ SPItemClass * item_class;
+ SPPathClass * path_class;
+
+ sp_object_class = (SPObjectClass *) klass;
+ item_class = (SPItemClass *) klass;
+ path_class = (SPPathClass *) klass;
+
+ parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
+
+ sp_object_class->build = sp_shape_build;
+ sp_object_class->release = sp_shape_release;
+ sp_object_class->update = sp_shape_update;
+ sp_object_class->modified = sp_shape_modified;
+
+ item_class->bbox = sp_shape_bbox;
+ item_class->print = sp_shape_print;
+ item_class->show = sp_shape_show;
+ item_class->hide = sp_shape_hide;
+ item_class->snappoints = sp_shape_snappoints;
+}
+
+static void
+sp_shape_init (SPShape *shape)
+{
+ /* Nothing here */
+}
+
+static void
+sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+{
+ if (((SPObjectClass *) (parent_class))->build) {
+ (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
+ }
+}
+
+static void
+sp_shape_release (SPObject *object)
+{
+ SPItem *item;
+ SPShape *shape;
+ SPItemView *v;
+ int i;
+
+ item = (SPItem *) object;
+ shape = (SPShape *) object;
+
+ for (i=SP_MARKER_LOC_START; i<SP_MARKER_LOC_QTY; i++) {
+ if (shape->marker[i]) {
+ sp_signal_disconnect_by_data (shape->marker[i], object);
+ for (v = item->display; v != NULL; v = v->next) {
+ sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
+ }
+ shape->marker[i] = sp_object_hunref (shape->marker[i], object);
+ }
+ }
+ if (shape->curve) {
+ shape->curve = sp_curve_unref (shape->curve);
+ }
+
+ if (((SPObjectClass *) parent_class)->release) {
+ ((SPObjectClass *) parent_class)->release (object);
+ }
+}
+
+static void
+sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
+{
+ SPItem *item = (SPItem *) object;
+ SPShape *shape = (SPShape *) object;
+
+ if (((SPObjectClass *) (parent_class))->update) {
+ (* ((SPObjectClass *) (parent_class))->update) (object, ctx, flags);
+ }
+
+ /* This stanza checks that an object's marker style agrees with
+ * the marker objects it has allocated. sp_shape_set_marker ensures
+ * that the appropriate marker objects are present (or absent) to
+ * match the style.
+ */
+ /* TODO: It would be nice if this could be done at an earlier level */
+ for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
+ sp_shape_set_marker (object, i, object->style->marker[i].value);
+ }
+
+ if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
+ SPStyle *style;
+ style = SP_OBJECT_STYLE (object);
+ if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
+ SPItemCtx *ictx = (SPItemCtx *) ctx;
+ double const aw = 1.0 / NR::expansion(ictx->i2vp);
+ style->stroke_width.computed = style->stroke_width.value * aw;
+ for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
+ nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
+ }
+ }
+ }
+
+ if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
+ /* This is suboptimal, because changing parent style schedules recalculation */
+ /* But on the other hand - how can we know that parent does not tie style and transform */
+ NR::Rect const paintbox = SP_ITEM(object)->invokeBbox(NR::identity());
+ for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
+ NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
+ if (flags & SP_OBJECT_MODIFIED_FLAG) {
+ nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
+ }
+ s->setPaintBox(paintbox);
+ }
+ }
+
+ if (sp_shape_has_markers (shape)) {
+
+ /* Dimension marker views */
+ for (SPItemView *v = item->display; v != NULL; v = v->next) {
+
+ if (!v->arenaitem->key) {
+ /* Get enough keys for all, start, mid and end marker types,
+ ** and set this view's arenaitem key to the first of these keys.
+ */
+ NR_ARENA_ITEM_SET_KEY (
+ v->arenaitem,
+ sp_item_display_key_new (SP_MARKER_LOC_QTY)
+ );
+ }
+
+ for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
+ if (shape->marker[i]) {
+ sp_marker_show_dimension ((SPMarker *) shape->marker[i],
+ NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i - SP_MARKER_LOC,
+ sp_shape_number_of_markers (shape, i));
+ }
+ }
+ }
+
+ /* Update marker views */
+ for (SPItemView *v = item->display; v != NULL; v = v->next) {
+ sp_shape_update_marker_view (shape, v->arenaitem);
+ }
+ }
+}
+
+
+/**
+* Works out whether a marker of a given type is required at a particular
+* point on a shape.
+*
+* \param shape Shape of interest.
+* \param m Marker type (e.g. SP_MARKER_LOC_START)
+* \param bp Path segment.
+* \return 1 if a marker is required here, otherwise 0.
+*/
+static bool
+sp_shape_marker_required(SPShape const *shape, int const m, NArtBpath *bp)
+{
+ if (shape->marker[m] == NULL) {
+ return false;
+ }
+
+ if (bp == shape->curve->bpath)
+ return m == SP_MARKER_LOC_START;
+ else if (bp[1].code == NR_END)
+ return m == SP_MARKER_LOC_END;
+ else
+ return m == SP_MARKER_LOC_MID;
+}
+
+static bool
+is_moveto(NRPathcode const c)
+{
+ return c == NR_MOVETO || c == NR_MOVETO_OPEN;
+}
+
+/** \pre The bpath[] containing bp begins with a moveto. */
+static NArtBpath const *
+first_seg_in_subpath(NArtBpath const *bp)
+{
+ while (!is_moveto(bp->code)) {
+ --bp;
+ }
+ return bp;
+}
+
+static NArtBpath const *
+last_seg_in_subpath(NArtBpath const *bp)
+{
+ for(;;) {
+ ++bp;
+ switch (bp->code) {
+ case NR_MOVETO:
+ case NR_MOVETO_OPEN:
+ case NR_END:
+ --bp;
+ return bp;
+
+ default: continue;
+ }
+ }
+}
+
+
+/* A subpath begins with a moveto and ends immediately before the next moveto or NR_END.
+ * (`moveto' here means either NR_MOVETO or NR_MOVETO_OPEN.) I'm assuming that non-empty
+ * paths always begin with a moveto.
+ *
+ * The control points of the subpath are the control points of the path elements of the subpath.
+ *
+ * As usual, the control points of a moveto or NR_LINETO are {c(3)}, and
+ * the control points of a NR_CURVETO are {c(1), c(2), c(3)}.
+ * (It follows from the definition that NR_END isn't part of a subpath.)
+ *
+ * The initial control point is bpath[bi0].c(3).
+ *
+ * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
+ * Reference for behaviour of zero-length segments:
+ * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
+ */
+
+static double const no_tangent = 128.0; /* arbitrarily-chosen value outside the range of atan2, i.e. outside of [-pi, pi]. */
+
+/** \pre The bpath[] containing bp0 begins with a moveto. */
+static double
+outgoing_tangent(NArtBpath const *bp0)
+{
+ /* See notes in comment block above. */
+
+ g_assert(bp0->code != NR_END);
+ NR::Point const &p0 = bp0->c(3);
+ NR::Point other;
+ for (NArtBpath const *bp = bp0;;) {
+ ++bp;
+ switch (bp->code) {
+ case NR_LINETO:
+ other = bp->c(3);
+ if (other != p0) {
+ goto found;
+ }
+ break;
+
+ case NR_CURVETO:
+ for (unsigned ci = 1; ci <= 3; ++ci) {
+ other = bp->c(ci);
+ if (other != p0) {
+ goto found;
+ }
+ }
+ break;
+
+ case NR_MOVETO_OPEN:
+ case NR_END:
+ case NR_MOVETO:
+ bp = first_seg_in_subpath(bp0);
+ if (bp == bp0) {
+ /* Gone right around the subpath without finding any different point since the
+ * initial moveto. */
+ return no_tangent;
+ }
+ if (bp->code != NR_MOVETO) {
+ /* Open subpath. */
+ return no_tangent;
+ }
+ other = bp->c(3);
+ if (other != p0) {
+ goto found;
+ }
+ break;
+ }
+
+ if (bp == bp0) {
+ /* Back where we started, so zero-length subpath. */
+ return no_tangent;
+
+ /* Note: this test must come after we've looked at element bp, in case bp0 is a curve:
+ * we must look at c(1) and c(2). (E.g. single-curve subpath.)
+ */
+ }
+ }
+
+found:
+ return atan2( other - p0 );
+}
+
+/** \pre The bpath[] containing bp0 begins with a moveto. */
+static double
+incoming_tangent(NArtBpath const *bp0)
+{
+ /* See notes in comment block before outgoing_tangent. */
+
+ g_assert(bp0->code != NR_END);
+ NR::Point const &p0 = bp0->c(3);
+ NR::Point other;
+ for (NArtBpath const *bp = bp0;;) {
+ switch (bp->code) {
+ case NR_LINETO:
+ other = bp->c(3);
+ if (other != p0) {
+ goto found;
+ }
+ --bp;
+ break;
+
+ case NR_CURVETO:
+ for (unsigned ci = 3; ci != 0; --ci) {
+ other = bp->c(ci);
+ if (other != p0) {
+ goto found;
+ }
+ }
+ --bp;
+ break;
+
+ case NR_MOVETO:
+ case NR_MOVETO_OPEN:
+ other = bp->c(3);
+ if (other != p0) {
+ goto found;
+ }
+ if (bp->code != NR_MOVETO) {
+ /* Open subpath. */
+ return no_tangent;
+ }
+ bp = last_seg_in_subpath(bp0);
+ break;
+
+ default: /* includes NR_END */
+ g_error("Found invalid path code %u in middle of path.", bp->code);
+ return no_tangent;
+ }
+
+ if (bp == bp0) {
+ /* Back where we started from: zero-length subpath. */
+ return no_tangent;
+ }
+ }
+
+found:
+ return atan2( p0 - other );
+}
+
+
+/**
+* Calculate the transform required to get a marker's path object in the
+* right place for particular path segment on a shape. You should
+* call sp_shape_marker_required first to see if a marker is required
+* at this point.
+*
+* \see sp_shape_marker_required.
+*
+* \param shape Shape which the marker is for.
+* \param m Marker type (e.g. SP_MARKER_LOC_START)
+* \param bp Path segment which the arrow is for.
+* \return Transform matrix.
+*/
+
+static NR::Matrix
+sp_shape_marker_get_transform(SPShape const *shape, NArtBpath const *bp)
+{
+ g_return_val_if_fail(( is_moveto(shape->curve->bpath[0].code)
+ && ( 0 < shape->curve->end )
+ && ( shape->curve->bpath[shape->curve->end].code == NR_END ) ),
+ NR::Matrix(NR::translate(bp->c(3))));
+ double const angle1 = incoming_tangent(bp);
+ double const angle2 = outgoing_tangent(bp);
+
+ /* angle1 and angle2 are now each either unset (i.e. still 100 from their initialization) or in
+ [-pi, pi] from atan2. */
+ g_assert((-3.15 < angle1 && angle1 < 3.15) || (angle1 == no_tangent));
+ g_assert((-3.15 < angle2 && angle2 < 3.15) || (angle2 == no_tangent));
+
+ double ret_angle;
+ if (angle1 == no_tangent) {
+ /* First vertex of an open subpath. */
+ ret_angle = ( angle2 == no_tangent
+ ? 0.
+ : angle2 );
+ } else if (angle2 == no_tangent) {
+ /* Last vertex of an open subpath. */
+ ret_angle = angle1;
+ } else {
+ ret_angle = .5 * (angle1 + angle2);
+
+ if ( fabs( angle2 - angle1 ) > M_PI ) {
+ /* ret_angle is in the middle of the larger of the two sectors between angle1 and
+ * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
+ *
+ * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
+ * circle. Those two rays divide the circle into two sectors.)
+ */
+ ret_angle += M_PI;
+ }
+ }
+
+ return NR::Matrix(NR::rotate(ret_angle)) * NR::translate(bp->c(3));
+}
+
+/* Marker views have to be scaled already */
+
+static void
+sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
+{
+ SPStyle *style = ((SPObject *) shape)->style;
+
+ marker_status("sp_shape_update_marker_view: Updating views of markers");
+
+ for (int i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
+ if (shape->marker[i] == NULL) {
+ continue;
+ }
+
+ int n = 0;
+
+ for (NArtBpath *bp = shape->curve->bpath; bp->code != NR_END; bp++) {
+ if (sp_shape_marker_required (shape, i, bp)) {
+ NR::Matrix const m(sp_shape_marker_get_transform(shape, bp));
+ sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
+ NR_ARENA_ITEM_GET_KEY(ai) + i, n, m,
+ style->stroke_width.computed);
+ n++;
+ }
+ }
+ }
+}
+
+static void
+sp_shape_modified (SPObject *object, unsigned int flags)
+{
+ SPShape *shape = SP_SHAPE (object);
+
+ if (((SPObjectClass *) (parent_class))->modified) {
+ (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
+ }
+
+ if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
+ for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
+ nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
+ }
+ }
+}
+
+static void sp_shape_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
+{
+ SPShape const *shape = SP_SHAPE (item);
+
+ if (shape->curve) {
+
+ NRRect cbbox;
+ NRBPath bp;
+
+ bp.path = SP_CURVE_BPATH (shape->curve);
+
+ cbbox.x0 = cbbox.y0 = NR_HUGE;
+ cbbox.x1 = cbbox.y1 = -NR_HUGE;
+
+ nr_path_matrix_bbox_union(&bp, transform, &cbbox);
+
+ SPStyle* style=SP_OBJECT_STYLE (item);
+ if (style->stroke.type != SP_PAINT_TYPE_NONE) {
+ double const scale = expansion(transform);
+ if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
+ double const width = MAX(0.125, style->stroke_width.computed * scale);
+ if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
+ cbbox.x0-=0.5*width;
+ cbbox.x1+=0.5*width;
+ cbbox.y0-=0.5*width;
+ cbbox.y1+=0.5*width;
+ }
+ }
+ }
+
+ // Union with bboxes of the markers, if any
+ if (sp_shape_has_markers (shape)) {
+ for (NArtBpath* bp = shape->curve->bpath; bp->code != NR_END; bp++) {
+ for (int m = SP_MARKER_LOC_START; m < SP_MARKER_LOC_QTY; m++) {
+ if (sp_shape_marker_required (shape, m, bp)) {
+
+ SPMarker* marker = SP_MARKER (shape->marker[m]);
+ SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[m]));
+
+ NR::Matrix tr(sp_shape_marker_get_transform(shape, bp));
+
+ if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
+ tr = NR::scale(style->stroke_width.computed) * tr;
+ }
+
+ // total marker transform
+ tr = marker_item->transform * marker->c2p * tr * transform;
+
+ // get bbox of the marker with that transform
+ NRRect marker_bbox;
+ sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
+ // union it with the shape bbox
+ nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
+ }
+ }
+ }
+ }
+
+ if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
+ NRRect tbbox=*bbox;
+ nr_rect_d_union (bbox, &cbbox, &tbbox);
+ }
+ }
+}
+
+void
+sp_shape_print (SPItem *item, SPPrintContext *ctx)
+{
+ NRRect pbox, dbox, bbox;
+
+ SPShape *shape = SP_SHAPE(item);
+
+ if (!shape->curve) return;
+
+ gint add_comments = prefs_get_int_attribute_limited ("printing.debug", "add-label-comments", 0, 0, 1);
+ if (add_comments) {
+ gchar * comment = g_strdup_printf("begin '%s'",
+ SP_OBJECT(item)->defaultLabel());
+ sp_print_comment(ctx, comment);
+ g_free(comment);
+ }
+
+ /* fixme: Think (Lauris) */
+ sp_item_invoke_bbox(item, &pbox, NR::identity(), TRUE);
+ dbox.x0 = 0.0;
+ dbox.y0 = 0.0;
+ dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
+ dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
+ sp_item_bbox_desktop (item, &bbox);
+ NR::Matrix const i2d = sp_item_i2d_affine(item);
+
+ SPStyle* style = SP_OBJECT_STYLE (item);
+
+ if (style->fill.type != SP_PAINT_TYPE_NONE) {
+ NRBPath bp;
+ bp.path = shape->curve->bpath;
+ sp_print_fill (ctx, &bp, i2d, style, &pbox, &dbox, &bbox);
+ }
+
+ if (style->stroke.type != SP_PAINT_TYPE_NONE) {
+ NRBPath bp;
+ bp.path = shape->curve->bpath;
+ sp_print_stroke (ctx, &bp, i2d, style, &pbox, &dbox, &bbox);
+ }
+
+ for (NArtBpath* bp = shape->curve->bpath; bp->code != NR_END; bp++) {
+ for (int m = SP_MARKER_LOC_START; m < SP_MARKER_LOC_QTY; m++) {
+ if (sp_shape_marker_required (shape, m, bp)) {
+
+ SPMarker* marker = SP_MARKER (shape->marker[m]);
+ SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[m]));
+
+ NR::Matrix tr(sp_shape_marker_get_transform(shape, bp));
+
+ if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
+ tr = NR::scale(style->stroke_width.computed) * tr;
+ }
+
+ tr = marker_item->transform * marker->c2p * tr;
+
+ NR::Matrix old_tr = marker_item->transform;
+ marker_item->transform = tr;
+ sp_item_invoke_print (marker_item, ctx);
+ marker_item->transform = old_tr;
+ }
+ }
+ }
+
+ if (add_comments) {
+ gchar * comment = g_strdup_printf("end '%s'",
+ SP_OBJECT(item)->defaultLabel());
+ sp_print_comment(ctx, comment);
+ g_free(comment);
+ }
+}
+
+static NRArenaItem *
+sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
+{
+ SPObject *object = SP_OBJECT(item);
+ SPShape *shape = SP_SHAPE(item);
+
+ NRArenaItem *arenaitem = NRArenaShape::create(arena);
+ NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
+ nr_arena_shape_set_style(s, object->style);
+ nr_arena_shape_set_path(s, shape->curve, false);
+ NR::Rect const paintbox = item->invokeBbox(NR::identity());
+ s->setPaintBox(paintbox);
+
+ if (sp_shape_has_markers (shape)) {
+
+ /* Dimension marker views */
+ if (!arenaitem->key) {
+ NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
+ }
+
+ for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
+ if (shape->marker[i]) {
+ sp_marker_show_dimension ((SPMarker *) shape->marker[i],
+ NR_ARENA_ITEM_GET_KEY (arenaitem) + i - SP_MARKER_LOC,
+ sp_shape_number_of_markers (shape, i));
+ }
+ }
+
+
+ /* Update marker views */
+ sp_shape_update_marker_view (shape, arenaitem);
+ }
+
+ return arenaitem;
+}
+
+static void
+sp_shape_hide (SPItem *item, unsigned int key)
+{
+ SPShape *shape;
+ SPItemView *v;
+ int i;
+
+ shape = (SPShape *) item;
+
+ for (i=0; i<SP_MARKER_LOC_QTY; i++) {
+ if (shape->marker[i]) {
+ for (v = item->display; v != NULL; v = v->next) {
+ if (key == v->key) {
+ sp_marker_hide ((SPMarker *) shape->marker[i],
+ NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
+ }
+ }
+ }
+ }
+
+ if (((SPItemClass *) parent_class)->hide) {
+ ((SPItemClass *) parent_class)->hide (item, key);
+ }
+}
+
+/* Marker stuff */
+
+/**
+* \param shape Shape.
+* \return TRUE if the shape has any markers, or FALSE if not.
+*/
+static int
+sp_shape_has_markers (SPShape const *shape)
+{
+ /* Note, we're ignoring 'marker' settings, which technically should apply for
+ all three settings. This should be fixed later such that if 'marker' is
+ specified, then all three should appear. */
+
+ return (
+ shape->curve &&
+ (shape->marker[SP_MARKER_LOC_START] ||
+ shape->marker[SP_MARKER_LOC_MID] ||
+ shape->marker[SP_MARKER_LOC_END])
+ );
+}
+
+
+/**
+* \param shape Shape.
+* \param type Marker type (e.g. SP_MARKER_LOC_START)
+* \return Number of markers that the shape has of this type.
+*/
+static int
+sp_shape_number_of_markers (SPShape *shape, int type)
+{
+ int n = 0;
+ for (NArtBpath* bp = shape->curve->bpath; bp->code != NR_END; bp++) {
+ if (sp_shape_marker_required (shape, type, bp)) {
+ n++;
+ }
+ }
+
+ return n;
+}
+
+static void
+sp_shape_marker_release (SPObject *marker, SPShape *shape)
+{
+ SPItem *item;
+ int i;
+
+ item = (SPItem *) shape;
+
+ marker_status("sp_shape_marker_release: Releasing markers");
+ for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
+ if (marker == shape->marker[i]) {
+ SPItemView *v;
+ /* Hide marker */
+ for (v = item->display; v != NULL; v = v->next) {
+ sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
+ /* fixme: Do we need explicit remove here? (Lauris) */
+ /* nr_arena_item_set_mask (v->arenaitem, NULL); */
+ }
+ /* Detach marker */
+ sp_signal_disconnect_by_data (shape->marker[i], item);
+ shape->marker[i] = sp_object_hunref (shape->marker[i], item);
+ }
+ }
+}
+
+static void
+sp_shape_marker_modified (SPObject *marker, guint flags, SPItem *item)
+{
+ /* I think mask does update automagically */
+ /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
+}
+
+void
+sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
+{
+ SPItem *item = (SPItem *) object;
+ SPShape *shape = (SPShape *) object;
+
+ if (key < SP_MARKER_LOC_START || key > SP_MARKER_LOC_END) {
+ return;
+ }
+
+ SPObject *mrk = sp_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
+ if (mrk != shape->marker[key]) {
+ if (shape->marker[key]) {
+ SPItemView *v;
+
+ /* Detach marker */
+ g_signal_handler_disconnect (shape->marker[key], shape->release_connect[key]);
+ g_signal_handler_disconnect (shape->marker[key], shape->modified_connect[key]);
+
+ /* Hide marker */
+ for (v = item->display; v != NULL; v = v->next) {
+ sp_marker_hide ((SPMarker *) (shape->marker[key]),
+ NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
+ /* fixme: Do we need explicit remove here? (Lauris) */
+ /* nr_arena_item_set_mask (v->arenaitem, NULL); */
+ }
+
+ /* Unref marker */
+ shape->marker[key] = sp_object_hunref (shape->marker[key], object);
+ }
+ if (SP_IS_MARKER (mrk)) {
+ shape->marker[key] = sp_object_href (mrk, object);
+ shape->release_connect[key] = g_signal_connect (G_OBJECT (shape->marker[key]), "release",
+ G_CALLBACK (sp_shape_marker_release), shape);
+ shape->modified_connect[key] = g_signal_connect (G_OBJECT (shape->marker[key]), "modified",
+ G_CALLBACK (sp_shape_marker_modified), shape);
+ }
+ }
+}
+
+
+
+/* Shape section */
+
+void
+sp_shape_set_shape (SPShape *shape)
+{
+ g_return_if_fail (shape != NULL);
+ g_return_if_fail (SP_IS_SHAPE (shape));
+
+ if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
+ SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
+ }
+}
+
+void
+sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
+{
+ if (shape->curve) {
+ shape->curve = sp_curve_unref (shape->curve);
+ }
+ if (curve) {
+ if (owner) {
+ shape->curve = sp_curve_ref (curve);
+ } else {
+ shape->curve = sp_curve_copy (curve);
+ }
+ }
+ SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
+/* Return duplicate of curve or NULL */
+SPCurve *
+sp_shape_get_curve (SPShape *shape)
+{
+ if (shape->curve) {
+ return sp_curve_copy (shape->curve);
+ }
+ return NULL;
+}
+
+/* NOT FOR GENERAL PUBLIC UNTIL SORTED OUT (Lauris) */
+void
+sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
+{
+ if (shape->curve) {
+ shape->curve = sp_curve_unref (shape->curve);
+ }
+ if (curve) {
+ if (owner) {
+ shape->curve = sp_curve_ref (curve);
+ } else {
+ shape->curve = sp_curve_copy (curve);
+ }
+ }
+}
+
+static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p)
+{
+ g_assert(item != NULL);
+ g_assert(SP_IS_SHAPE(item));
+
+ SPShape const *shape = SP_SHAPE(item);
+ if (shape->curve == NULL) {
+ return;
+ }
+
+ NR::Matrix const i2d (sp_item_i2d_affine (item));
+
+ /* Use the end points of each segment of the path */
+ NArtBpath const *bp = shape->curve->bpath;
+ while (bp->code != NR_END) {
+ *p = bp->c(3) * i2d;
+ bp++;
+ }
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :