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