Update Spanish translation
[gnumeric.git] / src / gnm-so-path.c
blobcf0e4407ddd1be49dc609d96936fd4bb4d96f2c7
1 /*
2 * gnm-so-path.c
4 * Copyright (C) 2012 Jean Bréfort <jean.brefort@normalesup.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
22 #include <gnumeric-config.h>
23 #include <gnumeric.h>
24 #include <application.h>
25 #include <gnm-so-path.h>
26 #include <sheet-object-impl.h>
27 #include <sheet.h>
28 #include <gutils.h>
29 #include <xml-sax.h>
31 #include <goffice/goffice.h>
32 #include <gsf/gsf-impl-utils.h>
33 #include <glib/gi18n-lib.h>
35 #define CXML2C(s) ((char const *)(s))
37 static inline gboolean
38 attr_eq (const xmlChar *a, const char *s)
40 return !strcmp (CXML2C (a), s);
43 #define GNM_SO_PATH(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GNM_SO_PATH_TYPE, GnmSOPath))
45 typedef struct {
46 SheetObject base;
47 GOStyle *style;
48 GOPath *path;
49 double x_offset, y_offset, width, height;
50 GPtrArray *paths, *styles;
52 char *text;
53 PangoAttrList *markup;
54 struct {
55 double top, bottom, left, right;
56 } margin_pts;
57 } GnmSOPath;
58 typedef SheetObjectClass GnmSOPathClass;
60 #ifdef GNM_WITH_GTK
61 #include <gnm-pane.h>
63 typedef struct {
64 SheetObjectView base;
65 GocItem *path, *text;
66 GPtrArray *paths;
67 } GnmSOPathView;
69 static void
70 so_path_view_set_bounds (SheetObjectView *sov, double const *coords, gboolean visible)
72 GnmSOPathView *spv = (GnmSOPathView *) sov;
74 if (visible) {
75 SheetObject *so = sheet_object_view_get_so (sov);
76 GnmSOPath const *sop = GNM_SO_PATH (so);
77 GOPath *path;
78 double scale, x_scale, y_scale, x, y;
79 if ((sop->path == NULL && sop->paths == NULL) || sop->width <=0. || sop->height <=0.)
80 return;
82 scale = goc_canvas_get_pixels_per_unit (GOC_ITEM (sov)->canvas);
83 x_scale = fabs (coords[2] - coords[0]) / sop->width / scale;
84 y_scale = fabs (coords[3] - coords[1]) / sop->height / scale;
85 x = MIN (coords[0], coords[2]) / scale - sop->x_offset * x_scale;
86 y = MIN (coords[1], coords[3]) / scale - sop->y_offset * y_scale;
88 if (sop->path != NULL) {
89 path = go_path_scale (sop->path, x_scale, y_scale);
90 goc_item_set (spv->path, "x", x, "y", y, "path", path, NULL);
91 go_path_free (path);
92 } else {
93 unsigned i;
94 for (i = 0; i < sop->paths->len; i++) {
95 path = go_path_scale ((GOPath *) g_ptr_array_index (sop->paths, i), x_scale, y_scale);
96 goc_item_set (GOC_ITEM (g_ptr_array_index (spv->paths, i)), "x", x, "y", y, "path", path, NULL);
97 go_path_free (path);
101 if (spv->text != NULL && GOC_ITEM (spv->text)) {
102 double x0, y0, x1, y1;
103 if (spv->path)
104 goc_item_get_bounds (spv->path, &x0, &y0, &x1, &y1);
105 else {
106 unsigned i;
107 double mx, my, Mx, My;
108 x0 = y0 = G_MAXDOUBLE;
109 x1 = y1 = -G_MAXDOUBLE;
110 for (i = 0; i < spv->paths->len; i++) {
111 goc_item_get_bounds (GOC_ITEM (g_ptr_array_index (spv->paths, i)), &mx, &my, &Mx, &My);
112 if (mx < x0)
113 x0 = mx;
114 if (my < y0)
115 y0 = my;
116 if (Mx > x1)
117 x1 = Mx;
118 if (My > y1)
119 y1 = My;
122 x1 -= x0 + sop->margin_pts.left + sop->margin_pts.right;
123 y1 -= y0 + sop->margin_pts.top + sop->margin_pts.bottom;
124 x0 += x1 / 2. + sop->margin_pts.left;
125 y0 += y1 / 2. + sop->margin_pts.top;
126 x1 = MAX (x1, DBL_MIN);
127 y1 = MAX (y1, DBL_MIN);
129 goc_item_set (GOC_ITEM (spv->text),
130 "x", x0,
131 "y", y0,
132 "clip-height", y1,
133 "clip-width", x1,
134 "wrap-width", x1,
135 NULL);
137 } else
138 goc_item_hide (GOC_ITEM (sov));
141 static void
142 so_path_goc_view_class_init (SheetObjectViewClass *sov_klass)
144 sov_klass->set_bounds = so_path_view_set_bounds;
147 typedef SheetObjectViewClass GnmSOPathViewClass;
148 static GSF_CLASS (GnmSOPathView, so_path_goc_view,
149 so_path_goc_view_class_init, NULL,
150 GNM_SO_VIEW_TYPE)
152 #endif
154 /*****************************************************************************/
156 static SheetObjectClass *gnm_so_path_parent_class;
157 enum {
158 SOP_PROP_0,
159 SOP_PROP_STYLE,
160 SOP_PROP_PATH,
161 SOP_PROP_TEXT,
162 SOP_PROP_MARKUP,
163 SOP_PROP_PATHS,
164 SOP_PROP_VIEWBOX
168 static GOStyle *
169 sop_default_style (void)
171 GOStyle *res = go_style_new ();
172 res->interesting_fields = GO_STYLE_OUTLINE | GO_STYLE_FILL;
173 res->line.width = 0; /* hairline */
174 res->line.color = GO_COLOR_BLACK;
175 res->line.dash_type = GO_LINE_SOLID; /* anything but 0 */
176 res->line.join = CAIRO_LINE_JOIN_ROUND;
177 res->fill.type = GO_STYLE_FILL_PATTERN;
178 go_pattern_set_solid (&res->fill.pattern, GO_COLOR_WHITE);
179 return res;
182 #ifdef GNM_WITH_GTK
183 #include <sheet-control-gui.h>
184 #include <dialogs/dialogs.h>
186 static void
187 cb_gnm_so_path_style_changed (GocItem *item, GnmSOPath const *sop)
189 GOStyle const *style = sop->style;
190 goc_item_set (item, "style", style, NULL);
193 static void
194 gnm_so_path_user_config (SheetObject *so, SheetControl *sc)
196 GnmSOPath *sop = GNM_SO_PATH (so);
197 dialog_so_styled (scg_wbcg (GNM_SCG (sc)), G_OBJECT (sop),
198 sop_default_style (),
199 _("Filled Object Properties"),
200 SO_STYLED_TEXT);
203 static void
204 cb_gnm_so_path_changed (GnmSOPath const *sop,
205 G_GNUC_UNUSED GParamSpec *pspec,
206 GnmSOPathView *group)
208 GList *ptr = GOC_GROUP (group)->children;
209 for (; ptr && ptr->data; ptr = ptr->next)
210 if (GOC_IS_PATH (ptr->data))
211 cb_gnm_so_path_style_changed (GOC_ITEM (ptr->data), sop);
213 if (sop->text != NULL && *sop->text != 0) {
214 /* set a font, a very bad solution, but will do until we move to GOString */
215 PangoFontDescription *desc = pango_font_description_from_string ("Sans 10");
216 GOStyle *style;
217 if (group->text == NULL) {
218 double x0, y0, x1, y1;
219 if (group->path)
220 goc_item_get_bounds (group->path, &x0, &y0, &x1, &y1);
221 else {
222 unsigned i;
223 double mx, my, Mx, My;
224 x0 = y0 = G_MAXDOUBLE;
225 x1 = y1 = -G_MAXDOUBLE;
226 for (i = 0; i < group->paths->len; i++) {
227 goc_item_get_bounds (GOC_ITEM (g_ptr_array_index (group->paths, i)), &mx, &my, &Mx, &My);
228 if (mx < x0)
229 x0 = mx;
230 if (my < y0)
231 y0 = my;
232 if (Mx > x1)
233 x1 = Mx;
234 if (My > y1)
235 y1 = My;
238 x1 -= x0 + sop->margin_pts.left + sop->margin_pts.right;
239 y1 -= y0 + sop->margin_pts.top + sop->margin_pts.bottom;
240 x0 += x1 / 2. + sop->margin_pts.left;
241 y0 += y1 / 2. + sop->margin_pts.top;
242 x1 = MAX (x1, DBL_MIN);
243 y1 = MAX (y1, DBL_MIN);
244 group->text = goc_item_new (GOC_GROUP (group), GOC_TYPE_TEXT,
245 "anchor", GO_ANCHOR_CENTER,
246 "clip", TRUE,
247 "x", x0,
248 "y", y0,
249 "clip-height", y1,
250 "clip-width", x1,
251 "wrap-width", x1,
252 "attributes", sop->markup,
253 NULL);
255 style = go_styled_object_get_style (GO_STYLED_OBJECT (group->text));
256 go_style_set_font_desc (style, desc);
257 goc_item_set (group->text,
258 "text", sop->text,
259 "attributes", sop->markup,
260 NULL);
261 } else if (group->text != NULL) {
262 g_object_unref (group->text);
263 group->text = NULL;
267 static SheetObjectView *
268 gnm_so_path_new_view (SheetObject *so, SheetObjectViewContainer *container)
270 GnmSOPath *sop = GNM_SO_PATH (so);
271 GnmSOPathView *item;
272 /* FIXME: this is unsafe if the paths change after the view is created,
273 * but this can't occur for now */
274 unsigned i;
276 if (sop->path == NULL && sop->paths == NULL)
277 return NULL;
278 item = (GnmSOPathView *) goc_item_new (
279 gnm_pane_object_group (GNM_PANE (container)),
280 so_path_goc_view_get_type (),
281 NULL);
282 if (sop->path)
283 item->path = goc_item_new (GOC_GROUP (item),
284 GOC_TYPE_PATH,
285 "closed", TRUE,
286 "fill-rule", TRUE,
287 NULL);
288 else {
289 item->paths = g_ptr_array_sized_new (sop->paths->len);
290 g_ptr_array_set_free_func (item->paths, g_object_unref);
291 for (i = 0; i < sop->paths->len; i++)
292 g_ptr_array_add (item->paths,
293 goc_item_new (GOC_GROUP (item),
294 GOC_TYPE_PATH,
295 "closed", TRUE,
296 "fill-rule", TRUE,
297 NULL));
299 cb_gnm_so_path_changed (sop, NULL, item);
300 g_signal_connect_object (sop,
301 "notify::style", G_CALLBACK (cb_gnm_so_path_changed),
302 item, 0);
303 return gnm_pane_object_register (so, GOC_ITEM (item), TRUE);
306 #endif
308 static void
309 gnm_so_path_draw_cairo (SheetObject const *so, cairo_t *cr,
310 double width, double height)
312 GnmSOPath *sop = GNM_SO_PATH (so);
313 GOStyle const *style = sop->style;
315 cairo_new_path (cr);
316 cairo_save (cr);
317 cairo_move_to (cr, -sop->x_offset, -sop->y_offset);
318 cairo_scale (cr, width / sop->width, height / sop->height);
319 go_path_to_cairo (sop->path, GO_PATH_DIRECTION_FORWARD, cr);
320 cairo_restore (cr);
321 /* Fill the shape */
322 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD); /* might be an option */
323 go_style_fill (style, cr, TRUE);
324 /* Draw the line */
325 if (go_style_set_cairo_line (style, cr))
326 cairo_stroke (cr);
327 else
328 cairo_new_path (cr);
329 /* Draw the text. */
330 if (sop->text != NULL && *(sop->text) != '\0') {
331 PangoLayout *pl = pango_cairo_create_layout (cr);
332 double const scale_h = 72. / gnm_app_display_dpi_get (TRUE);
333 double const scale_v = 72. / gnm_app_display_dpi_get (FALSE);
334 double pl_height = (height - sop->margin_pts.top
335 - sop->margin_pts.bottom) * PANGO_SCALE
336 / scale_v;
337 double pl_width = (width - sop->margin_pts.left
338 - sop->margin_pts.right) * PANGO_SCALE
339 / scale_h;
340 /* set a font, a very bad solution, but will do until we move to GOString */
341 PangoFontDescription *desc = pango_font_description_from_string ("Sans 10");
342 PangoRectangle r;
343 pango_layout_set_font_description (pl, desc);
344 pango_layout_set_text (pl, sop->text, -1);
345 pango_layout_set_attributes (pl, sop->markup);
346 pango_layout_set_width (pl, pl_width);
347 pango_layout_set_height (pl, pl_height);
348 cairo_save (cr);
349 pango_layout_get_extents (pl, NULL, &r);
350 cairo_move_to (cr,
351 (width - r.width / PANGO_SCALE * scale_h) / 2.,
352 (height - r.height / PANGO_SCALE * scale_v) / 2.);
353 cairo_scale (cr, scale_h, scale_v);
354 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (style->font.color));
355 pango_cairo_show_layout (cr, pl);
356 cairo_new_path (cr);
357 cairo_restore (cr);
358 g_object_unref (pl);
362 static void
363 gnm_so_path_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
364 G_GNUC_UNUSED GnmConventions const *convs)
366 GnmSOPath const *sop = GNM_SO_PATH (so);
367 char *svg;
369 if (sop->text != NULL && *(sop->text) != '\0') {
370 gsf_xml_out_add_cstr (output, "Label", sop->text);
371 if (sop->markup != NULL) {
372 GOFormat *fmt = go_format_new_markup (sop->markup, TRUE);
373 gsf_xml_out_add_cstr (output, "LabelFormat",
374 go_format_as_XL (fmt));
375 go_format_unref (fmt);
378 if (sop->path) {
379 svg = go_path_to_svg (sop->path);
380 gsf_xml_out_add_cstr (output, "Path", svg);
381 g_free (svg);
382 } else if (sop->paths) {
383 unsigned i;
384 for (i = 0; i < sop->paths->len; i++) {
385 gsf_xml_out_start_element (output, "Path");
386 svg = go_path_to_svg ((GOPath *) g_ptr_array_index (sop->paths, i));
387 gsf_xml_out_add_cstr (output, "Path", svg);
388 g_free (svg);
389 gsf_xml_out_end_element (output); /* </Path> */
393 gsf_xml_out_start_element (output, "Style");
394 go_persist_sax_save (GO_PERSIST (sop->style), output);
395 gsf_xml_out_end_element (output); /* </Style> */
398 static void
399 sop_sax_path (GsfXMLIn *xin, xmlChar const **attrs)
401 SheetObject *so = gnm_xml_in_cur_obj (xin);
402 GnmSOPath *sop = GNM_SO_PATH (so);
403 GOPath *path;
404 g_return_if_fail (sop->path == NULL);
405 if (sop->paths == NULL)
406 sop->paths = g_ptr_array_new_with_free_func ((GDestroyNotify) go_path_free);
407 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
408 if (attr_eq (attrs[0], "Path")) {
409 path = go_path_new_from_svg (attrs[1]);
410 if (path)
411 g_ptr_array_add (sop->paths, path);
412 /* we need to update the extents, not optimal */
413 g_ptr_array_ref (sop->paths);
414 g_object_set (G_OBJECT (sop), "paths", sop->paths, NULL);
415 g_ptr_array_unref (sop->paths);
416 return;
420 static void
421 sop_sax_style (GsfXMLIn *xin, xmlChar const **attrs)
423 SheetObject *so = gnm_xml_in_cur_obj (xin);
424 GnmSOPath *sop = GNM_SO_PATH (so);
425 go_persist_prep_sax (GO_PERSIST (sop->style), xin, attrs);
428 static void
429 gnm_so_path_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
430 xmlChar const **attrs,
431 G_GNUC_UNUSED GnmConventions const *convs)
433 static GsfXMLInNode const dtd[] = {
434 GSF_XML_IN_NODE (SOPATH, SOPATH, -1, "SheetObjectPath", GSF_XML_NO_CONTENT, NULL, NULL),
435 GSF_XML_IN_NODE (SOPATH, PATH, -1, "Path", GSF_XML_NO_CONTENT, &sop_sax_path, NULL),
436 GSF_XML_IN_NODE (SOPATH, STYLE, -1, "Style", GSF_XML_NO_CONTENT, &sop_sax_style, NULL),
437 GSF_XML_IN_NODE_END
439 static GsfXMLInDoc *doc = NULL;
440 GnmSOPath *sop = GNM_SO_PATH(so);
442 if (NULL == doc) {
443 doc = gsf_xml_in_doc_new (dtd, NULL);
444 gnm_xml_in_doc_dispose_on_exit (&doc);
446 gsf_xml_in_push_state (xin, doc, NULL, NULL, attrs);
448 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
449 if (attr_eq (attrs[0], "Label"))
450 g_object_set (G_OBJECT (sop), "text", attrs[1], NULL);
451 else if (attr_eq (attrs[0], "LabelFormat")) {
452 GOFormat * fmt = go_format_new_from_XL (attrs[1]);
453 if (go_format_is_markup (fmt))
454 g_object_set (G_OBJECT (sop),
455 "markup", go_format_get_markup (fmt),
456 NULL);
457 go_format_unref (fmt);
458 } else if (attr_eq (attrs[0], "Path")) {
459 GOPath *path = go_path_new_from_svg (attrs[1]);
460 if (path) {
461 g_object_set (G_OBJECT (sop), "path", path, NULL);
462 go_path_free (path);
467 static void
468 gnm_so_path_copy (SheetObject *dst, SheetObject const *src)
470 GnmSOPath const *sop = GNM_SO_PATH (src);
471 GnmSOPath *new_sop = GNM_SO_PATH (dst);
473 g_object_unref (new_sop->style);
474 new_sop->style = go_style_dup (sop->style);
475 new_sop->x_offset = sop->x_offset;
476 new_sop->y_offset = sop->y_offset;
477 new_sop->width = sop->width;
478 new_sop->height = sop->height;
479 if (new_sop->path) {
480 go_path_free (new_sop->path);
481 new_sop->path = NULL;
482 } else if (new_sop->paths) {
483 g_ptr_array_unref (new_sop->paths);
484 new_sop->paths = NULL;
486 if (sop->path)
487 new_sop->path = go_path_ref (sop->path);
488 else {
489 unsigned i;
490 new_sop->paths = g_ptr_array_new_full (sop->paths->len,
491 (GDestroyNotify) go_path_free);
492 for (i = 0; i < sop->paths->len; i++)
493 g_ptr_array_add (new_sop->paths, go_path_ref (g_ptr_array_index (sop->paths, i)));
495 gnm_so_path_parent_class->copy (dst, src);
498 static void
499 gnm_so_path_set_property (GObject *obj, guint param_id,
500 GValue const *value, GParamSpec *pspec)
502 GnmSOPath *sop = GNM_SO_PATH (obj);
504 switch (param_id) {
505 case SOP_PROP_STYLE: {
506 GOStyle *style = go_style_dup (g_value_get_object (value));
507 style->interesting_fields = GO_STYLE_OUTLINE | GO_STYLE_FILL;
508 g_object_unref (sop->style);
509 sop->style = style;
510 break;
512 case SOP_PROP_PATH: {
513 GOPath *path = g_value_get_boxed (value);
514 if (sop->path)
515 go_path_free (sop->path);
516 else if (sop->paths)
517 g_ptr_array_unref (sop->paths);
518 sop->path = NULL;
519 sop->paths = NULL;
520 if (path) {
521 cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
522 cairo_t *cr = cairo_create (surface);
524 sop->path = go_path_ref (path);
525 /* evaluates the bounding rectangle */
526 go_path_to_cairo (path, GO_PATH_DIRECTION_FORWARD, cr);
527 cairo_fill_extents (cr,
528 &sop->x_offset, &sop->y_offset,
529 &sop->width, &sop->height);
530 sop->width -= sop->x_offset;
531 sop->height -= sop->y_offset;
532 cairo_destroy (cr);
533 cairo_surface_destroy (surface);
535 break;
537 case SOP_PROP_PATHS: {
538 GPtrArray *paths = g_value_get_boxed (value);
539 unsigned i;
540 for (i = 0; i < paths->len; i++)
541 /* we can only check that the path is not NULL */
542 g_return_if_fail (g_ptr_array_index (paths, i) != NULL);
543 if (sop->path)
544 go_path_free (sop->path);
545 else if (sop->paths)
546 g_ptr_array_unref (sop->paths);
547 sop->path = NULL;
548 sop->paths = NULL;
549 if (paths) {
550 cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1);
551 cairo_t *cr = cairo_create (surface);
553 sop->paths = g_ptr_array_ref (paths);
554 /* evaluates the bounding rectangle */
555 for (i = 0; i < paths->len; i++)
556 go_path_to_cairo ((GOPath *) g_ptr_array_index (paths, i),
557 GO_PATH_DIRECTION_FORWARD, cr);
558 cairo_fill_extents (cr,
559 &sop->x_offset, &sop->y_offset,
560 &sop->width, &sop->height);
561 sop->width -= sop->x_offset;
562 sop->height -= sop->y_offset;
563 cairo_destroy (cr);
564 cairo_surface_destroy (surface);
566 break;
568 case SOP_PROP_TEXT: {
569 char const *str = g_value_get_string (value);
570 g_free (sop->text);
571 sop->text = g_strdup (str == NULL ? "" : str);
572 break;
574 case SOP_PROP_MARKUP:
575 if (sop->markup != NULL)
576 pango_attr_list_unref (sop->markup);
577 sop->markup = g_value_peek_pointer (value);
578 if (sop->markup != NULL)
579 pango_attr_list_ref (sop->markup);
580 break;
582 case SOP_PROP_VIEWBOX:
583 /* not settable */
584 default:
585 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
586 return;
590 static void
591 gnm_so_path_get_property (GObject *obj, guint param_id,
592 GValue *value, GParamSpec *pspec)
594 GnmSOPath *sop = GNM_SO_PATH (obj);
595 switch (param_id) {
596 case SOP_PROP_STYLE:
597 g_value_set_object (value, sop->style);
598 break;
599 case SOP_PROP_PATH:
600 g_value_set_boxed (value, sop->path);
601 break;
602 case SOP_PROP_PATHS:
603 g_value_set_boxed (value, sop->paths);
604 break;
605 case SOP_PROP_TEXT:
606 g_value_set_string (value, sop->text);
607 break;
608 case SOP_PROP_MARKUP:
609 g_value_set_boxed (value, sop->markup);
610 break;
611 case SOP_PROP_VIEWBOX:
612 g_value_take_string
613 (value,
614 g_strdup_printf ("%0.0f %0.0f %0.0f %0.0f", sop->x_offset, sop->y_offset,
615 sop->width + sop->x_offset, sop->height + sop->y_offset));
616 break;
617 default:
618 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
619 break;
623 static void
624 gnm_so_path_finalize (GObject *object)
626 GnmSOPath *sop = GNM_SO_PATH (object);
628 if (sop->path != NULL)
629 go_path_free (sop->path);
630 sop->path = NULL;
631 if (sop->paths != NULL)
632 g_ptr_array_unref (sop->paths);
633 sop->paths = NULL;
634 g_object_unref (sop->style);
635 sop->style = NULL;
636 sop->paths = NULL;
637 g_free (sop->text);
638 sop->text = NULL;
639 if (NULL != sop->markup) {
640 pango_attr_list_unref (sop->markup);
641 sop->markup = NULL;
643 G_OBJECT_CLASS (gnm_so_path_parent_class)->finalize (object);
646 static void
647 gnm_so_path_class_init (GObjectClass *gobject_class)
649 SheetObjectClass *so_class = GNM_SO_CLASS (gobject_class);
651 gnm_so_path_parent_class = g_type_class_peek_parent (gobject_class);
653 gobject_class->finalize = gnm_so_path_finalize;
654 gobject_class->set_property = gnm_so_path_set_property;
655 gobject_class->get_property = gnm_so_path_get_property;
656 so_class->write_xml_sax = gnm_so_path_write_xml_sax;
657 so_class->prep_sax_parser = gnm_so_path_prep_sax_parser;
658 so_class->copy = gnm_so_path_copy;
659 so_class->rubber_band_directly = FALSE;
660 so_class->xml_export_name = "SheetObjectPath";
662 #ifdef GNM_WITH_GTK
663 so_class->new_view = gnm_so_path_new_view;
664 so_class->user_config = gnm_so_path_user_config;
665 #endif /* GNM_WITH_GTK */
666 so_class->draw_cairo = gnm_so_path_draw_cairo;
668 g_object_class_install_property (gobject_class, SOP_PROP_PATH,
669 g_param_spec_boxed ("path", NULL, NULL, GO_TYPE_PATH,
670 GSF_PARAM_STATIC | G_PARAM_READWRITE));
671 g_object_class_install_property (gobject_class, SOP_PROP_STYLE,
672 g_param_spec_object ("style", NULL, NULL, GO_TYPE_STYLE,
673 GSF_PARAM_STATIC | G_PARAM_READWRITE));
674 g_object_class_install_property (gobject_class, SOP_PROP_TEXT,
675 g_param_spec_string ("text", NULL, NULL, NULL,
676 GSF_PARAM_STATIC | G_PARAM_READWRITE));
677 g_object_class_install_property (gobject_class, SOP_PROP_MARKUP,
678 g_param_spec_boxed ("markup", NULL, NULL, PANGO_TYPE_ATTR_LIST,
679 GSF_PARAM_STATIC | G_PARAM_READWRITE));
680 g_object_class_install_property (gobject_class, SOP_PROP_PATHS,
681 g_param_spec_boxed ("paths", NULL, NULL, G_TYPE_PTR_ARRAY,
682 GSF_PARAM_STATIC | G_PARAM_READWRITE));
683 g_object_class_install_property (gobject_class, SOP_PROP_VIEWBOX,
684 g_param_spec_string ("viewbox", NULL, NULL, NULL,
685 GSF_PARAM_STATIC | G_PARAM_READABLE));
688 static void
689 gnm_so_path_init (GObject *obj)
691 GnmSOPath *sop = GNM_SO_PATH (obj);
692 sop->style = sop_default_style ();
695 GSF_CLASS (GnmSOPath, gnm_so_path,
696 gnm_so_path_class_init, gnm_so_path_init,
697 GNM_SO_TYPE)