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