Move plugin init code
[gnumeric.git] / src / gnm-so-filled.c
blob0a940487c23d61274b6717e0d96e622e9fb0f24a
2 /*
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
20 * USA
23 #include <gnumeric-config.h>
24 #include <gnumeric.h>
25 #include <application.h>
26 #include <gnm-so-filled.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>
35 #include <string.h>
36 #include <math.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))
49 typedef struct {
50 SheetObject base;
52 GOStyle *style;
53 gboolean is_oval;
55 char *text;
56 PangoAttrList *markup;
57 struct {
58 double top, bottom, left, right;
59 } margin_pts;
60 } GnmSOFilled;
61 typedef SheetObjectClass GnmSOFilledClass;
63 #ifdef GNM_WITH_GTK
64 #include <goffice/goffice.h>
66 typedef struct {
67 SheetObjectView base;
68 GocItem *bg, *text;
69 } FilledItemView;
71 static void
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);
78 if (visible) {
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;
84 goc_item_set (view,
85 "x", MIN (coords [0], coords [2]) / scale,
86 "y", MIN (coords [1], coords [3]) / scale,
87 NULL);
89 goc_item_set (GOC_ITEM (fiv->bg),
90 "width", w, "height", h,
91 NULL);
94 if (fiv->text != NULL && GOC_IS_ITEM (fiv->text)) {
95 w -= (sof->margin_pts.left + sof->margin_pts.right)
96 / scale;
97 w = MAX (w, DBL_MIN);
99 h -= (sof->margin_pts.top + sof->margin_pts.bottom)
100 / scale;
101 h = MAX (h, DBL_MIN);
103 if (sof->is_oval)
104 goc_item_set (GOC_ITEM (fiv->text),
105 "x", w / 2.,
106 "y", h / 2.,
107 NULL);
109 goc_item_set (GOC_ITEM (fiv->text),
110 "clip-height", h,
111 "clip-width", w,
112 "wrap-width", w,
113 NULL);
116 goc_item_show (view);
117 } else
118 goc_item_hide (view);
121 static void
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,
130 GNM_SO_VIEW_TYPE)
132 #endif /* GNM_WITH_GTK */
134 /*****************************************************************************/
136 static SheetObjectClass *gnm_so_filled_parent_class;
138 enum {
139 SOF_PROP_0,
140 SOF_PROP_STYLE,
141 SOF_PROP_IS_OVAL,
142 SOF_PROP_TEXT,
143 SOF_PROP_MARKUP,
144 SOF_PROP_DOCUMENT
147 static GOStyle *
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);
157 return res;
160 /*****************************************************************************/
161 #ifdef GNM_WITH_GTK
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>
170 static void
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"),
176 SO_STYLED_TEXT);
179 static void
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);
187 static void
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");
197 GOStyle *style;
198 double w, h;
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)
202 / scale;
203 w = MAX (w, DBL_MIN);
205 h -= (sof->margin_pts.top + sof->margin_pts.bottom)
206 / scale;
207 h = MAX (h, DBL_MIN);
208 if (group->text == NULL) {
209 if (sof->is_oval) {
210 group->text = goc_item_new (GOC_GROUP (group), GOC_TYPE_TEXT,
211 "anchor", GO_ANCHOR_CENTER,
212 "clip", TRUE,
213 "x", w / 2.,
214 "y", h / 2.,
215 "attributes", sof->markup,
216 NULL);
217 } else
218 group->text = goc_item_new (GOC_GROUP (group), GOC_TYPE_TEXT,
219 "anchor", GO_ANCHOR_NW,
220 "clip", TRUE,
221 "x", sof->margin_pts.left,
222 "y", sof->margin_pts.top,
223 "attributes", sof->markup,
224 NULL);
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,
229 "text", sof->text,
230 "attributes", sof->markup,
231 "clip-height", h,
232 "clip-width", w,
233 "wrap-width", w,
234 NULL);
235 } else if (group->text != NULL) {
236 g_object_unref (group->text);
237 group->text = NULL;
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 (),
248 NULL);
250 group->bg = goc_item_new (GOC_GROUP (group),
251 sof->is_oval ? GOC_TYPE_ELLIPSE : GOC_TYPE_RECTANGLE,
252 "x", 0., "y", 0.,
253 NULL);
254 cb_gnm_so_filled_changed (sof, NULL, group);
255 g_signal_connect_object (sof,
256 "notify", G_CALLBACK (cb_gnm_so_filled_changed),
257 group, 0);
258 return gnm_pane_object_register (so, GOC_ITEM (group), TRUE);
261 #endif /* GNM_WITH_GTK */
263 static void
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;
270 cairo_new_path (cr);
271 if (sof->is_oval) {
272 cairo_save (cr);
273 cairo_scale (cr, width, height);
274 cairo_arc (cr, .5, .5, .5, 0., 2 * M_PI);
275 cairo_restore (cr);
276 } else {
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);
284 /* Fill the shape */
285 go_style_fill (style, cr, TRUE);
286 /* Draw the line */
287 if (go_style_set_cairo_line (style, cr))
288 cairo_stroke (cr);
289 else
290 cairo_new_path (cr);
291 /* Draw the text. */
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
298 / scale_v;
299 double pl_width = (width - sof->margin_pts.left
300 - sof->margin_pts.right) * PANGO_SCALE
301 / scale_h;
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);
309 cairo_save (cr);
310 if (sof->is_oval) {
311 PangoRectangle r;
312 pango_layout_get_extents (pl, NULL, &r);
313 cairo_move_to (cr,
314 (width - r.width / PANGO_SCALE * scale_h) / 2.,
315 (height - r.height / PANGO_SCALE * scale_v) / 2.);
316 } else
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);
322 cairo_new_path (cr);
323 cairo_restore (cr);
324 g_object_unref (pl);
325 pango_font_description_free (desc);
329 static void
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> */
352 static void
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);
360 static void
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),
367 GSF_XML_IN_NODE_END
369 static GsfXMLInDoc *doc = NULL;
370 GnmSOFilled *sof = GNM_SO_FILLED (so);
371 double tmp;
372 int type;
374 if (NULL == doc) {
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),
388 NULL);
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);
402 static void
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);
420 static void
421 gnm_so_filled_set_property (GObject *obj, guint param_id,
422 GValue const *value, GParamSpec *pspec)
424 GnmSOFilled *sof = GNM_SO_FILLED (obj);
425 char const * str;
427 switch (param_id) {
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);
432 sof->style = style;
433 break;
435 case SOF_PROP_IS_OVAL:
436 sof->is_oval = g_value_get_boolean (value);
437 break;
438 case SOF_PROP_TEXT:
439 g_free (sof->text);
440 str = g_value_get_string (value);
441 sof->text = g_strdup (str == NULL ? "" : str);
442 break;
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);
449 break;
451 default:
452 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
453 return;
457 static void
458 gnm_so_filled_get_property (GObject *obj, guint param_id,
459 GValue *value, GParamSpec *pspec)
461 GnmSOFilled *sof = GNM_SO_FILLED (obj);
462 switch (param_id) {
463 case SOF_PROP_STYLE:
464 g_value_set_object (value, sof->style);
465 break;
466 case SOF_PROP_IS_OVAL:
467 g_value_set_boolean (value, sof->is_oval);
468 break;
469 case SOF_PROP_TEXT:
470 g_value_set_string (value, sof->text);
471 break;
472 case SOF_PROP_MARKUP:
473 g_value_set_boxed (value, sof->markup);
474 break;
475 case SOF_PROP_DOCUMENT:
476 g_value_set_object (value, sheet_object_get_sheet (GNM_SO (obj))->workbook);
477 break;
478 default:
479 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
480 break;
484 static void
485 gnm_so_filled_finalize (GObject *object)
487 GnmSOFilled *sof = GNM_SO_FILLED (object);
489 g_clear_object (&sof->style);
491 g_free (sof->text);
492 sof->text = NULL;
494 if (NULL != sof->markup) {
495 pango_attr_list_unref (sof->markup);
496 sof->markup = NULL;
499 G_OBJECT_CLASS (gnm_so_filled_parent_class)->finalize (object);
502 static void
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";
518 #ifdef GNM_WITH_GTK
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));
542 static void
543 gnm_so_filled_init (GObject *obj)
545 GnmSOFilled *sof = GNM_SO_FILLED (obj);
546 sof->style = sof_default_style ();
547 sof->markup = NULL;
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,
555 GNM_SO_TYPE)