Code

switch to sigc++ signal for "release"
[inkscape.git] / src / sp-shape.cpp
1 #define __SP_SHAPE_C__
3 /*
4  * Base class for shapes, including <path> element
5  *
6  * Author:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *
9  * Copyright (C) 1999-2002 Lauris Kaplinski
10  * Copyright (C) 2000-2001 Ximian, Inc.
11  * Copyright (C) 2004 John Cliff
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
21 #include <libnr/n-art-bpath.h>
22 #include <libnr/nr-matrix-fns.h>
23 #include <libnr/nr-matrix-ops.h>
24 #include <libnr/nr-matrix-translate-ops.h>
25 #include <libnr/nr-scale-matrix-ops.h>
28 #include "macros.h"
29 #include "display/nr-arena-shape.h"
30 #include "print.h"
31 #include "document.h"
32 #include "marker-status.h"
33 #include "style.h"
34 #include "sp-marker.h"
35 #include "sp-path.h"
36 #include "prefs-utils.h"
38 #define noSHAPE_VERBOSE
40 static void sp_shape_class_init (SPShapeClass *klass);
41 static void sp_shape_init (SPShape *shape);
43 static void sp_shape_build (SPObject * object, SPDocument * document, Inkscape::XML::Node * repr);
44 static void sp_shape_release (SPObject *object);
46 static void sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags);
47 static void sp_shape_modified (SPObject *object, unsigned int flags);
49 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
50 void sp_shape_print (SPItem * item, SPPrintContext * ctx);
51 static NRArenaItem *sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
52 static void sp_shape_hide (SPItem *item, unsigned int key);
53 static void sp_shape_snappoints (SPItem const *item, SnapPointsIter p);
55 static void sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai);
57 static SPItemClass *parent_class;
59 /**
60  * Registers the SPShape class with Gdk and returns its type number.
61  */
62 GType
63 sp_shape_get_type (void)
64 {
65         static GType type = 0;
66         if (!type) {
67                 GTypeInfo info = {
68                         sizeof (SPShapeClass),
69                         NULL, NULL,
70                         (GClassInitFunc) sp_shape_class_init,
71                         NULL, NULL,
72                         sizeof (SPShape),
73                         16,
74                         (GInstanceInitFunc) sp_shape_init,
75                         NULL,   /* value_table */
76                 };
77                 type = g_type_register_static (SP_TYPE_ITEM, "SPShape", &info, (GTypeFlags)0);
78         }
79         return type;
80 }
82 /**
83  * Initializes a SPShapeClass object.  Establishes the function pointers to the class'
84  * member routines in the class vtable, and sets pointers to parent classes.
85  */
86 static void
87 sp_shape_class_init (SPShapeClass *klass)
88 {
89         SPObjectClass *sp_object_class;
90         SPItemClass * item_class;
91         SPPathClass * path_class;
93         sp_object_class = (SPObjectClass *) klass;
94         item_class = (SPItemClass *) klass;
95         path_class = (SPPathClass *) klass;
97         parent_class = (SPItemClass *)g_type_class_peek_parent (klass);
99         sp_object_class->build = sp_shape_build;
100         sp_object_class->release = sp_shape_release;
101         sp_object_class->update = sp_shape_update;
102         sp_object_class->modified = sp_shape_modified;
104         item_class->bbox = sp_shape_bbox;
105         item_class->print = sp_shape_print;
106         item_class->show = sp_shape_show;
107         item_class->hide = sp_shape_hide;
108         item_class->snappoints = sp_shape_snappoints;
111 /**
112  * Initializes an SPShape object.  Nothing particular is needed to initialize
113  * an SPShape object; this is just here as a stub.
114  */
115 static void
116 sp_shape_init (SPShape *shape)
118         /* Nothing here */
121 /**
122  * Virtual build callback for SPMarker.
123  *
124  * This is to be invoked immediately after creation of an SPShape.  This is 
125  * just a stub.
126  *
127  * \see sp_object_build()
128  */
129 static void
130 sp_shape_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
132         if (((SPObjectClass *) (parent_class))->build) {
133           (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
134         }
137 /**
138  * Removes, releases and unrefs all children of object
139  *
140  * This is the inverse of sp_shape_build().  It must be invoked as soon
141  * as the shape is removed from the tree, even if it is still referenced
142  * by other objects.  This routine also disconnects/unrefs markers and
143  * curves attached to it.
144  *
145  * \see sp_object_release()
146  */
147 static void
148 sp_shape_release (SPObject *object)
150         SPItem *item;
151         SPShape *shape;
152         SPItemView *v;
153         int i;
155         item = (SPItem *) object;
156         shape = (SPShape *) object;
158         for (i=SP_MARKER_LOC_START; i<SP_MARKER_LOC_QTY; i++) {
159           if (shape->marker[i]) {
160             sp_signal_disconnect_by_data (shape->marker[i], object);
161             for (v = item->display; v != NULL; v = v->next) {
162               sp_marker_hide ((SPMarker *) shape->marker[i], NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
163             }
164             shape->marker[i] = sp_object_hunref (shape->marker[i], object);
165           }
166         }
167         if (shape->curve) {
168                 shape->curve = sp_curve_unref (shape->curve);
169         }
171         if (((SPObjectClass *) parent_class)->release) {
172           ((SPObjectClass *) parent_class)->release (object);
173         }
176 /** 
177  * Updates the shape when its attributes have changed.  Also establishes
178  * marker objects to match the style settings.  
179  */
180 static void
181 sp_shape_update (SPObject *object, SPCtx *ctx, unsigned int flags)
183     SPItem *item = (SPItem *) object;
184     SPShape *shape = (SPShape *) object;
186         if (((SPObjectClass *) (parent_class))->update) {
187           (* ((SPObjectClass *) (parent_class))->update) (object, ctx, flags);
188         }
190         /* This stanza checks that an object's marker style agrees with
191          * the marker objects it has allocated.  sp_shape_set_marker ensures
192          * that the appropriate marker objects are present (or absent) to
193          * match the style.
194          */
195         /* TODO:  It would be nice if this could be done at an earlier level */
196         for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
197             sp_shape_set_marker (object, i, object->style->marker[i].value);
198           }
200         if (flags & (SP_OBJECT_STYLE_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) {
201                 SPStyle *style;
202                 style = SP_OBJECT_STYLE (object);
203                 if (style->stroke_width.unit == SP_CSS_UNIT_PERCENT) {
204                         SPItemCtx *ictx = (SPItemCtx *) ctx;
205                         double const aw = 1.0 / NR::expansion(ictx->i2vp);
206                         style->stroke_width.computed = style->stroke_width.value * aw;
207                         for (SPItemView *v = ((SPItem *) (shape))->display; v != NULL; v = v->next) {
208                                 nr_arena_shape_set_style ((NRArenaShape *) v->arenaitem, style);
209                         }
210                 }
211         }
213         if (flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG)) {
214                 /* This is suboptimal, because changing parent style schedules recalculation */
215                 /* But on the other hand - how can we know that parent does not tie style and transform */
216                 NR::Rect const paintbox = SP_ITEM(object)->invokeBbox(NR::identity());
217                 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
218                     NRArenaShape * const s = NR_ARENA_SHAPE(v->arenaitem);
219                     if (flags & SP_OBJECT_MODIFIED_FLAG) {
220                         nr_arena_shape_set_path(s, shape->curve, (flags & SP_OBJECT_USER_MODIFIED_FLAG_B));
221                     }
222                     s->setPaintBox(paintbox);
223                 }
224         }
226         if (sp_shape_has_markers (shape)) {
228             /* Dimension marker views */
229             for (SPItemView *v = item->display; v != NULL; v = v->next) {
231                 if (!v->arenaitem->key) {
232                     /* Get enough keys for all, start, mid and end marker types,
233                     ** and set this view's arenaitem key to the first of these keys.
234                     */
235                     NR_ARENA_ITEM_SET_KEY (
236                         v->arenaitem,
237                         sp_item_display_key_new (SP_MARKER_LOC_QTY)
238                         );
239                 }
241                 for (int i = 0 ; i < SP_MARKER_LOC_QTY ; i++) {
242                     if (shape->marker[i]) {
243                         sp_marker_show_dimension ((SPMarker *) shape->marker[i],
244                                                   NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i - SP_MARKER_LOC,
245                                                   sp_shape_number_of_markers (shape, i));
246                     }
247                 }
248             }
250             /* Update marker views */
251             for (SPItemView *v = item->display; v != NULL; v = v->next) {
252                 sp_shape_update_marker_view (shape, v->arenaitem);
253             }
254         }
258 /**
259 * Works out whether a marker of a given type is required at a particular
260 * point on a shape.
262 * \param shape Shape of interest.
263 * \param m Marker type (e.g. SP_MARKER_LOC_START)
264 * \param bp Path segment.
265 * \return 1 if a marker is required here, otherwise 0.
266 */
267 bool
268 sp_shape_marker_required(SPShape const *shape, int const m, NArtBpath *bp)
270     if (shape->marker[m] == NULL) {
271         return false;
272     }
274     if (bp == SP_CURVE_BPATH(shape->curve))
275         return m == SP_MARKER_LOC_START;
276     else if (bp[1].code == NR_END)
277         return m == SP_MARKER_LOC_END;
278     else
279         return m == SP_MARKER_LOC_MID;
282 static bool
283 is_moveto(NRPathcode const c)
285     return c == NR_MOVETO || c == NR_MOVETO_OPEN;
288 /** 
289  * Helper function that advances a subpath's bpath to the first subpath
290  * by checking for moveto segments.
291  *
292  * \pre The bpath[] containing bp begins with a moveto. 
293  */
294 static NArtBpath const *
295 first_seg_in_subpath(NArtBpath const *bp)
297     while (!is_moveto(bp->code)) {
298         --bp;
299     }
300     return bp;
303 /**
304  * Advances the bpath to the last segment in the subpath.
305  */
306 static NArtBpath const *
307 last_seg_in_subpath(NArtBpath const *bp)
309     for(;;) {
310         ++bp;
311         switch (bp->code) {
312             case NR_MOVETO:
313             case NR_MOVETO_OPEN:
314             case NR_END:
315                 --bp;
316                 return bp;
318             default: continue;
319         }
320     }
324 /* A subpath begins with a moveto and ends immediately before the next moveto or NR_END.
325  * (`moveto' here means either NR_MOVETO or NR_MOVETO_OPEN.)  I'm assuming that non-empty
326  * paths always begin with a moveto.
327  *
328  * The control points of the subpath are the control points of the path elements of the subpath.
329  *
330  * As usual, the control points of a moveto or NR_LINETO are {c(3)}, and
331  * the control points of a NR_CURVETO are {c(1), c(2), c(3)}.
332  * (It follows from the definition that NR_END isn't part of a subpath.)
333  *
334  * The initial control point is bpath[bi0].c(3).
335  *
336  * Reference: http://www.w3.org/TR/SVG11/painting.html#MarkerElement, the `orient' attribute.
337  * Reference for behaviour of zero-length segments:
338  * http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
339  */
341 static double const no_tangent = 128.0;  /* arbitrarily-chosen value outside the range of atan2, i.e. outside of [-pi, pi]. */
343 /**
344  * Helper function to calculate the outgoing tangent of a path 
345  * ( atan2(other - p0) )
346  * \pre The bpath[] containing bp0 begins with a moveto. 
347  */
348 static double
349 outgoing_tangent(NArtBpath const *bp0)
351     /* See notes in comment block above. */
353     g_assert(bp0->code != NR_END);
354     NR::Point const &p0 = bp0->c(3);
355     NR::Point other;
356     for (NArtBpath const *bp = bp0;;) {
357         ++bp;
358         switch (bp->code) {
359             case NR_LINETO:
360                 other = bp->c(3);
361                 if (other != p0) {
362                     goto found;
363                 }
364                 break;
366             case NR_CURVETO:
367                 for (unsigned ci = 1; ci <= 3; ++ci) {
368                     other = bp->c(ci);
369                     if (other != p0) {
370                         goto found;
371                     }
372                 }
373                 break;
375             case NR_MOVETO_OPEN:
376             case NR_END:
377             case NR_MOVETO:
378                 bp = first_seg_in_subpath(bp0);
379                 if (bp == bp0) {
380                     /* Gone right around the subpath without finding any different point since the
381                      * initial moveto. */
382                     return no_tangent;
383                 }
384                 if (bp->code != NR_MOVETO) {
385                     /* Open subpath. */
386                     return no_tangent;
387                 }
388                 other = bp->c(3);
389                 if (other != p0) {
390                     goto found;
391                 }
392                 break;
393         }
395         if (bp == bp0) {
396             /* Back where we started, so zero-length subpath. */
397             return no_tangent;
399             /* Note: this test must come after we've looked at element bp, in case bp0 is a curve:
400              * we must look at c(1) and c(2).  (E.g. single-curve subpath.)
401              */
402         }
403     }
405 found:
406     return atan2( other - p0 );
409 /**
410  * Helper function to calculate the incoming tangent of a path
411  * ( atan2(p0 - other) )
412  * 
413  * \pre The bpath[] containing bp0 begins with a moveto. 
414  */
415 static double
416 incoming_tangent(NArtBpath const *bp0)
418     /* See notes in comment block before outgoing_tangent. */
420     g_assert(bp0->code != NR_END);
421     NR::Point const &p0 = bp0->c(3);
422     NR::Point other;
423     for (NArtBpath const *bp = bp0;;) {
424         switch (bp->code) {
425             case NR_LINETO:
426                 other = bp->c(3);
427                 if (other != p0) {
428                     goto found;
429                 }
430                 --bp;
431                 break;
433             case NR_CURVETO:
434                 for (unsigned ci = 3; ci != 0; --ci) {
435                     other = bp->c(ci);
436                     if (other != p0) {
437                         goto found;
438                     }
439                 }
440                 --bp;
441                 break;
443             case NR_MOVETO:
444             case NR_MOVETO_OPEN:
445                 other = bp->c(3);
446                 if (other != p0) {
447                     goto found;
448                 }
449                 if (bp->code != NR_MOVETO) {
450                     /* Open subpath. */
451                     return no_tangent;
452                 }
453                 bp = last_seg_in_subpath(bp0);
454                 break;
456             default: /* includes NR_END */
457                 g_error("Found invalid path code %u in middle of path.", bp->code);
458                 return no_tangent;
459         }
461         if (bp == bp0) {
462             /* Back where we started from: zero-length subpath. */
463             return no_tangent;
464         }
465     }
467 found:
468     return atan2( p0 - other );
472 /**
473  * Calculate the transform required to get a marker's path object in the
474  * right place for particular path segment on a shape.  You should
475  * call sp_shape_marker_required first to see if a marker is required
476  * at this point.
477  *
478  * \see sp_shape_marker_required.
479  *
480  * \param shape Shape which the marker is for.
481  * \param m Marker type (e.g. SP_MARKER_LOC_START)
482  * \param bp Path segment which the arrow is for.
483  * \return Transform matrix.
484  */
485 NR::Matrix
486 sp_shape_marker_get_transform(SPShape const *shape, NArtBpath const *bp)
488     g_return_val_if_fail(( is_moveto(SP_CURVE_BPATH(shape->curve)[0].code)
489                            && ( 0 < shape->curve->end )
490                            && ( SP_CURVE_BPATH(shape->curve)[shape->curve->end].code == NR_END ) ),
491                          NR::Matrix(NR::translate(bp->c(3))));
492     double const angle1 = incoming_tangent(bp);
493     double const angle2 = outgoing_tangent(bp);
495     /* angle1 and angle2 are now each either unset (i.e. still 100 from their initialization) or in
496        [-pi, pi] from atan2. */
497     g_assert((-3.15 < angle1 && angle1 < 3.15) || (angle1 == no_tangent));
498     g_assert((-3.15 < angle2 && angle2 < 3.15) || (angle2 == no_tangent));
500     double ret_angle;
501     if (angle1 == no_tangent) {
502         /* First vertex of an open subpath. */
503         ret_angle = ( angle2 == no_tangent
504                       ? 0.
505                       : angle2 );
506     } else if (angle2 == no_tangent) {
507         /* Last vertex of an open subpath. */
508         ret_angle = angle1;
509     } else {
510         ret_angle = .5 * (angle1 + angle2);
512         if ( fabs( angle2 - angle1 ) > M_PI ) {
513             /* ret_angle is in the middle of the larger of the two sectors between angle1 and
514              * angle2, so flip it by 180degrees to force it to the middle of the smaller sector.
515              *
516              * (Imagine a circle with rays drawn at angle1 and angle2 from the centre of the
517              * circle.  Those two rays divide the circle into two sectors.)
518              */
519             ret_angle += M_PI;
520         }
521     }
523     return NR::Matrix(NR::rotate(ret_angle)) * NR::translate(bp->c(3));
526 /**
527  * Updates the instances (views) of a given marker in a shape.
528  * Marker views have to be scaled already.  The transformation
529  * is retrieved and then shown by calling sp_marker_show_instance.
530  */
531 static void
532 sp_shape_update_marker_view (SPShape *shape, NRArenaItem *ai)
534         SPStyle *style = ((SPObject *) shape)->style;
536         marker_status("sp_shape_update_marker_view:  Updating views of markers");
538         for (int i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
539             if (shape->marker[i] == NULL) {
540                 continue;
541             }
543             int n = 0;
545             for (NArtBpath *bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
546                 if (sp_shape_marker_required (shape, i, bp)) {
547                     NR::Matrix const m(sp_shape_marker_get_transform(shape, bp));
548                     sp_marker_show_instance ((SPMarker* ) shape->marker[i], ai,
549                                              NR_ARENA_ITEM_GET_KEY(ai) + i, n, m,
550                                              style->stroke_width.computed);
551                     n++;
552                 }
553             }
554         }
557 /**
558  * Sets modified flag for all sub-item views.
559  */
560 static void
561 sp_shape_modified (SPObject *object, unsigned int flags)
563         SPShape *shape = SP_SHAPE (object);
565         if (((SPObjectClass *) (parent_class))->modified) {
566           (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
567         }
569         if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
570                 for (SPItemView *v = SP_ITEM (shape)->display; v != NULL; v = v->next) {
571                         nr_arena_shape_set_style (NR_ARENA_SHAPE (v->arenaitem), object->style);
572                 }
573         }
576 /**
577  * Calculates the bounding box for item, storing it into bbox.
578  * This also includes the bounding boxes of any markers included in the shape.
579  */
580 static void sp_shape_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
582     SPShape const *shape = SP_SHAPE (item);
584     if (shape->curve) {
586         NRRect  cbbox;
587         NRBPath bp;
589         bp.path = SP_CURVE_BPATH (shape->curve);
591         cbbox.x0 = cbbox.y0 = NR_HUGE;
592         cbbox.x1 = cbbox.y1 = -NR_HUGE;
594         nr_path_matrix_bbox_union(&bp, transform, &cbbox);
596         SPStyle* style=SP_OBJECT_STYLE (item);
597         if (style->stroke.type != SP_PAINT_TYPE_NONE) {
598             double const scale = expansion(transform);
599             if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
600                 double const width = MAX(0.125, style->stroke_width.computed * scale);
601                 if ( fabs(cbbox.x1-cbbox.x0) > -0.00001 && fabs(cbbox.y1-cbbox.y0) > -0.00001 ) {
602                     cbbox.x0-=0.5*width;
603                     cbbox.x1+=0.5*width;
604                     cbbox.y0-=0.5*width;
605                     cbbox.y1+=0.5*width;
606                 }
607             }
608         }
610         // Union with bboxes of the markers, if any
611         if (sp_shape_has_markers (shape)) {
612             for (NArtBpath* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
613                 for (int m = SP_MARKER_LOC_START; m < SP_MARKER_LOC_QTY; m++) {
614                     if (sp_shape_marker_required (shape, m, bp)) {
616                         SPMarker* marker = SP_MARKER (shape->marker[m]);
617                         SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[m]));
619                         NR::Matrix tr(sp_shape_marker_get_transform(shape, bp));
621                         if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
622                             tr = NR::scale(style->stroke_width.computed) * tr;
623                         }
625                         // total marker transform
626                         tr = marker_item->transform * marker->c2p * tr * transform;
628                         // get bbox of the marker with that transform
629                         NRRect marker_bbox;
630                         sp_item_invoke_bbox (marker_item, &marker_bbox, tr, true);
631                         // union it with the shape bbox
632                         nr_rect_d_union (&cbbox, &cbbox, &marker_bbox);
633                     }
634                 }
635             }
636         }
638         // copy our bbox to the variable we're given
639         *bbox = cbbox;
640     }
643 /**
644  * Prepares shape for printing.  Handles printing of comments for printing
645  * debugging, sizes the item to fit into the document width/height,
646  * applies print fill/stroke, sets transforms for markers, and adds
647  * comment labels.
648  */
649 void
650 sp_shape_print (SPItem *item, SPPrintContext *ctx)
652         NRRect pbox, dbox, bbox;
654         SPShape *shape = SP_SHAPE(item);
656         if (!shape->curve) return;
658         gint add_comments = prefs_get_int_attribute_limited ("printing.debug", "add-label-comments", 0, 0, 1);
659         if (add_comments) {
660             gchar * comment = g_strdup_printf("begin '%s'",
661                                               SP_OBJECT(item)->defaultLabel());
662             sp_print_comment(ctx, comment);
663             g_free(comment);
664         }
666         /* fixme: Think (Lauris) */
667         sp_item_invoke_bbox(item, &pbox, NR::identity(), TRUE);
668         dbox.x0 = 0.0;
669         dbox.y0 = 0.0;
670         dbox.x1 = sp_document_width (SP_OBJECT_DOCUMENT (item));
671         dbox.y1 = sp_document_height (SP_OBJECT_DOCUMENT (item));
672         sp_item_bbox_desktop (item, &bbox);
673         NR::Matrix const i2d = sp_item_i2d_affine(item);
675         SPStyle* style = SP_OBJECT_STYLE (item);
677         if (style->fill.type != SP_PAINT_TYPE_NONE) {
678                 NRBPath bp;
679                 bp.path = SP_CURVE_BPATH(shape->curve);
680                 sp_print_fill (ctx, &bp, i2d, style, &pbox, &dbox, &bbox);
681         }
683         if (style->stroke.type != SP_PAINT_TYPE_NONE) {
684                 NRBPath bp;
685                 bp.path = SP_CURVE_BPATH(shape->curve);
686                 sp_print_stroke (ctx, &bp, i2d, style, &pbox, &dbox, &bbox);
687         }
689         for (NArtBpath* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
690             for (int m = SP_MARKER_LOC_START; m < SP_MARKER_LOC_QTY; m++) {
691                 if (sp_shape_marker_required (shape, m, bp)) {
693                     SPMarker* marker = SP_MARKER (shape->marker[m]);
694                     SPItem* marker_item = sp_item_first_item_child (SP_OBJECT (shape->marker[m]));
696                     NR::Matrix tr(sp_shape_marker_get_transform(shape, bp));
698                     if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
699                         tr = NR::scale(style->stroke_width.computed) * tr;
700                     }
702                     tr = marker_item->transform * marker->c2p * tr;
704                     NR::Matrix old_tr = marker_item->transform;
705                     marker_item->transform = tr;
706                     sp_item_invoke_print (marker_item, ctx);
707                     marker_item->transform = old_tr;
708                 }
709             }
710         }
712         if (add_comments) {
713             gchar * comment = g_strdup_printf("end '%s'",
714                                               SP_OBJECT(item)->defaultLabel());
715             sp_print_comment(ctx, comment);
716             g_free(comment);
717         }
720 /**
721  * Sets style, path, and paintbox.  Updates marker views, including dimensions.
722  */
723 static NRArenaItem *
724 sp_shape_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
726         SPObject *object = SP_OBJECT(item);
727         SPShape *shape = SP_SHAPE(item);
729         NRArenaItem *arenaitem = NRArenaShape::create(arena);
730         NRArenaShape * const s = NR_ARENA_SHAPE(arenaitem);
731         nr_arena_shape_set_style(s, object->style);
732         nr_arena_shape_set_path(s, shape->curve, false);
733         NR::Rect const paintbox = item->invokeBbox(NR::identity());
734         s->setPaintBox(paintbox);
736         if (sp_shape_has_markers (shape)) {
738             /* Dimension the marker views */
739             if (!arenaitem->key) {
740                 NR_ARENA_ITEM_SET_KEY (arenaitem, sp_item_display_key_new (SP_MARKER_LOC_QTY));
741             }
743             for (int i = 0; i < SP_MARKER_LOC_QTY; i++) {
744                 if (shape->marker[i]) {
745                     sp_marker_show_dimension ((SPMarker *) shape->marker[i],
746                                               NR_ARENA_ITEM_GET_KEY (arenaitem) + i - SP_MARKER_LOC,
747                                               sp_shape_number_of_markers (shape, i));
748                 }
749             }
752             /* Update marker views */
753             sp_shape_update_marker_view (shape, arenaitem);
754         }
756         return arenaitem;
759 /**
760  * Hides/removes marker views from the shape.
761  */
762 static void
763 sp_shape_hide (SPItem *item, unsigned int key)
765         SPShape *shape;
766         SPItemView *v;
767         int i;
769         shape = (SPShape *) item;
771         for (i=0; i<SP_MARKER_LOC_QTY; i++) {
772           if (shape->marker[i]) {
773             for (v = item->display; v != NULL; v = v->next) {
774                 if (key == v->key) {
775               sp_marker_hide ((SPMarker *) shape->marker[i],
776                                     NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
777                 }
778             }
779           }
780         }
782         if (((SPItemClass *) parent_class)->hide) {
783           ((SPItemClass *) parent_class)->hide (item, key);
784         }
787 /**
788 * \param shape Shape.
789 * \return TRUE if the shape has any markers, or FALSE if not.
790 */
791 int
792 sp_shape_has_markers (SPShape const *shape)
794     /* Note, we're ignoring 'marker' settings, which technically should apply for
795        all three settings.  This should be fixed later such that if 'marker' is
796        specified, then all three should appear. */
798     return (
799         shape->curve &&
800         (shape->marker[SP_MARKER_LOC_START] ||
801          shape->marker[SP_MARKER_LOC_MID] ||
802          shape->marker[SP_MARKER_LOC_END])
803         );
807 /**
808 * \param shape Shape.
809 * \param type Marker type (e.g. SP_MARKER_LOC_START)
810 * \return Number of markers that the shape has of this type.
811 */
812 int
813 sp_shape_number_of_markers (SPShape *shape, int type)
815     int n = 0;
816     for (NArtBpath* bp = SP_CURVE_BPATH(shape->curve); bp->code != NR_END; bp++) {
817         if (sp_shape_marker_required (shape, type, bp)) {
818             n++;
819         }
820     }
822     return n;
825 /**
826  * Checks if the given marker is used in the shape, and if so, it
827  * releases it by calling sp_marker_hide.  Also detaches signals
828  * and unrefs the marker from the shape.
829  */
830 static void
831 sp_shape_marker_release (SPObject *marker, SPShape *shape)
833         SPItem *item;
834         int i;
836         item = (SPItem *) shape;
838         marker_status("sp_shape_marker_release:  Releasing markers");
839         for (i = SP_MARKER_LOC_START; i < SP_MARKER_LOC_QTY; i++) {
840           if (marker == shape->marker[i]) {
841             SPItemView *v;
842             /* Hide marker */
843             for (v = item->display; v != NULL; v = v->next) {
844               sp_marker_hide ((SPMarker *) (shape->marker[i]), NR_ARENA_ITEM_GET_KEY (v->arenaitem) + i);
845               /* fixme: Do we need explicit remove here? (Lauris) */
846               /* nr_arena_item_set_mask (v->arenaitem, NULL); */
847             }
848             /* Detach marker */
849             sp_signal_disconnect_by_data (shape->marker[i], item);
850             shape->marker[i] = sp_object_hunref (shape->marker[i], item);
851           }
852         }
855 /**
856  * No-op.  Exists for handling 'modified' messages
857  */
858 static void
859 sp_shape_marker_modified (SPObject *marker, guint flags, SPItem *item)
861         /* I think mask does update automagically */
862         /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
865 /**
866  * Adds a new marker to shape object at the location indicated by key.  value 
867  * must be a valid URI reference resolvable from the shape object (i.e., present
868  * in the document <defs>).  If the shape object already has a marker
869  * registered at the given position, it is removed first.  Then the
870  * new marker is hrefed and its signals connected.
871  */
872 void
873 sp_shape_set_marker (SPObject *object, unsigned int key, const gchar *value)
875     SPItem *item = (SPItem *) object;
876     SPShape *shape = (SPShape *) object;
878     if (key < SP_MARKER_LOC_START || key > SP_MARKER_LOC_END) {
879         return;
880     }
882     SPObject *mrk = sp_uri_reference_resolve (SP_OBJECT_DOCUMENT (object), value);
883     if (mrk != shape->marker[key]) {
884         if (shape->marker[key]) {
885             SPItemView *v;
887             /* Detach marker */
888             g_signal_handler_disconnect (shape->marker[key], shape->release_connect[key]);
889             g_signal_handler_disconnect (shape->marker[key], shape->modified_connect[key]);
891             /* Hide marker */
892             for (v = item->display; v != NULL; v = v->next) {
893                 sp_marker_hide ((SPMarker *) (shape->marker[key]),
894                                 NR_ARENA_ITEM_GET_KEY (v->arenaitem) + key);
895                 /* fixme: Do we need explicit remove here? (Lauris) */
896                 /* nr_arena_item_set_mask (v->arenaitem, NULL); */
897             }
899             /* Unref marker */
900             shape->marker[key] = sp_object_hunref (shape->marker[key], object);
901         }
902         if (SP_IS_MARKER (mrk)) {
903             shape->marker[key] = sp_object_href (mrk, object);
904             shape->release_connect[key] = g_signal_connect (G_OBJECT (shape->marker[key]), "release",
905                               G_CALLBACK (sp_shape_marker_release), shape);
906             shape->modified_connect[key] = g_signal_connect (G_OBJECT (shape->marker[key]), "modified",
907                               G_CALLBACK (sp_shape_marker_modified), shape);
908         }
909     }
914 /* Shape section */
916 /**
917  * Calls any registered handlers for the set_shape action
918  */
919 void
920 sp_shape_set_shape (SPShape *shape)
922         g_return_if_fail (shape != NULL);
923         g_return_if_fail (SP_IS_SHAPE (shape));
925         if (SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape) {
926           SP_SHAPE_CLASS (G_OBJECT_GET_CLASS (shape))->set_shape (shape);
927         }
930 /**
931  * Adds a curve to the shape.  If owner is specified, a reference
932  * will be made, otherwise the curve will be copied into the shape.
933  * Any existing curve in the shape will be unreferenced first.
934  * This routine also triggers a request to update the display.
935  */
936 void
937 sp_shape_set_curve (SPShape *shape, SPCurve *curve, unsigned int owner)
939         if (shape->curve) {
940                 shape->curve = sp_curve_unref (shape->curve);
941         }
942         if (curve) {
943                 if (owner) {
944                         shape->curve = sp_curve_ref (curve);
945                 } else {
946                         shape->curve = sp_curve_copy (curve);
947                 }
948         }
949         SP_OBJECT(shape)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
952 /**
953  * Return duplicate of curve (if any exists) or NULL if there is no curve
954  */
955 SPCurve *
956 sp_shape_get_curve (SPShape *shape)
958         if (shape->curve) {
959                 return sp_curve_copy (shape->curve);
960         }
961         return NULL;
964 /* NOT FOR GENERAL PUBLIC UNTIL SORTED OUT (Lauris) */
965 void
966 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
968         if (shape->curve) {
969                 shape->curve = sp_curve_unref (shape->curve);
970         }
971         if (curve) {
972                 if (owner) {
973                         shape->curve = sp_curve_ref (curve);
974                 } else {
975                         shape->curve = sp_curve_copy (curve);
976                 }
977         }
980 /**
981  * Sets the snappoint p to the end point of the path segment
982  */
983 static void sp_shape_snappoints(SPItem const *item, SnapPointsIter p)
985     g_assert(item != NULL);
986     g_assert(SP_IS_SHAPE(item));
988     SPShape const *shape = SP_SHAPE(item);
989     if (shape->curve == NULL) {
990         return;
991     }
993     NR::Matrix const i2d (sp_item_i2d_affine (item));
995     /* Use the end points of each segment of the path */
996     NArtBpath const *bp = SP_CURVE_BPATH(shape->curve);
997     while (bp->code != NR_END) {
998         *p = bp->c(3) * i2d;
999         bp++;
1000     }
1004 /*
1005   Local Variables:
1006   mode:c++
1007   c-file-style:"stroustrup"
1008   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1009   indent-tabs-mode:nil
1010   fill-column:99
1011   End:
1012 */
1013 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :