1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * gnm-so-filled.c: Boxes, Ovals and Polygons
6 * Copyright (C) 2004-2006 Jody Goldberg (jody@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 #include <gnumeric-config.h>
26 #include "application.h"
27 #include "gnm-so-filled.h"
28 #include "sheet-object-impl.h"
33 #include <goffice/goffice.h>
34 #include <gsf/gsf-impl-utils.h>
35 #include <glib/gi18n-lib.h>
39 #define CXML2C(s) ((char const *)(s))
41 static inline gboolean
42 attr_eq (const xmlChar
*a
, const char *s
)
44 return !strcmp (CXML2C (a
), s
);
47 #define GNM_SO_FILLED(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_SO_FILLED_TYPE, GnmSOFilled))
48 #define GNM_SO_FILLED_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SO_FILLED_TYPE, GnmSOFilledClass))
57 PangoAttrList
*markup
;
59 double top
, bottom
, left
, right
;
62 typedef SheetObjectClass GnmSOFilledClass
;
65 #include <goffice/goffice.h>
73 so_filled_view_set_bounds (SheetObjectView
*sov
, double const *coords
, gboolean visible
)
75 GocItem
*view
= GOC_ITEM (sov
);
76 FilledItemView
*fiv
= (FilledItemView
*) sov
;
77 double scale
= goc_canvas_get_pixels_per_unit (view
->canvas
);
80 SheetObject
*so
= sheet_object_view_get_so (sov
);
81 GnmSOFilled
*sof
= GNM_SO_FILLED (so
);
82 double w
= fabs (coords
[2] - coords
[0]) / scale
;
83 double h
= fabs (coords
[3] - coords
[1]) / scale
;
86 "x", MIN (coords
[0], coords
[2]) / scale
,
87 "y", MIN (coords
[1], coords
[3]) / scale
,
90 goc_item_set (GOC_ITEM (fiv
->bg
),
91 "width", w
, "height", h
,
95 if (fiv
->text
!= NULL
&& GOC_IS_ITEM (fiv
->text
)) {
96 w
-= (sof
->margin_pts
.left
+ sof
->margin_pts
.right
)
100 h
-= (sof
->margin_pts
.top
+ sof
->margin_pts
.bottom
)
102 h
= MAX (h
, DBL_MIN
);
105 goc_item_set (GOC_ITEM (fiv
->text
),
110 goc_item_set (GOC_ITEM (fiv
->text
),
117 goc_item_show (view
);
119 goc_item_hide (view
);
123 so_filled_item_view_class_init (SheetObjectViewClass
*sov_klass
)
125 sov_klass
->set_bounds
= so_filled_view_set_bounds
;
128 typedef SheetObjectViewClass FilledItemViewClass
;
129 static GSF_CLASS (FilledItemView
, so_filled_item_view
,
130 so_filled_item_view_class_init
, NULL
,
133 #endif /* GNM_WITH_GTK */
135 /*****************************************************************************/
137 static SheetObjectClass
*gnm_so_filled_parent_class
;
149 sof_default_style (void)
151 GOStyle
*res
= go_style_new ();
152 res
->interesting_fields
= GO_STYLE_OUTLINE
| GO_STYLE_FILL
;
153 res
->line
.width
= 0; /* hairline */
154 res
->line
.color
= GO_COLOR_BLACK
;
155 res
->line
.dash_type
= GO_LINE_SOLID
; /* anything but 0 */
156 res
->fill
.type
= GO_STYLE_FILL_PATTERN
;
157 go_pattern_set_solid (&res
->fill
.pattern
, GO_COLOR_WHITE
);
161 /*****************************************************************************/
163 #include <sheet-control-gui.h>
164 #include <dialogs/dialogs.h>
165 #include <gnumeric-simple-canvas.h>
166 #include <gnm-pane.h>
167 #include <goffice/canvas/goc-rectangle.h>
168 #include <goffice/canvas/goc-ellipse.h>
169 #include <goffice/canvas/goc-text.h>
172 gnm_so_filled_user_config (SheetObject
*so
, SheetControl
*sc
)
174 dialog_so_styled (scg_wbcg (GNM_SCG (sc
)), G_OBJECT (so
),
175 sof_default_style (),
176 _("Filled Object Properties"),
181 cb_gnm_so_filled_style_changed (GocItem
*background
, GnmSOFilled
const *sof
)
183 GOStyle
const *style
= sof
->style
;
185 goc_item_set (background
, "style", style
, NULL
);
189 cb_gnm_so_filled_changed (GnmSOFilled
const *sof
,
190 G_GNUC_UNUSED GParamSpec
*pspec
,
191 FilledItemView
*group
)
193 cb_gnm_so_filled_style_changed (GOC_ITEM (group
->bg
), sof
);
195 if (sof
->text
!= NULL
) {
196 /* set a font, a very bad solution, but will do until we move to GOString */
197 PangoFontDescription
*desc
= pango_font_description_from_string ("Sans 10");
200 double scale
= goc_canvas_get_pixels_per_unit (GOC_ITEM (group
)->canvas
);
201 g_object_get (group
->bg
, "width", &w
, "height", &h
, NULL
);
202 w
-= (sof
->margin_pts
.left
+ sof
->margin_pts
.right
)
204 w
= MAX (w
, DBL_MIN
);
206 h
-= (sof
->margin_pts
.top
+ sof
->margin_pts
.bottom
)
208 h
= MAX (h
, DBL_MIN
);
209 if (group
->text
== NULL
) {
211 group
->text
= goc_item_new (GOC_GROUP (group
), GOC_TYPE_TEXT
,
212 "anchor", GO_ANCHOR_CENTER
,
216 "attributes", sof
->markup
,
219 group
->text
= goc_item_new (GOC_GROUP (group
), GOC_TYPE_TEXT
,
220 "anchor", GO_ANCHOR_NW
,
222 "x", sof
->margin_pts
.left
,
223 "y", sof
->margin_pts
.top
,
224 "attributes", sof
->markup
,
227 style
= go_styled_object_get_style (GO_STYLED_OBJECT (group
->text
));
228 go_style_set_font_desc (style
, desc
);
229 goc_item_set (group
->text
,
231 "attributes", sof
->markup
,
236 } else if (group
->text
!= NULL
) {
237 g_object_unref (group
->text
);
242 static SheetObjectView
*
243 gnm_so_filled_new_view (SheetObject
*so
, SheetObjectViewContainer
*container
)
245 GnmSOFilled
*sof
= GNM_SO_FILLED (so
);
246 FilledItemView
*group
= (FilledItemView
*) goc_item_new (
247 gnm_pane_object_group (GNM_PANE (container
)),
248 so_filled_item_view_get_type (),
251 group
->bg
= goc_item_new (GOC_GROUP (group
),
252 sof
->is_oval
? GOC_TYPE_ELLIPSE
: GOC_TYPE_RECTANGLE
,
255 cb_gnm_so_filled_changed (sof
, NULL
, group
);
256 g_signal_connect_object (sof
,
257 "notify", G_CALLBACK (cb_gnm_so_filled_changed
),
259 return gnm_pane_object_register (so
, GOC_ITEM (group
), TRUE
);
262 #endif /* GNM_WITH_GTK */
265 gnm_so_filled_draw_cairo (SheetObject
const *so
, cairo_t
*cr
,
266 double width
, double height
)
268 GnmSOFilled
*sof
= GNM_SO_FILLED (so
);
269 GOStyle
const *style
= sof
->style
;
274 cairo_scale (cr
, width
, height
);
275 cairo_arc (cr
, .5, .5, .5, 0., 2 * M_PI
);
278 cairo_move_to (cr
, 0., 0.);
279 cairo_line_to (cr
, width
, 0.);
280 cairo_line_to (cr
, width
, height
);
281 cairo_line_to (cr
, 0., height
);
282 cairo_line_to (cr
, 0., 0.);
283 cairo_close_path (cr
);
286 go_style_fill (style
, cr
, TRUE
);
288 if (go_style_set_cairo_line (style
, cr
))
293 if (sof
->text
!= NULL
&& *(sof
->text
) != '\0') {
294 PangoLayout
*pl
= pango_cairo_create_layout (cr
);
295 double const scale_h
= 72. / gnm_app_display_dpi_get (TRUE
);
296 double const scale_v
= 72. / gnm_app_display_dpi_get (FALSE
);
297 double pl_height
= (height
- sof
->margin_pts
.top
298 - sof
->margin_pts
.bottom
) * PANGO_SCALE
300 double pl_width
= (width
- sof
->margin_pts
.left
301 - sof
->margin_pts
.right
) * PANGO_SCALE
303 /* set a font, a very bad solution, but will do until we move to GOString */
304 PangoFontDescription
*desc
= pango_font_description_from_string ("Sans 10");
305 pango_layout_set_font_description (pl
, desc
);
306 pango_layout_set_text (pl
, sof
->text
, -1);
307 pango_layout_set_attributes (pl
, sof
->markup
);
308 pango_layout_set_width (pl
, pl_width
);
309 pango_layout_set_height (pl
, pl_height
);
313 pango_layout_get_extents (pl
, NULL
, &r
);
315 (width
- r
.width
/ PANGO_SCALE
* scale_h
) / 2.,
316 (height
- r
.height
/ PANGO_SCALE
* scale_v
) / 2.);
318 cairo_move_to (cr
, sof
->margin_pts
.left
,
319 sof
->margin_pts
.top
);
320 cairo_scale (cr
, scale_h
, scale_v
);
321 cairo_set_source_rgba (cr
, GO_COLOR_TO_CAIRO (style
->font
.color
));
322 pango_cairo_show_layout (cr
, pl
);
326 pango_font_description_free (desc
);
331 gnm_so_filled_write_xml_sax (SheetObject
const *so
, GsfXMLOut
*output
,
332 GnmConventions
const *convs
)
334 GnmSOFilled
const *sof
= GNM_SO_FILLED (so
);
335 GOStyle
const *style
= sof
->style
;
336 gsf_xml_out_add_int (output
, "Type", sof
->is_oval
? 102 : 101);
338 if (sof
->text
!= NULL
&& sof
->text
[0] != '\0') {
339 gsf_xml_out_add_cstr (output
, "Label", sof
->text
);
340 if (sof
->markup
!= NULL
) {
341 GOFormat
*fmt
= go_format_new_markup (sof
->markup
, TRUE
);
342 gsf_xml_out_add_cstr (output
, "LabelFormat",
343 go_format_as_XL (fmt
));
344 go_format_unref (fmt
);
348 gsf_xml_out_start_element (output
, "Style");
349 go_persist_sax_save (GO_PERSIST (style
), output
);
350 gsf_xml_out_end_element (output
); /* </Style> */
354 sof_sax_style (GsfXMLIn
*xin
, xmlChar
const **attrs
)
356 SheetObject
*so
= gnm_xml_in_cur_obj (xin
);
357 GnmSOFilled
*sof
= GNM_SO_FILLED (so
);
358 go_persist_prep_sax (GO_PERSIST (sof
->style
), xin
, attrs
);
362 gnm_so_filled_prep_sax_parser (SheetObject
*so
, GsfXMLIn
*xin
,
363 xmlChar
const **attrs
,
364 GnmConventions
const *convs
)
366 static GsfXMLInNode
const dtd
[] = {
367 GSF_XML_IN_NODE (STYLE
, STYLE
, -1, "Style", GSF_XML_NO_CONTENT
, &sof_sax_style
, NULL
),
370 static GsfXMLInDoc
*doc
= NULL
;
371 GnmSOFilled
*sof
= GNM_SO_FILLED (so
);
376 doc
= gsf_xml_in_doc_new (dtd
, NULL
);
377 gnm_xml_in_doc_dispose_on_exit (&doc
);
379 gsf_xml_in_push_state (xin
, doc
, NULL
, NULL
, attrs
);
381 for (; attrs
!= NULL
&& attrs
[0] && attrs
[1] ; attrs
+= 2)
382 if (attr_eq (attrs
[0], "Label"))
383 g_object_set (G_OBJECT (sof
), "text", attrs
[1], NULL
);
384 else if (attr_eq (attrs
[0], "LabelFormat")) {
385 GOFormat
* fmt
= go_format_new_from_XL (attrs
[1]);
386 if (go_format_is_markup (fmt
))
387 g_object_set (G_OBJECT (sof
),
388 "markup", go_format_get_markup (fmt
),
390 go_format_unref (fmt
);
391 } else if (gnm_xml_attr_int (attrs
, "Type", &type
))
392 sof
->is_oval
= (type
== 102);
394 /* Old 1.0 and 1.2 */
395 else if (gnm_xml_attr_double (attrs
, "Width", &tmp
))
396 sof
->style
->line
.width
= tmp
;
397 else if (attr_eq (attrs
[0], "OutlineColor"))
398 go_color_from_str (CXML2C (attrs
[1]), &sof
->style
->line
.color
);
399 else if (attr_eq (attrs
[0], "FillColor"))
400 go_color_from_str (CXML2C (attrs
[1]), &sof
->style
->fill
.pattern
.back
);
404 gnm_so_filled_copy (SheetObject
*dst
, SheetObject
const *src
)
406 GnmSOFilled
const *sof
= GNM_SO_FILLED (src
);
407 GnmSOFilled
*new_sof
= GNM_SO_FILLED (dst
);
409 g_object_unref (new_sof
->style
);
410 new_sof
->is_oval
= sof
->is_oval
;
411 new_sof
->style
= go_style_dup (sof
->style
);
412 new_sof
->text
= g_strdup (sof
->text
);
413 new_sof
->margin_pts
.top
= sof
->margin_pts
.top
;
414 new_sof
->margin_pts
.bottom
= sof
->margin_pts
.bottom
;
415 new_sof
->margin_pts
.left
= sof
->margin_pts
.left
;
416 new_sof
->margin_pts
.right
= sof
->margin_pts
.right
;
417 if (NULL
!= (new_sof
->markup
= sof
->markup
))
418 pango_attr_list_ref (new_sof
->markup
);
422 gnm_so_filled_set_property (GObject
*obj
, guint param_id
,
423 GValue
const *value
, GParamSpec
*pspec
)
425 GnmSOFilled
*sof
= GNM_SO_FILLED (obj
);
429 case SOF_PROP_STYLE
: {
430 GOStyle
*style
= go_style_dup (g_value_get_object (value
));
431 style
->interesting_fields
= GO_STYLE_OUTLINE
| GO_STYLE_FILL
;
432 g_object_unref (sof
->style
);
436 case SOF_PROP_IS_OVAL
:
437 sof
->is_oval
= g_value_get_boolean (value
);
441 str
= g_value_get_string (value
);
442 sof
->text
= g_strdup (str
== NULL
? "" : str
);
444 case SOF_PROP_MARKUP
:
445 if (sof
->markup
!= NULL
)
446 pango_attr_list_unref (sof
->markup
);
447 sof
->markup
= g_value_peek_pointer (value
);
448 if (sof
->markup
!= NULL
)
449 pango_attr_list_ref (sof
->markup
);
453 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
459 gnm_so_filled_get_property (GObject
*obj
, guint param_id
,
460 GValue
*value
, GParamSpec
*pspec
)
462 GnmSOFilled
*sof
= GNM_SO_FILLED (obj
);
465 g_value_set_object (value
, sof
->style
);
467 case SOF_PROP_IS_OVAL
:
468 g_value_set_boolean (value
, sof
->is_oval
);
471 g_value_set_string (value
, sof
->text
);
473 case SOF_PROP_MARKUP
:
474 g_value_set_boxed (value
, sof
->markup
);
476 case SOF_PROP_DOCUMENT
:
477 g_value_set_object (value
, sheet_object_get_sheet (GNM_SO (obj
))->workbook
);
480 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
486 gnm_so_filled_finalize (GObject
*object
)
488 GnmSOFilled
*sof
= GNM_SO_FILLED (object
);
490 g_clear_object (&sof
->style
);
495 if (NULL
!= sof
->markup
) {
496 pango_attr_list_unref (sof
->markup
);
500 G_OBJECT_CLASS (gnm_so_filled_parent_class
)->finalize (object
);
504 gnm_so_filled_class_init (GObjectClass
*gobject_class
)
506 SheetObjectClass
*so_class
= GNM_SO_CLASS (gobject_class
);
508 gnm_so_filled_parent_class
= g_type_class_peek_parent (gobject_class
);
510 gobject_class
->finalize
= gnm_so_filled_finalize
;
511 gobject_class
->set_property
= gnm_so_filled_set_property
;
512 gobject_class
->get_property
= gnm_so_filled_get_property
;
513 so_class
->write_xml_sax
= gnm_so_filled_write_xml_sax
;
514 so_class
->prep_sax_parser
= gnm_so_filled_prep_sax_parser
;
515 so_class
->copy
= gnm_so_filled_copy
;
516 so_class
->rubber_band_directly
= TRUE
;
517 so_class
->xml_export_name
= "SheetObjectFilled";
520 so_class
->new_view
= gnm_so_filled_new_view
;
521 so_class
->user_config
= gnm_so_filled_user_config
;
522 #endif /* GNM_WITH_GTK */
524 so_class
->draw_cairo
= gnm_so_filled_draw_cairo
;
526 g_object_class_install_property (gobject_class
, SOF_PROP_STYLE
,
527 g_param_spec_object ("style", NULL
, NULL
, GO_TYPE_STYLE
,
528 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
529 g_object_class_install_property (gobject_class
, SOF_PROP_IS_OVAL
,
530 g_param_spec_boolean ("is-oval", NULL
, NULL
, FALSE
,
531 GSF_PARAM_STATIC
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
532 g_object_class_install_property (gobject_class
, SOF_PROP_TEXT
,
533 g_param_spec_string ("text", NULL
, NULL
, NULL
,
534 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
535 g_object_class_install_property (gobject_class
, SOF_PROP_MARKUP
,
536 g_param_spec_boxed ("markup", NULL
, NULL
, PANGO_TYPE_ATTR_LIST
,
537 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
538 g_object_class_install_property (gobject_class
, SOF_PROP_DOCUMENT
,
539 g_param_spec_object ("document", NULL
, NULL
, GO_TYPE_DOC
,
540 GSF_PARAM_STATIC
| G_PARAM_READABLE
));
544 gnm_so_filled_init (GObject
*obj
)
546 GnmSOFilled
*sof
= GNM_SO_FILLED (obj
);
547 sof
->style
= sof_default_style ();
549 sof
->margin_pts
.top
= sof
->margin_pts
.bottom
= 3;
550 sof
->margin_pts
.left
= sof
->margin_pts
.right
= 5;
551 GNM_SO (obj
)->anchor
.base
.direction
= GOD_ANCHOR_DIR_NONE_MASK
;
554 GSF_CLASS (GnmSOFilled
, gnm_so_filled
,
555 gnm_so_filled_class_init
, gnm_so_filled_init
,