Code

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