1 #define __MARKER_C__
3 /*
4 * SVG <marker> implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * Bryce Harrington <bryce@bryceharrington.org>
9 *
10 * Copyright (C) 1999-2003 Lauris Kaplinski
11 * 2004-2006 Bryce Harrington
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #include "config.h"
19 #include "libnr/nr-matrix-fns.h"
20 #include "libnr/nr-matrix-ops.h"
21 #include "libnr/nr-scale-matrix-ops.h"
22 #include "libnr/nr-rotate-fns.h"
23 #include "svg/svg.h"
24 #include "display/nr-arena-group.h"
25 #include "xml/repr.h"
26 #include "attributes.h"
27 #include "marker.h"
29 struct SPMarkerView {
30 SPMarkerView *next;
31 unsigned int key;
32 unsigned int size;
33 NRArenaItem *items[1];
34 };
36 static void sp_marker_class_init (SPMarkerClass *klass);
37 static void sp_marker_init (SPMarker *marker);
39 static void sp_marker_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
40 static void sp_marker_release (SPObject *object);
41 static void sp_marker_set (SPObject *object, unsigned int key, const gchar *value);
42 static void sp_marker_update (SPObject *object, SPCtx *ctx, guint flags);
43 static Inkscape::XML::Node *sp_marker_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
45 static NRArenaItem *sp_marker_private_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
46 static void sp_marker_private_hide (SPItem *item, unsigned int key);
47 static void sp_marker_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
48 static void sp_marker_print (SPItem *item, SPPrintContext *ctx);
50 static void sp_marker_view_remove (SPMarker *marker, SPMarkerView *view, unsigned int destroyitems);
52 static SPGroupClass *parent_class;
54 /**
55 * Registers the SPMarker class with Gdk and returns its type number.
56 */
57 GType
58 sp_marker_get_type (void)
59 {
60 static GType type = 0;
61 if (!type) {
62 GTypeInfo info = {
63 sizeof (SPMarkerClass),
64 NULL, NULL,
65 (GClassInitFunc) sp_marker_class_init,
66 NULL, NULL,
67 sizeof (SPMarker),
68 16,
69 (GInstanceInitFunc) sp_marker_init,
70 NULL, /* value_table */
71 };
72 type = g_type_register_static (SP_TYPE_GROUP, "SPMarker", &info, (GTypeFlags)0);
73 }
74 return type;
75 }
77 /**
78 * Initializes a SPMarkerClass object. Establishes the function pointers to the class'
79 * member routines in the class vtable, and sets pointers to parent classes.
80 */
81 static void
82 sp_marker_class_init (SPMarkerClass *klass)
83 {
84 GObjectClass *object_class;
85 SPObjectClass *sp_object_class;
86 SPItemClass *sp_item_class;
88 object_class = G_OBJECT_CLASS (klass);
89 sp_object_class = (SPObjectClass *) klass;
90 sp_item_class = (SPItemClass *) klass;
92 parent_class = (SPGroupClass *)g_type_class_ref (SP_TYPE_GROUP);
94 sp_object_class->build = sp_marker_build;
95 sp_object_class->release = sp_marker_release;
96 sp_object_class->set = sp_marker_set;
97 sp_object_class->update = sp_marker_update;
98 sp_object_class->write = sp_marker_write;
100 sp_item_class->show = sp_marker_private_show;
101 sp_item_class->hide = sp_marker_private_hide;
102 sp_item_class->bbox = sp_marker_bbox;
103 sp_item_class->print = sp_marker_print;
104 }
106 /**
107 * Initializes an SPMarker object. This notes the marker's viewBox is
108 * not set and initializes the marker's c2p identity matrix.
109 */
110 static void
111 sp_marker_init (SPMarker *marker)
112 {
113 marker->viewBox_set = FALSE;
115 nr_matrix_set_identity (&marker->c2p);
116 }
118 /**
119 * Virtual build callback for SPMarker.
120 *
121 * This is to be invoked immediately after creation of an SPMarker. This
122 * method fills an SPMarker object with its SVG attributes, and calls the
123 * parent class' build routine to attach the object to its document and
124 * repr. The result will be creation of the whole document tree.
125 *
126 * \see sp_object_build()
127 */
128 static void
129 sp_marker_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
130 {
131 SPGroup *group;
132 SPMarker *marker;
134 group = (SPGroup *) object;
135 marker = (SPMarker *) object;
137 sp_object_read_attr (object, "markerUnits");
138 sp_object_read_attr (object, "refX");
139 sp_object_read_attr (object, "refY");
140 sp_object_read_attr (object, "markerWidth");
141 sp_object_read_attr (object, "markerHeight");
142 sp_object_read_attr (object, "orient");
143 sp_object_read_attr (object, "viewBox");
144 sp_object_read_attr (object, "preserveAspectRatio");
146 if (((SPObjectClass *) parent_class)->build)
147 ((SPObjectClass *) parent_class)->build (object, document, repr);
148 }
150 /**
151 * Removes, releases and unrefs all children of object
152 *
153 * This is the inverse of sp_marker_build(). It must be invoked as soon
154 * as the marker is removed from the tree, even if it is still referenced
155 * by other objects. It hides and removes any views of the marker, then
156 * calls the parent classes' release function to deregister the object
157 * and release its SPRepr bindings. The result will be the destruction
158 * of the entire document tree.
159 *
160 * \see sp_object_release()
161 */
162 static void
163 sp_marker_release (SPObject *object)
164 {
165 SPMarker *marker;
167 marker = (SPMarker *) object;
169 while (marker->views) {
170 /* Destroy all NRArenaitems etc. */
171 /* Parent class ::hide method */
172 ((SPItemClass *) parent_class)->hide ((SPItem *) marker, marker->views->key);
173 sp_marker_view_remove (marker, marker->views, TRUE);
174 }
176 if (((SPObjectClass *) parent_class)->release)
177 ((SPObjectClass *) parent_class)->release (object);
178 }
180 /**
181 * Sets an attribute, 'key', of a marker object to 'value'. Supported
182 * attributes that can be set with this routine include:
183 *
184 * SP_ATTR_MARKERUNITS
185 * SP_ATTR_REFX
186 * SP_ATTR_REFY
187 * SP_ATTR_MARKERWIDTH
188 * SP_ATTR_MARKERHEIGHT
189 * SP_ATTR_ORIENT
190 * SP_ATTR_VIEWBOX
191 * SP_ATTR_PRESERVEASPECTRATIO
192 */
193 static void
194 sp_marker_set (SPObject *object, unsigned int key, const gchar *value)
195 {
196 SPItem *item;
197 SPMarker *marker;
199 item = SP_ITEM (object);
200 marker = SP_MARKER (object);
202 switch (key) {
203 case SP_ATTR_MARKERUNITS:
204 marker->markerUnits_set = FALSE;
205 marker->markerUnits = SP_MARKER_UNITS_STROKEWIDTH;
206 if (value) {
207 if (!strcmp (value, "strokeWidth")) {
208 marker->markerUnits_set = TRUE;
209 } else if (!strcmp (value, "userSpaceOnUse")) {
210 marker->markerUnits = SP_MARKER_UNITS_USERSPACEONUSE;
211 marker->markerUnits_set = TRUE;
212 }
213 }
214 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
215 break;
216 case SP_ATTR_REFX:
217 marker->refX.readOrUnset(value);
218 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
219 break;
220 case SP_ATTR_REFY:
221 marker->refY.readOrUnset(value);
222 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
223 break;
224 case SP_ATTR_MARKERWIDTH:
225 marker->markerWidth.readOrUnset(value, SVGLength::NONE, 3.0, 3.0);
226 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
227 break;
228 case SP_ATTR_MARKERHEIGHT:
229 marker->markerHeight.readOrUnset(value, SVGLength::NONE, 3.0, 3.0);
230 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
231 break;
232 case SP_ATTR_ORIENT:
233 marker->orient_set = FALSE;
234 marker->orient_auto = FALSE;
235 marker->orient = 0.0;
236 if (value) {
237 if (!strcmp (value, "auto")) {
238 marker->orient_auto = TRUE;
239 marker->orient_set = TRUE;
240 } else if (sp_svg_number_read_f (value, &marker->orient)) {
241 marker->orient_set = TRUE;
242 }
243 }
244 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
245 break;
246 case SP_ATTR_VIEWBOX:
247 marker->viewBox_set = FALSE;
248 if (value) {
249 double x, y, width, height;
250 char *eptr;
251 /* fixme: We have to take original item affine into account */
252 /* fixme: Think (Lauris) */
253 eptr = (gchar *) value;
254 x = g_ascii_strtod (eptr, &eptr);
255 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
256 y = g_ascii_strtod (eptr, &eptr);
257 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
258 width = g_ascii_strtod (eptr, &eptr);
259 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
260 height = g_ascii_strtod (eptr, &eptr);
261 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
262 if ((width > 0) && (height > 0)) {
263 /* Set viewbox */
264 marker->viewBox.x0 = x;
265 marker->viewBox.y0 = y;
266 marker->viewBox.x1 = x + width;
267 marker->viewBox.y1 = y + height;
268 marker->viewBox_set = TRUE;
269 }
270 }
271 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
272 break;
273 case SP_ATTR_PRESERVEASPECTRATIO:
274 /* Do setup before, so we can use break to escape */
275 marker->aspect_set = FALSE;
276 marker->aspect_align = SP_ASPECT_NONE;
277 marker->aspect_clip = SP_ASPECT_MEET;
278 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
279 if (value) {
280 int len;
281 gchar c[256];
282 const gchar *p, *e;
283 unsigned int align, clip;
284 p = value;
285 while (*p && *p == 32) p += 1;
286 if (!*p) break;
287 e = p;
288 while (*e && *e != 32) e += 1;
289 len = e - p;
290 if (len > 8) break;
291 memcpy (c, value, len);
292 c[len] = 0;
293 /* Now the actual part */
294 if (!strcmp (c, "none")) {
295 align = SP_ASPECT_NONE;
296 } else if (!strcmp (c, "xMinYMin")) {
297 align = SP_ASPECT_XMIN_YMIN;
298 } else if (!strcmp (c, "xMidYMin")) {
299 align = SP_ASPECT_XMID_YMIN;
300 } else if (!strcmp (c, "xMaxYMin")) {
301 align = SP_ASPECT_XMAX_YMIN;
302 } else if (!strcmp (c, "xMinYMid")) {
303 align = SP_ASPECT_XMIN_YMID;
304 } else if (!strcmp (c, "xMidYMid")) {
305 align = SP_ASPECT_XMID_YMID;
306 } else if (!strcmp (c, "xMaxYMin")) {
307 align = SP_ASPECT_XMAX_YMID;
308 } else if (!strcmp (c, "xMinYMax")) {
309 align = SP_ASPECT_XMIN_YMAX;
310 } else if (!strcmp (c, "xMidYMax")) {
311 align = SP_ASPECT_XMID_YMAX;
312 } else if (!strcmp (c, "xMaxYMax")) {
313 align = SP_ASPECT_XMAX_YMAX;
314 } else {
315 break;
316 }
317 clip = SP_ASPECT_MEET;
318 while (*e && *e == 32) e += 1;
319 if (e) {
320 if (!strcmp (e, "meet")) {
321 clip = SP_ASPECT_MEET;
322 } else if (!strcmp (e, "slice")) {
323 clip = SP_ASPECT_SLICE;
324 } else {
325 break;
326 }
327 }
328 marker->aspect_set = TRUE;
329 marker->aspect_align = align;
330 marker->aspect_clip = clip;
331 }
332 break;
333 default:
334 if (((SPObjectClass *) parent_class)->set)
335 ((SPObjectClass *) parent_class)->set (object, key, value);
336 break;
337 }
338 }
340 /**
341 * Updates <marker> when its attributes have changed. Takes care of setting up
342 * transformations and viewBoxes.
343 */
344 static void
345 sp_marker_update (SPObject *object, SPCtx *ctx, guint flags)
346 {
347 SPItem *item;
348 SPMarker *marker;
349 SPItemCtx rctx;
350 NRRect *vb;
351 double x, y, width, height;
352 NRMatrix q;
353 SPMarkerView *v;
355 item = SP_ITEM (object);
356 marker = SP_MARKER (object);
358 /* fixme: We have to set up clip here too */
360 /* Copy parent context */
361 rctx.ctx = *ctx;
362 /* Initialize tranformations */
363 rctx.i2doc = NR::identity();
364 rctx.i2vp = NR::identity();
365 /* Set up viewport */
366 rctx.vp.x0 = 0.0;
367 rctx.vp.y0 = 0.0;
368 rctx.vp.x1 = marker->markerWidth.computed;
369 rctx.vp.y1 = marker->markerHeight.computed;
371 /* Start with identity transform */
372 nr_matrix_set_identity (&marker->c2p);
374 /* Viewbox is always present, either implicitly or explicitly */
375 if (marker->viewBox_set) {
376 vb = &marker->viewBox;
377 } else {
378 vb = &rctx.vp;
379 }
380 /* Now set up viewbox transformation */
381 /* Determine actual viewbox in viewport coordinates */
382 if (marker->aspect_align == SP_ASPECT_NONE) {
383 x = 0.0;
384 y = 0.0;
385 width = rctx.vp.x1 - rctx.vp.x0;
386 height = rctx.vp.y1 - rctx.vp.y0;
387 } else {
388 double scalex, scaley, scale;
389 /* Things are getting interesting */
390 scalex = (rctx.vp.x1 - rctx.vp.x0) / (vb->x1 - vb->x0);
391 scaley = (rctx.vp.y1 - rctx.vp.y0) / (vb->y1 - vb->y0);
392 scale = (marker->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley);
393 width = (vb->x1 - vb->x0) * scale;
394 height = (vb->y1 - vb->y0) * scale;
395 /* Now place viewbox to requested position */
396 switch (marker->aspect_align) {
397 case SP_ASPECT_XMIN_YMIN:
398 x = 0.0;
399 y = 0.0;
400 break;
401 case SP_ASPECT_XMID_YMIN:
402 x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
403 y = 0.0;
404 break;
405 case SP_ASPECT_XMAX_YMIN:
406 x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
407 y = 0.0;
408 break;
409 case SP_ASPECT_XMIN_YMID:
410 x = 0.0;
411 y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
412 break;
413 case SP_ASPECT_XMID_YMID:
414 x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
415 y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
416 break;
417 case SP_ASPECT_XMAX_YMID:
418 x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
419 y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
420 break;
421 case SP_ASPECT_XMIN_YMAX:
422 x = 0.0;
423 y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
424 break;
425 case SP_ASPECT_XMID_YMAX:
426 x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
427 y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
428 break;
429 case SP_ASPECT_XMAX_YMAX:
430 x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
431 y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
432 break;
433 default:
434 x = 0.0;
435 y = 0.0;
436 break;
437 }
438 }
439 /* Compose additional transformation from scale and position */
440 q.c[0] = width / (vb->x1 - vb->x0);
441 q.c[1] = 0.0;
442 q.c[2] = 0.0;
443 q.c[3] = height / (vb->y1 - vb->y0);
444 q.c[4] = -vb->x0 * q.c[0] + x;
445 q.c[5] = -vb->y0 * q.c[3] + y;
446 /* Append viewbox transformation */
447 nr_matrix_multiply (&marker->c2p, &q, &marker->c2p);
450 /* Append reference translation */
451 /* fixme: lala (Lauris) */
452 nr_matrix_set_translate (&q, -marker->refX.computed, -marker->refY.computed);
453 nr_matrix_multiply (&marker->c2p, &q, &marker->c2p);
455 rctx.i2doc = marker->c2p * rctx.i2doc;
457 /* If viewBox is set reinitialize child viewport */
458 /* Otherwise it already correct */
459 if (marker->viewBox_set) {
460 rctx.vp.x0 = marker->viewBox.x0;
461 rctx.vp.y0 = marker->viewBox.y0;
462 rctx.vp.x1 = marker->viewBox.x1;
463 rctx.vp.y1 = marker->viewBox.y1;
464 rctx.i2vp = NR::identity();
465 }
467 /* And invoke parent method */
468 if (((SPObjectClass *) (parent_class))->update)
469 ((SPObjectClass *) (parent_class))->update (object, (SPCtx *) &rctx, flags);
471 /* As last step set additional transform of arena group */
472 for (v = marker->views; v != NULL; v = v->next) {
473 for (unsigned i = 0 ; i < v->size ; i++) {
474 if (v->items[i]) {
475 nr_arena_group_set_child_transform(NR_ARENA_GROUP(v->items[i]), &marker->c2p);
476 }
477 }
478 }
479 }
481 /**
482 * Writes the object's properties into its repr object.
483 */
484 static Inkscape::XML::Node *
485 sp_marker_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
486 {
487 SPMarker *marker;
489 marker = SP_MARKER (object);
491 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
492 repr = sp_repr_new ("svg:marker");
493 }
495 if (marker->markerUnits_set) {
496 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
497 repr->setAttribute("markerUnits", "strokeWidth");
498 } else {
499 repr->setAttribute("markerUnits", "userSpaceOnUse");
500 }
501 } else {
502 repr->setAttribute("markerUnits", NULL);
503 }
504 if (marker->refX._set) {
505 sp_repr_set_svg_double(repr, "refX", marker->refX.computed);
506 } else {
507 repr->setAttribute("refX", NULL);
508 }
509 if (marker->refY._set) {
510 sp_repr_set_svg_double (repr, "refY", marker->refY.computed);
511 } else {
512 repr->setAttribute("refY", NULL);
513 }
514 if (marker->markerWidth._set) {
515 sp_repr_set_svg_double (repr, "markerWidth", marker->markerWidth.computed);
516 } else {
517 repr->setAttribute("markerWidth", NULL);
518 }
519 if (marker->markerHeight._set) {
520 sp_repr_set_svg_double (repr, "markerHeight", marker->markerHeight.computed);
521 } else {
522 repr->setAttribute("markerHeight", NULL);
523 }
524 if (marker->orient_set) {
525 if (marker->orient_auto) {
526 repr->setAttribute("orient", "auto");
527 } else {
528 sp_repr_set_css_double(repr, "orient", marker->orient);
529 }
530 } else {
531 repr->setAttribute("orient", NULL);
532 }
533 /* fixme: */
534 repr->setAttribute("viewBox", object->repr->attribute("viewBox"));
535 repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
537 if (((SPObjectClass *) (parent_class))->write)
538 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
540 return repr;
541 }
543 /**
544 * This routine is disabled to break propagation.
545 */
546 static NRArenaItem *
547 sp_marker_private_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
548 {
549 /* Break propagation */
550 return NULL;
551 }
553 /**
554 * This routine is disabled to break propagation.
555 */
556 static void
557 sp_marker_private_hide (SPItem *item, unsigned int key)
558 {
559 /* Break propagation */
560 }
562 /**
563 * This routine is disabled to break propagation.
564 */
565 static void
566 sp_marker_bbox(SPItem const *, NRRect *, NR::Matrix const &, unsigned const)
567 {
568 /* Break propagation */
569 }
571 /**
572 * This routine is disabled to break propagation.
573 */
574 static void
575 sp_marker_print (SPItem *item, SPPrintContext *ctx)
576 {
577 /* Break propagation */
578 }
580 /* fixme: Remove link if zero-sized (Lauris) */
582 /**
583 * Removes any SPMarkerViews that a marker has with a specific key.
584 * Set up the NRArenaItem array's size in the specified SPMarker's SPMarkerView.
585 * This is called from sp_shape_update() for shapes that have markers. It
586 * removes the old view of the marker and establishes a new one, registering
587 * it with the marker's list of views for future updates.
588 *
589 * \param marker Marker to create views in.
590 * \param key Key to give each SPMarkerView.
591 * \param size Number of NRArenaItems to put in the SPMarkerView.
592 */
593 void
594 sp_marker_show_dimension (SPMarker *marker, unsigned int key, unsigned int size)
595 {
596 SPMarkerView *view;
597 unsigned int i;
599 for (view = marker->views; view != NULL; view = view->next) {
600 if (view->key == key) break;
601 }
602 if (view && (view->size != size)) {
603 /* Free old view and allocate new */
604 /* Parent class ::hide method */
605 ((SPItemClass *) parent_class)->hide ((SPItem *) marker, key);
606 sp_marker_view_remove (marker, view, TRUE);
607 view = NULL;
608 }
609 if (!view) {
610 view = (SPMarkerView *)g_malloc (sizeof (SPMarkerView) + (size) * sizeof (NRArenaItem *));
611 for (i = 0; i < size; i++) view->items[i] = NULL;
612 view->next = marker->views;
613 marker->views = view;
614 view->key = key;
615 view->size = size;
616 }
617 }
619 /**
620 * Shows an instance of a marker. This is called during sp_shape_update_marker_view()
621 * show and transform a child item in the arena for all views with the given key.
622 */
623 NRArenaItem *
624 sp_marker_show_instance (SPMarker *marker, NRArenaItem *parent,
625 unsigned int key, unsigned int pos,
626 NR::Matrix const &base, float linewidth)
627 {
628 for (SPMarkerView *v = marker->views; v != NULL; v = v->next) {
629 if (v->key == key) {
630 if (pos >= v->size) {
631 return NULL;
632 }
633 if (!v->items[pos]) {
634 /* Parent class ::show method */
635 v->items[pos] = ((SPItemClass *) parent_class)->show ((SPItem *) marker,
636 parent->arena, key,
637 SP_ITEM_REFERENCE_FLAGS);
638 if (v->items[pos]) {
639 /* fixme: Position (Lauris) */
640 nr_arena_item_add_child (parent, v->items[pos], NULL);
641 /* nr_arena_item_unref (v->items[pos]); */
642 nr_arena_group_set_child_transform((NRArenaGroup *) v->items[pos], &marker->c2p);
643 }
644 }
645 if (v->items[pos]) {
646 NR::Matrix m;
647 if (marker->orient_auto) {
648 m = base;
649 } else {
650 /* fixme: Orient units (Lauris) */
651 m = NR::Matrix(rotate_degrees(marker->orient));
652 m *= get_translation(base);
653 }
654 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
655 m = NR::scale(linewidth) * m;
656 }
658 nr_arena_item_set_transform(v->items[pos], m);
659 }
660 return v->items[pos];
661 }
662 }
664 return NULL;
665 }
667 /**
668 * Hides/removes all views of the given marker that have key 'key'.
669 * This replaces SPItem implementation because we have our own views
670 * \param key SPMarkerView key to hide.
671 */
672 void
673 sp_marker_hide (SPMarker *marker, unsigned int key)
674 {
675 SPMarkerView *v;
677 v = marker->views;
678 while (v != NULL) {
679 SPMarkerView *next;
680 next = v->next;
681 if (v->key == key) {
682 /* Parent class ::hide method */
683 ((SPItemClass *) parent_class)->hide ((SPItem *) marker, key);
684 sp_marker_view_remove (marker, v, TRUE);
685 return;
686 }
687 v = next;
688 }
689 }
691 /**
692 * Removes a given view. Also will destroy sub-items in the view if destroyitems
693 * is set to a non-zero value.
694 */
695 static void
696 sp_marker_view_remove (SPMarker *marker, SPMarkerView *view, unsigned int destroyitems)
697 {
698 unsigned int i;
699 if (view == marker->views) {
700 marker->views = view->next;
701 } else {
702 SPMarkerView *v;
703 for (v = marker->views; v->next != view; v = v->next) if (!v->next) return;
704 v->next = view->next;
705 }
706 if (destroyitems) {
707 for (i = 0; i < view->size; i++) {
708 /* We have to walk through the whole array because there may be hidden items */
709 if (view->items[i]) nr_arena_item_unref (view->items[i]);
710 }
711 }
712 g_free (view);
713 }