3 * gnm-so-filled.c: Boxes, Ovals and Polygons
5 * Copyright (C) 2004-2006 Jody Goldberg (jody@gnome.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
23 #include <gnumeric-config.h>
25 #include <application.h>
26 #include <gnm-so-filled.h>
27 #include <sheet-object-impl.h>
32 #include <goffice/goffice.h>
33 #include <gsf/gsf-impl-utils.h>
34 #include <glib/gi18n-lib.h>
38 #define CXML2C(s) ((char const *)(s))
40 static inline gboolean
41 attr_eq (const xmlChar
*a
, const char *s
)
43 return !strcmp (CXML2C (a
), s
);
46 #define GNM_SO_FILLED(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_SO_FILLED_TYPE, GnmSOFilled))
47 #define GNM_SO_FILLED_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SO_FILLED_TYPE, GnmSOFilledClass))
56 PangoAttrList
*markup
;
58 double top
, bottom
, left
, right
;
61 typedef SheetObjectClass GnmSOFilledClass
;
64 #include <goffice/goffice.h>
72 so_filled_view_set_bounds (SheetObjectView
*sov
, double const *coords
, gboolean visible
)
74 GocItem
*view
= GOC_ITEM (sov
);
75 FilledItemView
*fiv
= (FilledItemView
*) sov
;
76 double scale
= goc_canvas_get_pixels_per_unit (view
->canvas
);
79 SheetObject
*so
= sheet_object_view_get_so (sov
);
80 GnmSOFilled
*sof
= GNM_SO_FILLED (so
);
81 double w
= fabs (coords
[2] - coords
[0]) / scale
;
82 double h
= fabs (coords
[3] - coords
[1]) / scale
;
85 "x", MIN (coords
[0], coords
[2]) / scale
,
86 "y", MIN (coords
[1], coords
[3]) / scale
,
89 goc_item_set (GOC_ITEM (fiv
->bg
),
90 "width", w
, "height", h
,
94 if (fiv
->text
!= NULL
&& GOC_IS_ITEM (fiv
->text
)) {
95 w
-= (sof
->margin_pts
.left
+ sof
->margin_pts
.right
)
99 h
-= (sof
->margin_pts
.top
+ sof
->margin_pts
.bottom
)
101 h
= MAX (h
, DBL_MIN
);
104 goc_item_set (GOC_ITEM (fiv
->text
),
109 goc_item_set (GOC_ITEM (fiv
->text
),
116 goc_item_show (view
);
118 goc_item_hide (view
);
122 so_filled_item_view_class_init (SheetObjectViewClass
*sov_klass
)
124 sov_klass
->set_bounds
= so_filled_view_set_bounds
;
127 typedef SheetObjectViewClass FilledItemViewClass
;
128 static GSF_CLASS (FilledItemView
, so_filled_item_view
,
129 so_filled_item_view_class_init
, NULL
,
132 #endif /* GNM_WITH_GTK */
134 /*****************************************************************************/
136 static SheetObjectClass
*gnm_so_filled_parent_class
;
148 sof_default_style (void)
150 GOStyle
*res
= go_style_new ();
151 res
->interesting_fields
= GO_STYLE_OUTLINE
| GO_STYLE_FILL
;
152 res
->line
.width
= 0; /* hairline */
153 res
->line
.color
= GO_COLOR_BLACK
;
154 res
->line
.dash_type
= GO_LINE_SOLID
; /* anything but 0 */
155 res
->fill
.type
= GO_STYLE_FILL_PATTERN
;
156 go_pattern_set_solid (&res
->fill
.pattern
, GO_COLOR_WHITE
);
160 /*****************************************************************************/
162 #include <sheet-control-gui.h>
163 #include <dialogs/dialogs.h>
164 #include <gnumeric-simple-canvas.h>
165 #include <gnm-pane.h>
166 #include <goffice/canvas/goc-rectangle.h>
167 #include <goffice/canvas/goc-ellipse.h>
168 #include <goffice/canvas/goc-text.h>
171 gnm_so_filled_user_config (SheetObject
*so
, SheetControl
*sc
)
173 dialog_so_styled (scg_wbcg (GNM_SCG (sc
)), G_OBJECT (so
),
174 sof_default_style (),
175 _("Filled Object Properties"),
180 cb_gnm_so_filled_style_changed (GocItem
*background
, GnmSOFilled
const *sof
)
182 GOStyle
const *style
= sof
->style
;
184 goc_item_set (background
, "style", style
, NULL
);
188 cb_gnm_so_filled_changed (GnmSOFilled
const *sof
,
189 G_GNUC_UNUSED GParamSpec
*pspec
,
190 FilledItemView
*group
)
192 cb_gnm_so_filled_style_changed (GOC_ITEM (group
->bg
), sof
);
194 if (sof
->text
!= NULL
) {
195 /* set a font, a very bad solution, but will do until we move to GOString */
196 PangoFontDescription
*desc
= pango_font_description_from_string ("Sans 10");
199 double scale
= goc_canvas_get_pixels_per_unit (GOC_ITEM (group
)->canvas
);
200 g_object_get (group
->bg
, "width", &w
, "height", &h
, NULL
);
201 w
-= (sof
->margin_pts
.left
+ sof
->margin_pts
.right
)
203 w
= MAX (w
, DBL_MIN
);
205 h
-= (sof
->margin_pts
.top
+ sof
->margin_pts
.bottom
)
207 h
= MAX (h
, DBL_MIN
);
208 if (group
->text
== NULL
) {
210 group
->text
= goc_item_new (GOC_GROUP (group
), GOC_TYPE_TEXT
,
211 "anchor", GO_ANCHOR_CENTER
,
215 "attributes", sof
->markup
,
218 group
->text
= goc_item_new (GOC_GROUP (group
), GOC_TYPE_TEXT
,
219 "anchor", GO_ANCHOR_NW
,
221 "x", sof
->margin_pts
.left
,
222 "y", sof
->margin_pts
.top
,
223 "attributes", sof
->markup
,
226 style
= go_styled_object_get_style (GO_STYLED_OBJECT (group
->text
));
227 go_style_set_font_desc (style
, desc
);
228 goc_item_set (group
->text
,
230 "attributes", sof
->markup
,
235 } else if (group
->text
!= NULL
) {
236 g_object_unref (group
->text
);
241 static SheetObjectView
*
242 gnm_so_filled_new_view (SheetObject
*so
, SheetObjectViewContainer
*container
)
244 GnmSOFilled
*sof
= GNM_SO_FILLED (so
);
245 FilledItemView
*group
= (FilledItemView
*) goc_item_new (
246 gnm_pane_object_group (GNM_PANE (container
)),
247 so_filled_item_view_get_type (),
250 group
->bg
= goc_item_new (GOC_GROUP (group
),
251 sof
->is_oval
? GOC_TYPE_ELLIPSE
: GOC_TYPE_RECTANGLE
,
254 cb_gnm_so_filled_changed (sof
, NULL
, group
);
255 g_signal_connect_object (sof
,
256 "notify", G_CALLBACK (cb_gnm_so_filled_changed
),
258 return gnm_pane_object_register (so
, GOC_ITEM (group
), TRUE
);
261 #endif /* GNM_WITH_GTK */
264 gnm_so_filled_draw_cairo (SheetObject
const *so
, cairo_t
*cr
,
265 double width
, double height
)
267 GnmSOFilled
*sof
= GNM_SO_FILLED (so
);
268 GOStyle
const *style
= sof
->style
;
273 cairo_scale (cr
, width
, height
);
274 cairo_arc (cr
, .5, .5, .5, 0., 2 * M_PI
);
277 cairo_move_to (cr
, 0., 0.);
278 cairo_line_to (cr
, width
, 0.);
279 cairo_line_to (cr
, width
, height
);
280 cairo_line_to (cr
, 0., height
);
281 cairo_line_to (cr
, 0., 0.);
282 cairo_close_path (cr
);
285 go_style_fill (style
, cr
, TRUE
);
287 if (go_style_set_cairo_line (style
, cr
))
292 if (sof
->text
!= NULL
&& *(sof
->text
) != '\0') {
293 PangoLayout
*pl
= pango_cairo_create_layout (cr
);
294 double const scale_h
= 72. / gnm_app_display_dpi_get (TRUE
);
295 double const scale_v
= 72. / gnm_app_display_dpi_get (FALSE
);
296 double pl_height
= (height
- sof
->margin_pts
.top
297 - sof
->margin_pts
.bottom
) * PANGO_SCALE
299 double pl_width
= (width
- sof
->margin_pts
.left
300 - sof
->margin_pts
.right
) * PANGO_SCALE
302 /* set a font, a very bad solution, but will do until we move to GOString */
303 PangoFontDescription
*desc
= pango_font_description_from_string ("Sans 10");
304 pango_layout_set_font_description (pl
, desc
);
305 pango_layout_set_text (pl
, sof
->text
, -1);
306 pango_layout_set_attributes (pl
, sof
->markup
);
307 pango_layout_set_width (pl
, pl_width
);
308 pango_layout_set_height (pl
, pl_height
);
312 pango_layout_get_extents (pl
, NULL
, &r
);
314 (width
- r
.width
/ PANGO_SCALE
* scale_h
) / 2.,
315 (height
- r
.height
/ PANGO_SCALE
* scale_v
) / 2.);
317 cairo_move_to (cr
, sof
->margin_pts
.left
,
318 sof
->margin_pts
.top
);
319 cairo_scale (cr
, scale_h
, scale_v
);
320 cairo_set_source_rgba (cr
, GO_COLOR_TO_CAIRO (style
->font
.color
));
321 pango_cairo_show_layout (cr
, pl
);
325 pango_font_description_free (desc
);
330 gnm_so_filled_write_xml_sax (SheetObject
const *so
, GsfXMLOut
*output
,
331 GnmConventions
const *convs
)
333 GnmSOFilled
const *sof
= GNM_SO_FILLED (so
);
334 GOStyle
const *style
= sof
->style
;
335 gsf_xml_out_add_int (output
, "Type", sof
->is_oval
? 102 : 101);
337 if (sof
->text
!= NULL
&& sof
->text
[0] != '\0') {
338 gsf_xml_out_add_cstr (output
, "Label", sof
->text
);
339 if (sof
->markup
!= NULL
) {
340 GOFormat
*fmt
= go_format_new_markup (sof
->markup
, TRUE
);
341 gsf_xml_out_add_cstr (output
, "LabelFormat",
342 go_format_as_XL (fmt
));
343 go_format_unref (fmt
);
347 gsf_xml_out_start_element (output
, "Style");
348 go_persist_sax_save (GO_PERSIST (style
), output
);
349 gsf_xml_out_end_element (output
); /* </Style> */
353 sof_sax_style (GsfXMLIn
*xin
, xmlChar
const **attrs
)
355 SheetObject
*so
= gnm_xml_in_cur_obj (xin
);
356 GnmSOFilled
*sof
= GNM_SO_FILLED (so
);
357 go_persist_prep_sax (GO_PERSIST (sof
->style
), xin
, attrs
);
361 gnm_so_filled_prep_sax_parser (SheetObject
*so
, GsfXMLIn
*xin
,
362 xmlChar
const **attrs
,
363 GnmConventions
const *convs
)
365 static GsfXMLInNode
const dtd
[] = {
366 GSF_XML_IN_NODE (STYLE
, STYLE
, -1, "Style", GSF_XML_NO_CONTENT
, &sof_sax_style
, NULL
),
369 static GsfXMLInDoc
*doc
= NULL
;
370 GnmSOFilled
*sof
= GNM_SO_FILLED (so
);
375 doc
= gsf_xml_in_doc_new (dtd
, NULL
);
376 gnm_xml_in_doc_dispose_on_exit (&doc
);
378 gsf_xml_in_push_state (xin
, doc
, NULL
, NULL
, attrs
);
380 for (; attrs
!= NULL
&& attrs
[0] && attrs
[1] ; attrs
+= 2)
381 if (attr_eq (attrs
[0], "Label"))
382 g_object_set (G_OBJECT (sof
), "text", attrs
[1], NULL
);
383 else if (attr_eq (attrs
[0], "LabelFormat")) {
384 GOFormat
* fmt
= go_format_new_from_XL (attrs
[1]);
385 if (go_format_is_markup (fmt
))
386 g_object_set (G_OBJECT (sof
),
387 "markup", go_format_get_markup (fmt
),
389 go_format_unref (fmt
);
390 } else if (gnm_xml_attr_int (attrs
, "Type", &type
))
391 sof
->is_oval
= (type
== 102);
393 /* Old 1.0 and 1.2 */
394 else if (gnm_xml_attr_double (attrs
, "Width", &tmp
))
395 sof
->style
->line
.width
= tmp
;
396 else if (attr_eq (attrs
[0], "OutlineColor"))
397 go_color_from_str (CXML2C (attrs
[1]), &sof
->style
->line
.color
);
398 else if (attr_eq (attrs
[0], "FillColor"))
399 go_color_from_str (CXML2C (attrs
[1]), &sof
->style
->fill
.pattern
.back
);
403 gnm_so_filled_copy (SheetObject
*dst
, SheetObject
const *src
)
405 GnmSOFilled
const *sof
= GNM_SO_FILLED (src
);
406 GnmSOFilled
*new_sof
= GNM_SO_FILLED (dst
);
408 g_object_unref (new_sof
->style
);
409 new_sof
->is_oval
= sof
->is_oval
;
410 new_sof
->style
= go_style_dup (sof
->style
);
411 new_sof
->text
= g_strdup (sof
->text
);
412 new_sof
->margin_pts
.top
= sof
->margin_pts
.top
;
413 new_sof
->margin_pts
.bottom
= sof
->margin_pts
.bottom
;
414 new_sof
->margin_pts
.left
= sof
->margin_pts
.left
;
415 new_sof
->margin_pts
.right
= sof
->margin_pts
.right
;
416 if (NULL
!= (new_sof
->markup
= sof
->markup
))
417 pango_attr_list_ref (new_sof
->markup
);
421 gnm_so_filled_set_property (GObject
*obj
, guint param_id
,
422 GValue
const *value
, GParamSpec
*pspec
)
424 GnmSOFilled
*sof
= GNM_SO_FILLED (obj
);
428 case SOF_PROP_STYLE
: {
429 GOStyle
*style
= go_style_dup (g_value_get_object (value
));
430 style
->interesting_fields
= GO_STYLE_OUTLINE
| GO_STYLE_FILL
;
431 g_object_unref (sof
->style
);
435 case SOF_PROP_IS_OVAL
:
436 sof
->is_oval
= g_value_get_boolean (value
);
440 str
= g_value_get_string (value
);
441 sof
->text
= g_strdup (str
== NULL
? "" : str
);
443 case SOF_PROP_MARKUP
:
444 if (sof
->markup
!= NULL
)
445 pango_attr_list_unref (sof
->markup
);
446 sof
->markup
= g_value_peek_pointer (value
);
447 if (sof
->markup
!= NULL
)
448 pango_attr_list_ref (sof
->markup
);
452 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
458 gnm_so_filled_get_property (GObject
*obj
, guint param_id
,
459 GValue
*value
, GParamSpec
*pspec
)
461 GnmSOFilled
*sof
= GNM_SO_FILLED (obj
);
464 g_value_set_object (value
, sof
->style
);
466 case SOF_PROP_IS_OVAL
:
467 g_value_set_boolean (value
, sof
->is_oval
);
470 g_value_set_string (value
, sof
->text
);
472 case SOF_PROP_MARKUP
:
473 g_value_set_boxed (value
, sof
->markup
);
475 case SOF_PROP_DOCUMENT
:
476 g_value_set_object (value
, sheet_object_get_sheet (GNM_SO (obj
))->workbook
);
479 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
485 gnm_so_filled_finalize (GObject
*object
)
487 GnmSOFilled
*sof
= GNM_SO_FILLED (object
);
489 g_clear_object (&sof
->style
);
494 if (NULL
!= sof
->markup
) {
495 pango_attr_list_unref (sof
->markup
);
499 G_OBJECT_CLASS (gnm_so_filled_parent_class
)->finalize (object
);
503 gnm_so_filled_class_init (GObjectClass
*gobject_class
)
505 SheetObjectClass
*so_class
= GNM_SO_CLASS (gobject_class
);
507 gnm_so_filled_parent_class
= g_type_class_peek_parent (gobject_class
);
509 gobject_class
->finalize
= gnm_so_filled_finalize
;
510 gobject_class
->set_property
= gnm_so_filled_set_property
;
511 gobject_class
->get_property
= gnm_so_filled_get_property
;
512 so_class
->write_xml_sax
= gnm_so_filled_write_xml_sax
;
513 so_class
->prep_sax_parser
= gnm_so_filled_prep_sax_parser
;
514 so_class
->copy
= gnm_so_filled_copy
;
515 so_class
->rubber_band_directly
= TRUE
;
516 so_class
->xml_export_name
= "SheetObjectFilled";
519 so_class
->new_view
= gnm_so_filled_new_view
;
520 so_class
->user_config
= gnm_so_filled_user_config
;
521 #endif /* GNM_WITH_GTK */
523 so_class
->draw_cairo
= gnm_so_filled_draw_cairo
;
525 g_object_class_install_property (gobject_class
, SOF_PROP_STYLE
,
526 g_param_spec_object ("style", NULL
, NULL
, GO_TYPE_STYLE
,
527 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
528 g_object_class_install_property (gobject_class
, SOF_PROP_IS_OVAL
,
529 g_param_spec_boolean ("is-oval", NULL
, NULL
, FALSE
,
530 GSF_PARAM_STATIC
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
531 g_object_class_install_property (gobject_class
, SOF_PROP_TEXT
,
532 g_param_spec_string ("text", NULL
, NULL
, NULL
,
533 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
534 g_object_class_install_property (gobject_class
, SOF_PROP_MARKUP
,
535 g_param_spec_boxed ("markup", NULL
, NULL
, PANGO_TYPE_ATTR_LIST
,
536 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
537 g_object_class_install_property (gobject_class
, SOF_PROP_DOCUMENT
,
538 g_param_spec_object ("document", NULL
, NULL
, GO_TYPE_DOC
,
539 GSF_PARAM_STATIC
| G_PARAM_READABLE
));
543 gnm_so_filled_init (GObject
*obj
)
545 GnmSOFilled
*sof
= GNM_SO_FILLED (obj
);
546 sof
->style
= sof_default_style ();
548 sof
->margin_pts
.top
= sof
->margin_pts
.bottom
= 3;
549 sof
->margin_pts
.left
= sof
->margin_pts
.right
= 5;
550 GNM_SO (obj
)->anchor
.base
.direction
= GOD_ANCHOR_DIR_NONE_MASK
;
553 GSF_CLASS (GnmSOFilled
, gnm_so_filled
,
554 gnm_so_filled_class_init
, gnm_so_filled_init
,