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;
109 }
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)
117 {
118 /* Nothing here */
119 }
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)
131 {
132 if (((SPObjectClass *) (parent_class))->build) {
133 (*((SPObjectClass *) (parent_class))->build) (object, document, repr);
134 }
135 }
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)
149 {
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 }
174 }
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)
182 {
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 }
255 }
258 /**
259 * Works out whether a marker of a given type is required at a particular
260 * point on a shape.
261 *
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)
269 {
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;
280 }
282 static bool
283 is_moveto(NRPathcode const c)
284 {
285 return c == NR_MOVETO || c == NR_MOVETO_OPEN;
286 }
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)
296 {
297 while (!is_moveto(bp->code)) {
298 --bp;
299 }
300 return bp;
301 }
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)
308 {
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 }
321 }
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)
350 {
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 );
407 }
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)
417 {
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 );
469 }
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)
487 {
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));
524 }
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)
533 {
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 }
555 }
557 /**
558 * Sets modified flag for all sub-item views.
559 */
560 static void
561 sp_shape_modified (SPObject *object, unsigned int flags)
562 {
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 }
574 }
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)
581 {
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 }
641 }
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)
651 {
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 }
718 }
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)
725 {
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;
757 }
759 /**
760 * Hides/removes marker views from the shape.
761 */
762 static void
763 sp_shape_hide (SPItem *item, unsigned int key)
764 {
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 }
785 }
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)
793 {
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 );
804 }
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)
814 {
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;
823 }
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)
832 {
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 }
853 }
855 /**
856 * No-op. Exists for handling 'modified' messages
857 */
858 static void
859 sp_shape_marker_modified (SPObject *marker, guint flags, SPItem *item)
860 {
861 /* I think mask does update automagically */
862 /* g_warning ("Item %s mask %s modified", SP_OBJECT_ID (item), SP_OBJECT_ID (mask)); */
863 }
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)
874 {
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 }
910 }
914 /* Shape section */
916 /**
917 * Calls any registered handlers for the set_shape action
918 */
919 void
920 sp_shape_set_shape (SPShape *shape)
921 {
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 }
928 }
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)
938 {
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);
950 }
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)
957 {
958 if (shape->curve) {
959 return sp_curve_copy (shape->curve);
960 }
961 return NULL;
962 }
964 /* NOT FOR GENERAL PUBLIC UNTIL SORTED OUT (Lauris) */
965 void
966 sp_shape_set_curve_insync (SPShape *shape, SPCurve *curve, unsigned int owner)
967 {
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 }
978 }
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)
984 {
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 }
1001 }
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 :