GUI: Move .ui files from goffice resources to glib resources
[gnumeric.git] / src / gnm-so-line.c
blob5e3b4b09cef934a8b538ea490a87dd1049fc2d03
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * gnm-so-line.c: Lines, arrows, arcs
6 * Copyright (C) 2004-2007 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
21 * USA
24 #include <gnumeric-config.h>
25 #include "gnumeric.h"
26 #include "gnm-so-line.h"
27 #include "sheet-object-impl.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>
34 #include <string.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_LINE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_SO_LINE_TYPE, GnmSOLine))
45 #define GNM_SO_LINE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SO_LINE_TYPE, GnmSOLineClass))
47 /*****************************************************************************/
49 typedef struct {
50 SheetObject base;
52 GOStyle *style;
53 GOArrow start_arrow, end_arrow;
54 } GnmSOLine;
55 typedef SheetObjectClass GnmSOLineClass;
57 static SheetObjectClass *gnm_so_line_parent_class;
59 #ifdef GNM_WITH_GTK
60 static void
61 so_line_view_set_bounds (SheetObjectView *sov, double const *coords, gboolean visible)
63 GocItem *view = GOC_ITEM (sov), *item = GOC_ITEM (GOC_GROUP (view)->children->data);
64 SheetObject *so = sheet_object_view_get_so (sov);
65 GOStyleLine const *style = &GNM_SO_LINE (so)->style->line;
66 double scale = goc_canvas_get_pixels_per_unit (view->canvas);
68 sheet_object_direction_set (so, coords);
70 if (visible &&
71 style->color != 0 && style->width >= 0 && style->dash_type != GO_LINE_NONE) {
72 goc_item_set (item,
73 "x0", coords[0] / scale,
74 "y0", coords[1] / scale,
75 "x1", coords[2] / scale,
76 "y1", coords[3] / scale,
77 NULL);
78 goc_item_show (view);
79 } else
80 goc_item_hide (view);
83 static void
84 so_line_goc_view_class_init (SheetObjectViewClass *sov_klass)
86 sov_klass->set_bounds = so_line_view_set_bounds;
88 typedef SheetObjectView LineGocView;
89 typedef SheetObjectViewClass LineGocViewClass;
90 static GSF_CLASS (LineGocView, so_line_goc_view,
91 so_line_goc_view_class_init, NULL,
92 GNM_SO_VIEW_TYPE)
94 #endif /* GNM_WITH_GTK */
95 enum {
96 SOL_PROP_0,
97 SOL_PROP_STYLE,
98 SOL_PROP_START_ARROW,
99 SOL_PROP_END_ARROW
102 static GOStyle *
103 sol_default_style (void)
105 GOStyle *res = go_style_new ();
106 res->interesting_fields = GO_STYLE_LINE;
107 res->line.width = 0; /* hairline */
108 res->line.color = GO_COLOR_BLACK;
109 res->line.dash_type = GO_LINE_SOLID; /* anything but 0 */
110 return res;
113 #ifdef GNM_WITH_GTK
114 #include <sheet-control-gui.h>
115 #include <dialogs/dialogs.h>
116 #include <gnumeric-simple-canvas.h>
117 #include <gnm-pane.h>
119 static void
120 gnm_so_line_user_config (SheetObject *so, SheetControl *sc)
122 dialog_so_styled (scg_wbcg (GNM_SCG (sc)), G_OBJECT (so),
123 sol_default_style (),
124 _("Line/Arrow Properties"), SO_STYLED_LINE);
127 static void
128 cb_gnm_so_line_changed (GnmSOLine const *sol,
129 G_GNUC_UNUSED GParamSpec *pspec,
130 GocItem *item)
132 item = GOC_ITEM (GOC_GROUP (item)->children->data);
133 goc_item_set (item,
134 "start-arrow", &sol->start_arrow,
135 "end-arrow", &sol->end_arrow,
136 "style", sol->style,
137 NULL);
140 static SheetObjectView *
141 gnm_so_line_new_view (SheetObject *so, SheetObjectViewContainer *container)
143 GnmSOLine const *sol = GNM_SO_LINE (so);
144 GocItem *item = goc_item_new (
145 gnm_pane_object_group (GNM_PANE (container)),
146 so_line_goc_view_get_type (),
147 NULL);
148 goc_item_new (GOC_GROUP (item), GOC_TYPE_LINE, NULL);
149 cb_gnm_so_line_changed (sol, NULL, item);
150 g_signal_connect_object (G_OBJECT (sol),
151 "notify", G_CALLBACK (cb_gnm_so_line_changed),
152 item, 0);
153 return gnm_pane_object_register (so, item, TRUE);
156 #endif /* GNM_WITH_GTK */
158 static void
159 draw_arrow (const GOArrow *arrow, cairo_t *cr,
160 double *x, double *y, double phi)
162 double dx, dy;
164 cairo_save (cr);
165 cairo_translate (cr, *x, *y);
166 go_arrow_draw (arrow, cr, &dx, &dy, phi);
167 *x += dx;
168 *y += dy;
169 cairo_restore (cr);
172 static void
173 gnm_so_line_draw_cairo (SheetObject const *so, cairo_t *cr,
174 double width, double height)
176 GnmSOLine *sol = GNM_SO_LINE (so);
177 GOStyle const *style = sol->style;
178 double x1, y1, x2, y2;
179 double phi;
181 if (style->line.color == 0 ||
182 style->line.width < 0 ||
183 style->line.dash_type == GO_LINE_NONE)
184 return;
186 if ((so->anchor.base.direction & GOD_ANCHOR_DIR_H_MASK) == GOD_ANCHOR_DIR_RIGHT) {
187 x1 = 0.;
188 x2 = width;
189 } else {
190 x1 = width;
191 x2 = 0.;
194 if ((so->anchor.base.direction & GOD_ANCHOR_DIR_V_MASK) == GOD_ANCHOR_DIR_DOWN) {
195 y1 = 0.;
196 y2 = height;
197 } else {
198 y1 = height;
199 y2 = 0.;
202 cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (style->line.color));
204 phi = atan2 (y2 - y1, x2 - x1) - M_PI_2;
205 draw_arrow (&sol->start_arrow, cr, &x1, &y1, phi + M_PI);
206 draw_arrow (&sol->end_arrow, cr, &x2, &y2, phi);
208 cairo_move_to (cr, x1, y1);
209 cairo_line_to (cr, x2, y2);
210 if (go_style_set_cairo_line (style, cr))
211 cairo_stroke (cr);
212 else
213 cairo_new_path (cr);
216 static void
217 write_xml_sax_arrow (GOArrow const *arrow, const char *prefix,
218 GsfXMLOut *output)
220 const char *typename = go_arrow_type_as_str (arrow->typ);
221 char *attr;
223 if (!typename || arrow->typ == GO_ARROW_NONE)
224 return;
226 attr = g_strconcat (prefix, "ArrowType", NULL);
227 gsf_xml_out_add_cstr (output, attr, typename);
228 g_free (attr);
230 attr = g_strconcat (prefix, "ArrowShapeA", NULL);
231 go_xml_out_add_double (output, attr, arrow->a);
232 g_free (attr);
234 attr = g_strconcat (prefix, "ArrowShapeB", NULL);
235 go_xml_out_add_double (output, attr, arrow->b);
236 g_free (attr);
238 attr = g_strconcat (prefix, "ArrowShapeC", NULL);
239 go_xml_out_add_double (output, attr, arrow->c);
240 g_free (attr);
243 static void
244 gnm_so_line_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
245 GnmConventions const *convs)
247 GnmSOLine const *sol = GNM_SO_LINE (so);
249 gsf_xml_out_add_int (output, "Type", 1);
250 write_xml_sax_arrow (&sol->start_arrow, "Start", output);
251 write_xml_sax_arrow (&sol->end_arrow, "End", output);
253 gsf_xml_out_start_element (output, "Style");
254 go_persist_sax_save (GO_PERSIST (sol->style), output);
255 gsf_xml_out_end_element (output); /* </Style> */
258 static void
259 sol_sax_style (GsfXMLIn *xin, xmlChar const **attrs)
261 SheetObject *so = gnm_xml_in_cur_obj (xin);
262 GnmSOLine *sol = GNM_SO_LINE (so);
263 go_persist_prep_sax (GO_PERSIST (sol->style), xin, attrs);
267 static gboolean
268 read_xml_sax_arrow (xmlChar const **attrs, const char *prefix,
269 GOArrow *arrow)
271 size_t plen = strlen (prefix);
272 const char *attr = CXML2C (attrs[0]);
273 const char *val = CXML2C (attrs[1]);
275 if (strncmp (attr, prefix, plen) != 0)
276 return FALSE;
277 attr += plen;
279 if (strcmp (attr, "ArrowType") == 0) {
280 arrow->typ = go_arrow_type_from_str (val);
281 } else if (strcmp (attr, "ArrowShapeA") == 0) {
282 arrow->a = go_strtod (val, NULL);
283 } else if (strcmp (attr, "ArrowShapeB") == 0) {
284 arrow->b = go_strtod (val, NULL);
285 } else if (strcmp (attr, "ArrowShapeC") == 0) {
286 arrow->c = go_strtod (val, NULL);
287 } else
288 return FALSE;
290 return TRUE;
293 static void
294 gnm_so_line_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
295 xmlChar const **attrs,
296 GnmConventions const *convs)
298 static GsfXMLInNode const dtd[] = {
299 GSF_XML_IN_NODE (STYLE, STYLE, -1, "Style", GSF_XML_NO_CONTENT, &sol_sax_style, NULL),
300 GSF_XML_IN_NODE_END
302 static GsfXMLInDoc *doc = NULL;
303 GnmSOLine *sol = GNM_SO_LINE (so);
304 gboolean old_format = FALSE;
305 double tmp, arrow_a = -1., arrow_b = -1., arrow_c = -1.;
306 int type = 0;
308 if (NULL == doc) {
309 doc = gsf_xml_in_doc_new (dtd, NULL);
310 gnm_xml_in_doc_dispose_on_exit (&doc);
312 gsf_xml_in_push_state (xin, doc, NULL, NULL, attrs);
314 go_arrow_clear (&sol->start_arrow);
315 go_arrow_clear (&sol->end_arrow);
317 for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
318 /* Old 1.0 and 1.2 */
319 if (gnm_xml_attr_double (attrs, "Width", &tmp)) {
320 old_format = TRUE;
321 sol->style->line.width = tmp;
322 } else if (attr_eq (attrs[0], "FillColor")) {
323 go_color_from_str (CXML2C (attrs[1]), &sol->style->line.color);
324 old_format = TRUE;
325 } else if (gnm_xml_attr_int (attrs, "Type", &type)) {
326 } else if (gnm_xml_attr_double (attrs, "ArrowShapeA", &arrow_a) ||
327 gnm_xml_attr_double (attrs, "ArrowShapeB", &arrow_b) ||
328 gnm_xml_attr_double (attrs, "ArrowShapeC", &arrow_c))
329 old_format = TRUE;
330 else if (read_xml_sax_arrow (attrs, "Start", &sol->start_arrow) ||
331 read_xml_sax_arrow (attrs, "End", &sol->end_arrow))
332 ; /* Nothing */
335 /* 2 == arrow */
336 if (old_format &&
337 type == 2 &&
338 arrow_a >= 0. && arrow_b >= 0. && arrow_c >= 0.)
339 go_arrow_init_kite (&sol->end_arrow,
340 arrow_a, arrow_b, arrow_c);
343 static void
344 gnm_so_line_copy (SheetObject *dst, SheetObject const *src)
346 GnmSOLine const *sol = GNM_SO_LINE (src);
347 GnmSOLine *new_sol = GNM_SO_LINE (dst);
349 g_object_unref (new_sol->style);
350 new_sol->style = go_style_dup (sol->style);
351 new_sol->start_arrow = sol->start_arrow;
352 new_sol->end_arrow = sol->end_arrow;
355 static void
356 gnm_so_line_set_property (GObject *obj, guint param_id,
357 GValue const *value, GParamSpec *pspec)
359 GnmSOLine *sol = GNM_SO_LINE (obj);
360 switch (param_id) {
361 case SOL_PROP_STYLE: {
362 GOStyle *style = go_style_dup (g_value_get_object (value));
363 style->interesting_fields = GO_STYLE_LINE;
364 g_object_unref (sol->style);
365 sol->style = style;
366 break;
368 case SOL_PROP_START_ARROW:
369 sol->start_arrow = *((GOArrow *)g_value_peek_pointer (value));
370 break;
371 case SOL_PROP_END_ARROW:
372 sol->end_arrow = *((GOArrow* )g_value_peek_pointer (value));
373 break;
374 default:
375 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
376 return;
380 static void
381 gnm_so_line_get_property (GObject *obj, guint param_id,
382 GValue *value, GParamSpec *pspec)
384 GnmSOLine *sol = GNM_SO_LINE (obj);
385 switch (param_id) {
386 case SOL_PROP_STYLE:
387 g_value_set_object (value, sol->style);
388 break;
389 case SOL_PROP_START_ARROW:
390 g_value_set_boxed (value, &sol->start_arrow);
391 break;
392 case SOL_PROP_END_ARROW:
393 g_value_set_boxed (value, &sol->end_arrow);
394 break;
395 default:
396 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
397 break;
401 static void
402 gnm_so_line_finalize (GObject *object)
404 GnmSOLine *sol = GNM_SO_LINE (object);
405 g_clear_object (&sol->style);
406 G_OBJECT_CLASS (gnm_so_line_parent_class)->finalize (object);
409 static void
410 gnm_so_line_class_init (GObjectClass *gobject_class)
412 SheetObjectClass *so_class = GNM_SO_CLASS (gobject_class);
414 gnm_so_line_parent_class = g_type_class_peek_parent (gobject_class);
416 gobject_class->finalize = gnm_so_line_finalize;
417 gobject_class->set_property = gnm_so_line_set_property;
418 gobject_class->get_property = gnm_so_line_get_property;
419 so_class->write_xml_sax = gnm_so_line_write_xml_sax;
420 so_class->prep_sax_parser = gnm_so_line_prep_sax_parser;
421 so_class->copy = gnm_so_line_copy;
422 so_class->rubber_band_directly = TRUE;
423 so_class->xml_export_name = "SheetObjectGraphic";
425 #ifdef GNM_WITH_GTK
426 so_class->draw_cairo = gnm_so_line_draw_cairo;
427 so_class->user_config = gnm_so_line_user_config;
428 so_class->new_view = gnm_so_line_new_view;
429 #endif /* GNM_WITH_GTK */
431 g_object_class_install_property (gobject_class, SOL_PROP_STYLE,
432 g_param_spec_object ("style", NULL, NULL, GO_TYPE_STYLE,
433 GSF_PARAM_STATIC | G_PARAM_READWRITE));
434 g_object_class_install_property (gobject_class, SOL_PROP_START_ARROW,
435 g_param_spec_boxed ("start-arrow", NULL, NULL,
436 GO_ARROW_TYPE,
437 GSF_PARAM_STATIC | G_PARAM_READWRITE));
438 g_object_class_install_property (gobject_class, SOL_PROP_END_ARROW,
439 g_param_spec_boxed ("end-arrow", NULL, NULL,
440 GO_ARROW_TYPE,
441 GSF_PARAM_STATIC | G_PARAM_READWRITE));
444 static void
445 gnm_so_line_init (GObject *obj)
447 GnmSOLine *sol = GNM_SO_LINE (obj);
448 sol->style = sol_default_style ();
449 go_arrow_clear (&sol->start_arrow);
450 go_arrow_clear (&sol->end_arrow);
451 GNM_SO (obj)->anchor.base.direction = GOD_ANCHOR_DIR_NONE_MASK;
454 GSF_CLASS (GnmSOLine, gnm_so_line,
455 gnm_so_line_class_init, gnm_so_line_init,
456 GNM_SO_TYPE)