Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / marker.cpp
1 /*
2  * SVG <marker> implementation
3  *
4  * Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   Bryce Harrington <bryce@bryceharrington.org>
7  *   Abhishek Sharma
8  *
9  * Copyright (C) 1999-2003 Lauris Kaplinski
10  *               2004-2006 Bryce Harrington
11  *               2008      Johan Engelen
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #include <cstring>
17 #include <string>
18 #include "config.h"
21 #include "libnr/nr-matrix-fns.h"
22 #include "libnr/nr-matrix-ops.h"
23 #include "libnr/nr-matrix-translate-ops.h"
24 #include "libnr/nr-scale-matrix-ops.h"
25 #include "libnr/nr-translate-matrix-ops.h"
26 #include "libnr/nr-convert2geom.h"
27 #include <2geom/matrix.h>
28 #include "svg/svg.h"
29 #include "display/nr-arena-group.h"
30 #include "xml/repr.h"
31 #include "attributes.h"
32 #include "marker.h"
33 #include "document.h"
34 #include "document-private.h"
36 struct SPMarkerView {
37         SPMarkerView *next;
38         unsigned int key;
39   std::vector<NRArenaItem *> items;
40 };
42 static void sp_marker_class_init (SPMarkerClass *klass);
43 static void sp_marker_init (SPMarker *marker);
45 static void sp_marker_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
46 static void sp_marker_release (SPObject *object);
47 static void sp_marker_set (SPObject *object, unsigned int key, const gchar *value);
48 static void sp_marker_update (SPObject *object, SPCtx *ctx, guint flags);
49 static Inkscape::XML::Node *sp_marker_write (SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
51 static NRArenaItem *sp_marker_private_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
52 static void sp_marker_private_hide (SPItem *item, unsigned int key);
53 static void sp_marker_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags);
54 static void sp_marker_print (SPItem *item, SPPrintContext *ctx);
56 static void sp_marker_view_remove (SPMarker *marker, SPMarkerView *view, unsigned int destroyitems);
58 static SPGroupClass *parent_class;
60 /**
61  * Registers the SPMarker class with Gdk and returns its type number.
62  */
63 GType
64 sp_marker_get_type (void)
65 {
66         static GType type = 0;
67         if (!type) {
68                 GTypeInfo info = {
69                         sizeof (SPMarkerClass),
70                         NULL, NULL,
71                         (GClassInitFunc) sp_marker_class_init,
72                         NULL, NULL,
73                         sizeof (SPMarker),
74                         16,
75                         (GInstanceInitFunc) sp_marker_init,
76                         NULL,   /* value_table */
77                 };
78                 type = g_type_register_static (SP_TYPE_GROUP, "SPMarker", &info, (GTypeFlags)0);
79         }
80         return type;
81 }
83 /**
84  * Initializes a SPMarkerClass object.  Establishes the function pointers to the class'
85  * member routines in the class vtable, and sets pointers to parent classes.
86  */
87 static void
88 sp_marker_class_init (SPMarkerClass *klass)
89 {
90         GObjectClass *object_class;
91         SPObjectClass *sp_object_class;
92         SPItemClass *sp_item_class;
94         object_class = G_OBJECT_CLASS (klass);
95         sp_object_class = (SPObjectClass *) klass;
96         sp_item_class = (SPItemClass *) klass;
98         parent_class = (SPGroupClass *)g_type_class_ref (SP_TYPE_GROUP);
100         sp_object_class->build = sp_marker_build;
101         sp_object_class->release = sp_marker_release;
102         sp_object_class->set = sp_marker_set;
103         sp_object_class->update = sp_marker_update;
104         sp_object_class->write = sp_marker_write;
106         sp_item_class->show = sp_marker_private_show;
107         sp_item_class->hide = sp_marker_private_hide;
108         sp_item_class->bbox = sp_marker_bbox;
109         sp_item_class->print = sp_marker_print;
112 /**
113  * Initializes an SPMarker object.  This notes the marker's viewBox is
114  * not set and initializes the marker's c2p identity matrix.
115  */
116 static void
117 sp_marker_init (SPMarker *marker)
119     marker->viewBox = Geom::OptRect();
120     marker->c2p.setIdentity();
121     marker->views = NULL;
124 /**
125  * Virtual build callback for SPMarker.
126  *
127  * This is to be invoked immediately after creation of an SPMarker.  This
128  * method fills an SPMarker object with its SVG attributes, and calls the
129  * parent class' build routine to attach the object to its document and
130  * repr.  The result will be creation of the whole document tree.
131  *
132  * \see sp_object_build()
133  */
134 static void
135 sp_marker_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
137         SPGroup *group;
138         SPMarker *marker;
140         group = (SPGroup *) object;
141         marker = (SPMarker *) object;
143         object->readAttr( "markerUnits" );
144         object->readAttr( "refX" );
145         object->readAttr( "refY" );
146         object->readAttr( "markerWidth" );
147         object->readAttr( "markerHeight" );
148         object->readAttr( "orient" );
149         object->readAttr( "viewBox" );
150         object->readAttr( "preserveAspectRatio" );
152         if (((SPObjectClass *) parent_class)->build)
153                 ((SPObjectClass *) parent_class)->build (object, document, repr);
156 /**
157  * Removes, releases and unrefs all children of object
158  *
159  * This is the inverse of sp_marker_build().  It must be invoked as soon
160  * as the marker is removed from the tree, even if it is still referenced
161  * by other objects.  It hides and removes any views of the marker, then
162  * calls the parent classes' release function to deregister the object
163  * and release its SPRepr bindings.  The result will be the destruction
164  * of the entire document tree.
165  *
166  * \see sp_object_release()
167  */
168 static void
169 sp_marker_release (SPObject *object)
171         SPMarker *marker;
173         marker = (SPMarker *) object;
175         while (marker->views) {
176                 /* Destroy all NRArenaitems etc. */
177                 /* Parent class ::hide method */
178                 ((SPItemClass *) parent_class)->hide ((SPItem *) marker, marker->views->key);
179                 sp_marker_view_remove (marker, marker->views, TRUE);
180         }
182         if (((SPObjectClass *) parent_class)->release)
183                 ((SPObjectClass *) parent_class)->release (object);
186 /**
187  * Sets an attribute, 'key', of a marker object to 'value'.  Supported
188  * attributes that can be set with this routine include:
189  *
190  *     SP_ATTR_MARKERUNITS
191  *     SP_ATTR_REFX
192  *     SP_ATTR_REFY
193  *     SP_ATTR_MARKERWIDTH
194  *     SP_ATTR_MARKERHEIGHT
195  *     SP_ATTR_ORIENT
196  *     SP_ATTR_VIEWBOX
197  *     SP_ATTR_PRESERVEASPECTRATIO
198  */
199 static void
200 sp_marker_set (SPObject *object, unsigned int key, const gchar *value)
202         SPItem *item;
203         SPMarker *marker;
205         item = SP_ITEM (object);
206         marker = SP_MARKER (object);
208         switch (key) {
209         case SP_ATTR_MARKERUNITS:
210                 marker->markerUnits_set = FALSE;
211                 marker->markerUnits = SP_MARKER_UNITS_STROKEWIDTH;
212                 if (value) {
213                         if (!strcmp (value, "strokeWidth")) {
214                                 marker->markerUnits_set = TRUE;
215                         } else if (!strcmp (value, "userSpaceOnUse")) {
216                                 marker->markerUnits = SP_MARKER_UNITS_USERSPACEONUSE;
217                                 marker->markerUnits_set = TRUE;
218                         }
219                 }
220                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
221                 break;
222         case SP_ATTR_REFX:
223                 marker->refX.readOrUnset(value);
224                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
225                 break;
226         case SP_ATTR_REFY:
227                 marker->refY.readOrUnset(value);
228                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
229                 break;
230         case SP_ATTR_MARKERWIDTH:
231                 marker->markerWidth.readOrUnset(value, SVGLength::NONE, 3.0, 3.0);
232                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
233                 break;
234         case SP_ATTR_MARKERHEIGHT:
235                 marker->markerHeight.readOrUnset(value, SVGLength::NONE, 3.0, 3.0);
236                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
237                 break;
238         case SP_ATTR_ORIENT:
239                 marker->orient_set = FALSE;
240                 marker->orient_auto = FALSE;
241                 marker->orient = 0.0;
242                 if (value) {
243                         if (!strcmp (value, "auto")) {
244                                 marker->orient_auto = TRUE;
245                                 marker->orient_set = TRUE;
246                         } else if (sp_svg_number_read_f (value, &marker->orient)) {
247                                 marker->orient_set = TRUE;
248                         }
249                 }
250                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
251                 break;
252         case SP_ATTR_VIEWBOX:
253         marker->viewBox = Geom::OptRect();
254                 if (value) {
255                         double x, y, width, height;
256                         char *eptr;
257                         /* fixme: We have to take original item affine into account */
258                         /* fixme: Think (Lauris) */
259                         eptr = (gchar *) value;
260                         x = g_ascii_strtod (eptr, &eptr);
261                         while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
262                         y = g_ascii_strtod (eptr, &eptr);
263                         while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
264                         width = g_ascii_strtod (eptr, &eptr);
265                         while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
266                         height = g_ascii_strtod (eptr, &eptr);
267                         while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
268                         if ((width > 0) && (height > 0)) {
269                                 /* Set viewbox */
270                 marker->viewBox = Geom::Rect( Geom::Point(x,y),
271                                               Geom::Point(x + width, y + height) );
272                         }
273                 }
274                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
275                 break;
276         case SP_ATTR_PRESERVEASPECTRATIO:
277                 /* Do setup before, so we can use break to escape */
278                 marker->aspect_set = FALSE;
279                 marker->aspect_align = SP_ASPECT_NONE;
280                 marker->aspect_clip = SP_ASPECT_MEET;
281                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
282                 if (value) {
283                         int len;
284                         gchar c[256];
285                         const gchar *p, *e;
286                         unsigned int align, clip;
287                         p = value;
288                         while (*p && *p == 32) p += 1;
289                         if (!*p) break;
290                         e = p;
291                         while (*e && *e != 32) e += 1;
292                         len = e - p;
293                         if (len > 8) break;
294                         memcpy (c, value, len);
295                         c[len] = 0;
296                         /* Now the actual part */
297                         if (!strcmp (c, "none")) {
298                                 align = SP_ASPECT_NONE;
299                         } else if (!strcmp (c, "xMinYMin")) {
300                                 align = SP_ASPECT_XMIN_YMIN;
301                         } else if (!strcmp (c, "xMidYMin")) {
302                                 align = SP_ASPECT_XMID_YMIN;
303                         } else if (!strcmp (c, "xMaxYMin")) {
304                                 align = SP_ASPECT_XMAX_YMIN;
305                         } else if (!strcmp (c, "xMinYMid")) {
306                                 align = SP_ASPECT_XMIN_YMID;
307                         } else if (!strcmp (c, "xMidYMid")) {
308                                 align = SP_ASPECT_XMID_YMID;
309                         } else if (!strcmp (c, "xMaxYMid")) {
310                                 align = SP_ASPECT_XMAX_YMID;
311                         } else if (!strcmp (c, "xMinYMax")) {
312                                 align = SP_ASPECT_XMIN_YMAX;
313                         } else if (!strcmp (c, "xMidYMax")) {
314                                 align = SP_ASPECT_XMID_YMAX;
315                         } else if (!strcmp (c, "xMaxYMax")) {
316                                 align = SP_ASPECT_XMAX_YMAX;
317                         } else {
318                                 break;
319                         }
320                         clip = SP_ASPECT_MEET;
321                         while (*e && *e == 32) e += 1;
322                         if (*e) {
323                                 if (!strcmp (e, "meet")) {
324                                         clip = SP_ASPECT_MEET;
325                                 } else if (!strcmp (e, "slice")) {
326                                         clip = SP_ASPECT_SLICE;
327                                 } else {
328                                         break;
329                                 }
330                         }
331                         marker->aspect_set = TRUE;
332                         marker->aspect_align = align;
333                         marker->aspect_clip = clip;
334                 }
335                 break;
336         default:
337                 if (((SPObjectClass *) parent_class)->set)
338                         ((SPObjectClass *) parent_class)->set (object, key, value);
339                 break;
340         }
343 /**
344  * Updates <marker> when its attributes have changed.  Takes care of setting up
345  * transformations and viewBoxes.
346  */
347 static void
348 sp_marker_update (SPObject *object, SPCtx *ctx, guint flags)
350         SPItem *item;
351         SPMarker *marker;
352         SPItemCtx rctx;
353     Geom::Rect vb;
354         double x, y, width, height;
355         SPMarkerView *v;
357         item = SP_ITEM (object);
358         marker = SP_MARKER (object);
360         /* fixme: We have to set up clip here too */
362         /* Copy parent context */
363         rctx.ctx = *ctx;
364         /* Initialize tranformations */
365         rctx.i2doc = Geom::identity();
366         rctx.i2vp = Geom::identity();
367         /* Set up viewport */
368         rctx.vp.x0 = 0.0;
369         rctx.vp.y0 = 0.0;
370         rctx.vp.x1 = marker->markerWidth.computed;
371         rctx.vp.y1 = marker->markerHeight.computed;
373         /* Start with identity transform */
374         marker->c2p.setIdentity();
376         /* Viewbox is always present, either implicitly or explicitly */
377     if (marker->viewBox) {
378         vb = *marker->viewBox;
379         } else {
380         vb = *(rctx.vp.upgrade_2geom());
381         }
382         /* Now set up viewbox transformation */
383         /* Determine actual viewbox in viewport coordinates */
384         if (marker->aspect_align == SP_ASPECT_NONE) {
385                 x = 0.0;
386                 y = 0.0;
387                 width = rctx.vp.x1 - rctx.vp.x0;
388                 height = rctx.vp.y1 - rctx.vp.y0;
389         } else {
390                 double scalex, scaley, scale;
391                 /* Things are getting interesting */
392         scalex = (rctx.vp.x1 - rctx.vp.x0) / (vb.width());
393         scaley = (rctx.vp.y1 - rctx.vp.y0) / (vb.height());
394                 scale = (marker->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley);
395         width = (vb.width()) * scale;
396         height = (vb.height()) * scale;
397                 /* Now place viewbox to requested position */
398                 switch (marker->aspect_align) {
399                 case SP_ASPECT_XMIN_YMIN:
400                         x = 0.0;
401                         y = 0.0;
402                         break;
403                 case SP_ASPECT_XMID_YMIN:
404                         x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
405                         y = 0.0;
406                         break;
407                 case SP_ASPECT_XMAX_YMIN:
408                         x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
409                         y = 0.0;
410                         break;
411                 case SP_ASPECT_XMIN_YMID:
412                         x = 0.0;
413                         y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
414                         break;
415                 case SP_ASPECT_XMID_YMID:
416                         x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
417                         y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
418                         break;
419                 case SP_ASPECT_XMAX_YMID:
420                         x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
421                         y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
422                         break;
423                 case SP_ASPECT_XMIN_YMAX:
424                         x = 0.0;
425                         y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
426                         break;
427                 case SP_ASPECT_XMID_YMAX:
428                         x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
429                         y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
430                         break;
431                 case SP_ASPECT_XMAX_YMAX:
432                         x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
433                         y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
434                         break;
435                 default:
436                         x = 0.0;
437                         y = 0.0;
438                         break;
439                 }
440         }
442         // viewbox transformation and reference translation
443         marker->c2p = Geom::Translate(-marker->refX.computed, -marker->refY.computed) *
444                 Geom::Scale(width / vb.width(), height / vb.height());
446         rctx.i2doc = marker->c2p * rctx.i2doc;
448         /* If viewBox is set reinitialize child viewport */
449         /* Otherwise it already correct */
450         if (marker->viewBox) {
451             rctx.vp.x0 = marker->viewBox->min()[Geom::X];
452             rctx.vp.y0 = marker->viewBox->min()[Geom::Y];
453             rctx.vp.x1 = marker->viewBox->max()[Geom::X];
454             rctx.vp.y1 = marker->viewBox->max()[Geom::Y];
455             rctx.i2vp = Geom::identity();
456         }
458         /* And invoke parent method */
459         if (((SPObjectClass *) (parent_class))->update)
460                 ((SPObjectClass *) (parent_class))->update (object, (SPCtx *) &rctx, flags);
462         /* As last step set additional transform of arena group */
463         for (v = marker->views; v != NULL; v = v->next) {
464       for (unsigned i = 0 ; i < v->items.size() ; i++) {
465                 if (v->items[i]) {
466                     Geom::Matrix tmp = marker->c2p;
467                     nr_arena_group_set_child_transform(NR_ARENA_GROUP(v->items[i]), &tmp);
468                 }
469       }
470         }
473 /**
474  * Writes the object's properties into its repr object.
475  */
476 static Inkscape::XML::Node *
477 sp_marker_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
479         SPMarker *marker;
481         marker = SP_MARKER (object);
483         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
484                 repr = xml_doc->createElement("svg:marker");
485         }
487         if (marker->markerUnits_set) {
488                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
489                         repr->setAttribute("markerUnits", "strokeWidth");
490                 } else {
491                         repr->setAttribute("markerUnits", "userSpaceOnUse");
492                 }
493         } else {
494                 repr->setAttribute("markerUnits", NULL);
495         }
496         if (marker->refX._set) {
497                 sp_repr_set_svg_double(repr, "refX", marker->refX.computed);
498         } else {
499                 repr->setAttribute("refX", NULL);
500         }
501         if (marker->refY._set) {
502                 sp_repr_set_svg_double (repr, "refY", marker->refY.computed);
503         } else {
504                 repr->setAttribute("refY", NULL);
505         }
506         if (marker->markerWidth._set) {
507                 sp_repr_set_svg_double (repr, "markerWidth", marker->markerWidth.computed);
508         } else {
509                 repr->setAttribute("markerWidth", NULL);
510         }
511         if (marker->markerHeight._set) {
512                 sp_repr_set_svg_double (repr, "markerHeight", marker->markerHeight.computed);
513         } else {
514                 repr->setAttribute("markerHeight", NULL);
515         }
516         if (marker->orient_set) {
517                 if (marker->orient_auto) {
518                         repr->setAttribute("orient", "auto");
519                 } else {
520                         sp_repr_set_css_double(repr, "orient", marker->orient);
521                 }
522         } else {
523                 repr->setAttribute("orient", NULL);
524         }
525         /* fixme: */
526         //XML Tree being used directly here while it shouldn't be....
527         repr->setAttribute("viewBox", object->getRepr()->attribute("viewBox"));
528         //XML Tree being used directly here while it shouldn't be....
529         repr->setAttribute("preserveAspectRatio", object->getRepr()->attribute("preserveAspectRatio"));
531         if (((SPObjectClass *) (parent_class))->write)
532                 ((SPObjectClass *) (parent_class))->write (object, xml_doc, repr, flags);
534         return repr;
537 /**
538  * This routine is disabled to break propagation.
539  */
540 static NRArenaItem *
541 sp_marker_private_show (SPItem */*item*/, NRArena */*arena*/, unsigned int /*key*/, unsigned int /*flags*/)
543     /* Break propagation */
544     return NULL;
547 /**
548  * This routine is disabled to break propagation.
549  */
550 static void
551 sp_marker_private_hide (SPItem */*item*/, unsigned int /*key*/)
553     /* Break propagation */
556 /**
557  * This routine is disabled to break propagation.
558  */
559 static void
560 sp_marker_bbox(SPItem const *, NRRect *, Geom::Matrix const &, unsigned const)
562         /* Break propagation */
565 /**
566  * This routine is disabled to break propagation.
567  */
568 static void
569 sp_marker_print (SPItem */*item*/, SPPrintContext */*ctx*/)
571     /* Break propagation */
574 /* fixme: Remove link if zero-sized (Lauris) */
576 /**
577  * Removes any SPMarkerViews that a marker has with a specific key.
578  * Set up the NRArenaItem array's size in the specified SPMarker's SPMarkerView.
579  * This is called from sp_shape_update() for shapes that have markers.  It
580  * removes the old view of the marker and establishes a new one, registering
581  * it with the marker's list of views for future updates.
582  *
583  * \param marker Marker to create views in.
584  * \param key Key to give each SPMarkerView.
585  * \param size Number of NRArenaItems to put in the SPMarkerView.
586  */
587 void
588 sp_marker_show_dimension (SPMarker *marker, unsigned int key, unsigned int size)
590     SPMarkerView *view;
591     unsigned int i;
593     for (view = marker->views; view != NULL; view = view->next) {
594         if (view->key == key) break;
595     }
596     if (view && (view->items.size() != size)) {
597         /* Free old view and allocate new */
598         /* Parent class ::hide method */
599         ((SPItemClass *) parent_class)->hide ((SPItem *) marker, key);
600         sp_marker_view_remove (marker, view, TRUE);
601         view = NULL;
602     }
603     if (!view) {
604         view = new SPMarkerView();
605         view->items.clear();
606         for (i = 0; i < size; i++) {
607             view->items.push_back(NULL);
608         }
609         view->next = marker->views;
610         marker->views = view;
611         view->key = key;
612     }
615 /**
616  * Shows an instance of a marker.  This is called during sp_shape_update_marker_view()
617  * show and transform a child item in the arena for all views with the given key.
618  */
619 NRArenaItem *
620 sp_marker_show_instance ( SPMarker *marker, NRArenaItem *parent,
621                           unsigned int key, unsigned int pos,
622                           Geom::Matrix const &base, float linewidth)
624     for (SPMarkerView *v = marker->views; v != NULL; v = v->next) {
625         if (v->key == key) {
626             if (pos >= v->items.size()) {
627                 return NULL;
628             }
629             if (!v->items[pos]) {
630                 /* Parent class ::show method */
631                 v->items[pos] = ((SPItemClass *) parent_class)->show ((SPItem *) marker,
632                                                                       parent->arena, key,
633                                                                       SP_ITEM_REFERENCE_FLAGS);
634                 if (v->items[pos]) {
635                     /* fixme: Position (Lauris) */
636                     nr_arena_item_add_child (parent, v->items[pos], NULL);
637                     /* nr_arena_item_unref (v->items[pos]); */
638                     Geom::Matrix tmp = marker->c2p;
639                     nr_arena_group_set_child_transform((NRArenaGroup *) v->items[pos], &tmp);
640                 }
641             }
642             if (v->items[pos]) {
643                 Geom::Matrix m;
644                 if (marker->orient_auto) {
645                     m = base;
646                 } else {
647                     /* fixme: Orient units (Lauris) */
648                     m = Geom::Rotate::from_degrees(marker->orient);
649                     m *= Geom::Translate(base.translation());
650                 }
651                 if (marker->markerUnits == SP_MARKER_UNITS_STROKEWIDTH) {
652                     m = Geom::Scale(linewidth) * m;
653                 }
654                 
655                 nr_arena_item_set_transform(v->items[pos], m);
656             }
657             return v->items[pos];
658         }
659     }
661     return NULL;
664 /**
665  * Hides/removes all views of the given marker that have key 'key'.
666  * This replaces SPItem implementation because we have our own views
667  * \param key SPMarkerView key to hide.
668  */
669 void
670 sp_marker_hide (SPMarker *marker, unsigned int key)
672         SPMarkerView *v;
674         v = marker->views;
675         while (v != NULL) {
676                 SPMarkerView *next;
677                 next = v->next;
678                 if (v->key == key) {
679                         /* Parent class ::hide method */
680                         ((SPItemClass *) parent_class)->hide ((SPItem *) marker, key);
681                         sp_marker_view_remove (marker, v, TRUE);
682                         return;
683                 }
684                 v = next;
685         }
688 /**
689  * Removes a given view.  Also will destroy sub-items in the view if destroyitems
690  * is set to a non-zero value.
691  */
692 static void
693 sp_marker_view_remove (SPMarker *marker, SPMarkerView *view, unsigned int destroyitems)
695         unsigned int i;
696         if (view == marker->views) {
697                 marker->views = view->next;
698         } else {
699                 SPMarkerView *v;
700                 for (v = marker->views; v->next != view; v = v->next) if (!v->next) return;
701                 v->next = view->next;
702         }
703         if (destroyitems) {
704       for (i = 0; i < view->items.size(); i++) {
705                         /* We have to walk through the whole array because there may be hidden items */
706                         if (view->items[i]) nr_arena_item_unref (view->items[i]);
707                 }
708         }
709     view->items.clear();
710     delete view;
713 const gchar *generate_marker(GSList *reprs, Geom::Rect bounds, SPDocument *document, Geom::Matrix /*transform*/, Geom::Matrix move)
715     Inkscape::XML::Document *xml_doc = document->getReprDoc();
716     Inkscape::XML::Node *defsrepr = SP_DOCUMENT_DEFS(document)->getRepr();
718     Inkscape::XML::Node *repr = xml_doc->createElement("svg:marker");
720     // Uncommenting this will make the marker fixed-size independent of stroke width.
721     // Commented out for consistency with standard markers which scale when you change
722     // stroke width:
723     //repr->setAttribute("markerUnits", "userSpaceOnUse");
725     sp_repr_set_svg_double(repr, "markerWidth", bounds.dimensions()[Geom::X]);
726     sp_repr_set_svg_double(repr, "markerHeight", bounds.dimensions()[Geom::Y]);
728     repr->setAttribute("orient", "auto");
730     defsrepr->appendChild(repr);
731     const gchar *mark_id = repr->attribute("id");
732     SPObject *mark_object = document->getObjectById(mark_id);
734     for (GSList *i = reprs; i != NULL; i = i->next) {
735             Inkscape::XML::Node *node = (Inkscape::XML::Node *)(i->data);
736         SPItem *copy = SP_ITEM(mark_object->appendChildRepr(node));
738         Geom::Matrix dup_transform;
739         if (!sp_svg_transform_read (node->attribute("transform"), &dup_transform))
740             dup_transform = Geom::identity();
741         dup_transform *= move;
743         copy->doWriteTransform(SP_OBJECT_REPR(copy), dup_transform);
744     }
746     Inkscape::GC::release(repr);
747     return mark_id;
750 /*
751   Local Variables:
752   mode:c++
753   c-file-style:"stroustrup"
754   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
755   indent-tabs-mode:nil
756   fill-column:99
757   End:
758 */
759 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :