Code

moving trunk for module inkscape
[inkscape.git] / src / sp-symbol.cpp
1 #define __SP_SYMBOL_C__
3 /*
4  * SVG <symbol> 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"
16 #include "libnr/nr-matrix-fns.h"
17 #include "libnr/nr-matrix-ops.h"
18 #include "display/nr-arena-group.h"
19 #include "xml/repr.h"
20 #include "attributes.h"
21 #include "print.h"
22 #include "sp-symbol.h"
24 static void sp_symbol_class_init (SPSymbolClass *klass);
25 static void sp_symbol_init (SPSymbol *symbol);
27 static void sp_symbol_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
28 static void sp_symbol_release (SPObject *object);
29 static void sp_symbol_set (SPObject *object, unsigned int key, const gchar *value);
30 static void sp_symbol_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
31 static void sp_symbol_update (SPObject *object, SPCtx *ctx, guint flags);
32 static void sp_symbol_modified (SPObject *object, guint flags);
33 static Inkscape::XML::Node *sp_symbol_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
35 static NRArenaItem *sp_symbol_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
36 static void sp_symbol_hide (SPItem *item, unsigned int key);
37 static void sp_symbol_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
38 static void sp_symbol_print (SPItem *item, SPPrintContext *ctx);
40 static SPGroupClass *parent_class;
42 GType
43 sp_symbol_get_type (void)
44 {
45         static GType type = 0;
46         if (!type) {
47                 GTypeInfo info = {
48                         sizeof (SPSymbolClass),
49                         NULL, NULL,
50                         (GClassInitFunc) sp_symbol_class_init,
51                         NULL, NULL,
52                         sizeof (SPSymbol),
53                         16,
54                         (GInstanceInitFunc) sp_symbol_init,
55                         NULL,   /* value_table */
56                 };
57                 type = g_type_register_static (SP_TYPE_GROUP, "SPSymbol", &info, (GTypeFlags)0);
58         }
59         return type;
60 }
62 static void
63 sp_symbol_class_init (SPSymbolClass *klass)
64 {
65         GObjectClass *object_class;
66         SPObjectClass *sp_object_class;
67         SPItemClass *sp_item_class;
69         object_class = G_OBJECT_CLASS (klass);
70         sp_object_class = (SPObjectClass *) klass;
71         sp_item_class = (SPItemClass *) klass;
73         parent_class = (SPGroupClass *)g_type_class_ref (SP_TYPE_GROUP);
75         sp_object_class->build = sp_symbol_build;
76         sp_object_class->release = sp_symbol_release;
77         sp_object_class->set = sp_symbol_set;
78         sp_object_class->child_added = sp_symbol_child_added;
79         sp_object_class->update = sp_symbol_update;
80         sp_object_class->modified = sp_symbol_modified;
81         sp_object_class->write = sp_symbol_write;
83         sp_item_class->show = sp_symbol_show;
84         sp_item_class->hide = sp_symbol_hide;
85         sp_item_class->bbox = sp_symbol_bbox;
86         sp_item_class->print = sp_symbol_print;
87 }
89 static void
90 sp_symbol_init (SPSymbol *symbol)
91 {
92         symbol->viewBox_set = FALSE;
94         nr_matrix_set_identity (&symbol->c2p);
95 }
97 static void
98 sp_symbol_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
99 {
100         SPGroup *group;
101         SPSymbol *symbol;
103         group = (SPGroup *) object;
104         symbol = (SPSymbol *) object;
106         sp_object_read_attr (object, "viewBox");
107         sp_object_read_attr (object, "preserveAspectRatio");
109         if (((SPObjectClass *) parent_class)->build)
110                 ((SPObjectClass *) parent_class)->build (object, document, repr);
113 static void
114 sp_symbol_release (SPObject *object)
116         SPSymbol * symbol;
118         symbol = (SPSymbol *) object;
120         if (((SPObjectClass *) parent_class)->release)
121                 ((SPObjectClass *) parent_class)->release (object);
124 static void
125 sp_symbol_set (SPObject *object, unsigned int key, const gchar *value)
127         SPItem *item;
128         SPSymbol *symbol;
130         item = SP_ITEM (object);
131         symbol = SP_SYMBOL (object);
133         switch (key) {
134         case SP_ATTR_VIEWBOX:
135                 if (value) {
136                         double x, y, width, height;
137                         char *eptr;
138                         /* fixme: We have to take original item affine into account */
139                         /* fixme: Think (Lauris) */
140                         eptr = (gchar *) value;
141                         x = g_ascii_strtod (eptr, &eptr);
142                         while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
143                         y = g_ascii_strtod (eptr, &eptr);
144                         while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
145                         width = g_ascii_strtod (eptr, &eptr);
146                         while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
147                         height = g_ascii_strtod (eptr, &eptr);
148                         while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
149                         if ((width > 0) && (height > 0)) {
150                                 /* Set viewbox */
151                                 symbol->viewBox.x0 = x;
152                                 symbol->viewBox.y0 = y;
153                                 symbol->viewBox.x1 = x + width;
154                                 symbol->viewBox.y1 = y + height;
155                                 symbol->viewBox_set = TRUE;
156                         } else {
157                                 symbol->viewBox_set = FALSE;
158                         }
159                 } else {
160                         symbol->viewBox_set = FALSE;
161                 }
162                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
163                 break;
164         case SP_ATTR_PRESERVEASPECTRATIO:
165                 /* Do setup before, so we can use break to escape */
166                 symbol->aspect_set = FALSE;
167                 symbol->aspect_align = SP_ASPECT_NONE;
168                 symbol->aspect_clip = SP_ASPECT_MEET;
169                 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
170                 if (value) {
171                         int len;
172                         gchar c[256];
173                         const gchar *p, *e;
174                         unsigned int align, clip;
175                         p = value;
176                         while (*p && *p == 32) p += 1;
177                         if (!*p) break;
178                         e = p;
179                         while (*e && *e != 32) e += 1;
180                         len = e - p;
181                         if (len > 8) break;
182                         memcpy (c, value, len);
183                         c[len] = 0;
184                         /* Now the actual part */
185                         if (!strcmp (c, "none")) {
186                                 align = SP_ASPECT_NONE;
187                         } else if (!strcmp (c, "xMinYMin")) {
188                                 align = SP_ASPECT_XMIN_YMIN;
189                         } else if (!strcmp (c, "xMidYMin")) {
190                                 align = SP_ASPECT_XMID_YMIN;
191                         } else if (!strcmp (c, "xMaxYMin")) {
192                                 align = SP_ASPECT_XMAX_YMIN;
193                         } else if (!strcmp (c, "xMinYMid")) {
194                                 align = SP_ASPECT_XMIN_YMID;
195                         } else if (!strcmp (c, "xMidYMid")) {
196                                 align = SP_ASPECT_XMID_YMID;
197                         } else if (!strcmp (c, "xMaxYMin")) {
198                                 align = SP_ASPECT_XMAX_YMID;
199                         } else if (!strcmp (c, "xMinYMax")) {
200                                 align = SP_ASPECT_XMIN_YMAX;
201                         } else if (!strcmp (c, "xMidYMax")) {
202                                 align = SP_ASPECT_XMID_YMAX;
203                         } else if (!strcmp (c, "xMaxYMax")) {
204                                 align = SP_ASPECT_XMAX_YMAX;
205                         } else {
206                                 break;
207                         }
208                         clip = SP_ASPECT_MEET;
209                         while (*e && *e == 32) e += 1;
210                         if (e) {
211                                 if (!strcmp (e, "meet")) {
212                                         clip = SP_ASPECT_MEET;
213                                 } else if (!strcmp (e, "slice")) {
214                                         clip = SP_ASPECT_SLICE;
215                                 } else {
216                                         break;
217                                 }
218                         }
219                         symbol->aspect_set = TRUE;
220                         symbol->aspect_align = align;
221                         symbol->aspect_clip = clip;
222                 }
223                 break;
224         default:
225                 if (((SPObjectClass *) parent_class)->set)
226                         ((SPObjectClass *) parent_class)->set (object, key, value);
227                 break;
228         }
231 static void
232 sp_symbol_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
234         SPSymbol *symbol;
235         SPGroup *group;
237         symbol = (SPSymbol *) object;
238         group = (SPGroup *) object;
240         if (((SPObjectClass *) (parent_class))->child_added)
241                 ((SPObjectClass *) (parent_class))->child_added (object, child, ref);
244 static void
245 sp_symbol_update (SPObject *object, SPCtx *ctx, guint flags)
247         SPItem *item;
248         SPSymbol *symbol;
249         SPItemCtx *ictx, rctx;
250         SPItemView *v;
252         item = SP_ITEM (object);
253         symbol = SP_SYMBOL (object);
254         ictx = (SPItemCtx *) ctx;
256         if (SP_OBJECT_IS_CLONED (object)) {
257                 /* Cloned <symbol> is actually renderable */
259                 /* fixme: We have to set up clip here too */
261                 /* Create copy of item context */
262                 rctx = *ictx;
264                 /* Calculate child to parent transformation */
265                 /* Apply parent <use> translation (set up as vewport) */
266                 nr_matrix_set_translate (&symbol->c2p, rctx.vp.x0, rctx.vp.y0);
268                 if (symbol->viewBox_set) {
269                         double x, y, width, height;
270                         NRMatrix q;
271                         /* Determine actual viewbox in viewport coordinates */
272                         if (symbol->aspect_align == SP_ASPECT_NONE) {
273                                 x = 0.0;
274                                 y = 0.0;
275                                 width = rctx.vp.x1 - rctx.vp.x0;
276                                 height = rctx.vp.y1 - rctx.vp.y0;
277                         } else {
278                                 double scalex, scaley, scale;
279                                 /* Things are getting interesting */
280                                 scalex = (rctx.vp.x1 - rctx.vp.x0) / (symbol->viewBox.x1 - symbol->viewBox.x0);
281                                 scaley = (rctx.vp.y1 - rctx.vp.y0) / (symbol->viewBox.y1 - symbol->viewBox.y0);
282                                 scale = (symbol->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley);
283                                 width = (symbol->viewBox.x1 - symbol->viewBox.x0) * scale;
284                                 height = (symbol->viewBox.y1 - symbol->viewBox.y0) * scale;
285                                 /* Now place viewbox to requested position */
286                                 switch (symbol->aspect_align) {
287                                 case SP_ASPECT_XMIN_YMIN:
288                                         x = 0.0;
289                                         y = 0.0;
290                                         break;
291                                 case SP_ASPECT_XMID_YMIN:
292                                         x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
293                                         y = 0.0;
294                                         break;
295                                 case SP_ASPECT_XMAX_YMIN:
296                                         x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
297                                         y = 0.0;
298                                         break;
299                                 case SP_ASPECT_XMIN_YMID:
300                                         x = 0.0;
301                                         y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
302                                         break;
303                                 case SP_ASPECT_XMID_YMID:
304                                         x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
305                                         y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
306                                         break;
307                                 case SP_ASPECT_XMAX_YMID:
308                                         x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
309                                         y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
310                                         break;
311                                 case SP_ASPECT_XMIN_YMAX:
312                                         x = 0.0;
313                                         y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
314                                         break;
315                                 case SP_ASPECT_XMID_YMAX:
316                                         x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
317                                         y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
318                                         break;
319                                 case SP_ASPECT_XMAX_YMAX:
320                                         x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
321                                         y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
322                                         break;
323                                 default:
324                                         x = 0.0;
325                                         y = 0.0;
326                                         break;
327                                 }
328                         }
329                         /* Compose additional transformation from scale and position */
330                         q.c[0] = width / (symbol->viewBox.x1 - symbol->viewBox.x0);
331                         q.c[1] = 0.0;
332                         q.c[2] = 0.0;
333                         q.c[3] = height / (symbol->viewBox.y1 - symbol->viewBox.y0);
334                         q.c[4] = -symbol->viewBox.x0 * q.c[0] + x;
335                         q.c[5] = -symbol->viewBox.y0 * q.c[3] + y;
336                         /* Append viewbox transformation */
337                         nr_matrix_multiply (&symbol->c2p, &q, &symbol->c2p);
338                 }
340                 rctx.i2doc = symbol->c2p * rctx.i2doc;
342                 /* If viewBox is set initialize child viewport */
343                 /* Otherwise <use> has set it up already */
344                 if (symbol->viewBox_set) {
345                         rctx.vp.x0 = symbol->viewBox.x0;
346                         rctx.vp.y0 = symbol->viewBox.y0;
347                         rctx.vp.x1 = symbol->viewBox.x1;
348                         rctx.vp.y1 = symbol->viewBox.y1;
349                         rctx.i2vp = NR::identity();
350                 }
352                 /* And invoke parent method */
353                 if (((SPObjectClass *) (parent_class))->update)
354                         ((SPObjectClass *) (parent_class))->update (object, (SPCtx *) &rctx, flags);
356                 /* As last step set additional transform of arena group */
357                 for (v = item->display; v != NULL; v = v->next) {
358                         nr_arena_group_set_child_transform(NR_ARENA_GROUP(v->arenaitem), &symbol->c2p);
359                 }
360         } else {
361                 /* No-op */
362                 if (((SPObjectClass *) (parent_class))->update)
363                         ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
364         }
367 static void
368 sp_symbol_modified (SPObject *object, guint flags)
370         SPSymbol *symbol;
372         symbol = SP_SYMBOL (object);
374         if (((SPObjectClass *) (parent_class))->modified)
375                 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
378 static Inkscape::XML::Node *
379 sp_symbol_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
381         SPSymbol *symbol;
383         symbol = SP_SYMBOL (object);
385         if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
386                 repr = sp_repr_new ("svg:symbol");
387         }
389         repr->setAttribute("viewBox", object->repr->attribute("viewBox"));
390         repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
392         if (((SPObjectClass *) (parent_class))->write)
393                 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
395         return repr;
398 static NRArenaItem *
399 sp_symbol_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
401         SPSymbol *symbol;
402         NRArenaItem *ai;
404         symbol = SP_SYMBOL (item);
406         if (SP_OBJECT_IS_CLONED (symbol)) {
407                 /* Cloned <symbol> is actually renderable */
408                 if (((SPItemClass *) (parent_class))->show) {
409                         ai = ((SPItemClass *) (parent_class))->show (item, arena, key, flags);
410                         if (ai) {
411                                 nr_arena_group_set_child_transform(NR_ARENA_GROUP(ai), &symbol->c2p);
412                         }
413                 } else {
414                         ai = NULL;
415                 }
416         } else {
417                 ai = NULL;
418         }
420         return ai;
423 static void
424 sp_symbol_hide (SPItem *item, unsigned int key)
426         SPSymbol *symbol;
428         symbol = SP_SYMBOL (item);
430         if (SP_OBJECT_IS_CLONED (symbol)) {
431                 /* Cloned <symbol> is actually renderable */
432                 if (((SPItemClass *) (parent_class))->hide)
433                         ((SPItemClass *) (parent_class))->hide (item, key);
434         }
437 static void
438 sp_symbol_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
440         SPSymbol const *symbol = SP_SYMBOL(item);
442         if (SP_OBJECT_IS_CLONED (symbol)) {
443                 /* Cloned <symbol> is actually renderable */
445                 if (((SPItemClass *) (parent_class))->bbox) {
446                         NR::Matrix const a( symbol->c2p * transform );
447                         ((SPItemClass *) (parent_class))->bbox(item, bbox, a, flags);
448                 }
449         }
452 static void
453 sp_symbol_print (SPItem *item, SPPrintContext *ctx)
455         SPSymbol *symbol = SP_SYMBOL(item);
456         if (SP_OBJECT_IS_CLONED (symbol)) {
457                 /* Cloned <symbol> is actually renderable */
459                 sp_print_bind(ctx, &symbol->c2p, 1.0);
461                 if (((SPItemClass *) (parent_class))->print) {
462                         ((SPItemClass *) (parent_class))->print (item, ctx);
463                 }
465                 sp_print_release (ctx);
466         }