1 /*
2  * gog-renderer.c :
3  *
4  * Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) version 3.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21 #include <goffice/goffice-config.h>
22 #include <goffice/goffice-debug.h>
23 #include <goffice/graph/gog-renderer.h>
24 
25 #include <gsf/gsf-impl-utils.h>
26 #include <gsf/gsf-output-stdio.h>
27 
28 #include <glib/gi18n-lib.h>
29 
30 #include <pango/pangocairo.h>
31 
32 #include <cairo.h>
33 #ifdef CAIRO_HAS_PDF_SURFACE
34 #include <cairo-pdf.h>
35 #endif
36 #ifdef CAIRO_HAS_PS_SURFACE
37 #include <cairo-ps.h>
38 #endif
39 #ifdef CAIRO_HAS_SVG_SURFACE
40 #include <cairo-svg.h>
41 #endif
42 
43 #include <math.h>
44 
45 /**
46  * SECTION: gog-renderer
47  * @short_description: Rendering
48  *
49  * Note that #GogGraph has a functions for export/rendering, so you do not always
50  * need to use a #GogRenderer directly.
51 */
52 
53 enum {
54 	RENDERER_PROP_0,
55 	RENDERER_PROP_MODEL,
56 	RENDERER_PROP_VIEW,
57 	RENDERER_PROP_ZOOM
58 };
59 enum {
60 	RENDERER_SIGNAL_REQUEST_UPDATE,
61 	RENDERER_SIGNAL_LAST
62 };
63 static gulong renderer_signals [RENDERER_SIGNAL_LAST] = { 0, };
64 
65 static GObjectClass *parent_klass;
66 
67 struct _GogRenderer {
68 	GObject	 base;
69 
70 	GogGraph *model;
71 	GogView	 *view;
72 	double	  scale, scale_x, scale_y;
73 
74 	GClosure *font_watcher;
75 	gboolean  needs_update;
76 
77 	GOStyle const *cur_style;
78 	GSList   *style_stack;
79 
80 	GOLineDashSequence 	*line_dash;
81 
82 	GOStyle 	*grip_style;
83 	GOStyle 	*selection_style;
84 
85 	int 		 w, h;
86 
87 	gboolean	 is_vector;
88 
89 	cairo_t		*cairo;
90 	cairo_surface_t *cairo_surface;
91 
92 	GdkPixbuf 	*pixbuf;
93 
94 	gboolean	 marker_as_surface;
95 	cairo_surface_t *marker_surface;
96 	double		 marker_offset;
97 	GOMarker	*marker;
98 };
99 
100 typedef struct {
101 	GObjectClass base;
102 
103 	/* Signals */
104 	void (*request_update) (GogRenderer *renderer);
105 } GogRendererClass;
106 
107 #define GOG_RENDERER_CLASS(k)	 (G_TYPE_CHECK_CLASS_CAST ((k), GOG_TYPE_RENDERER, GogRendererClass))
108 #define GOG_IS_RENDERER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GOG_TYPE_RENDERER))
109 
110 #define GRC_LINEAR_SCALE(w,scale) (go_sub_epsilon (w) <= 0.0 ? GOG_RENDERER_HAIRLINE_WIDTH_PTS : w) * scale
111 
112 static double
_grc_line_size(GogRenderer const * rend,double width,gboolean sharp)113 _grc_line_size (GogRenderer const *rend, double width, gboolean sharp)
114 {
115 	if (rend->is_vector)
116 		return GRC_LINEAR_SCALE (width, rend->scale);
117 
118 	if (go_sub_epsilon (width) <= 0.) /* cheesy version of hairline */
119 		return 1.;
120 
121 	width *= rend->scale;
122 	if (!sharp || width <= 1.)
123 		return width;
124 
125 	return go_fake_round (width);
126 }
127 
128 static double
_line_size(GogRenderer const * rend,double width,gboolean sharp)129 _line_size (GogRenderer const *rend, double width, gboolean sharp)
130 {
131 	double size = _grc_line_size (rend, width, sharp);
132 
133 	if (!rend->is_vector && sharp && size < 1.0)
134 		return ceil (size);
135 
136 	return size;
137 }
138 
139 static void
_update_dash(GogRenderer * rend)140 _update_dash (GogRenderer *rend)
141 {
142 	double size;
143 
144 	go_line_dash_sequence_free (rend->line_dash);
145 	rend->line_dash = NULL;
146 
147 	if (rend->cur_style == NULL)
148 		return;
149 
150 	size = _line_size (rend, rend->cur_style->line.width, FALSE);
151 	rend->line_dash = go_line_dash_get_sequence (rend->cur_style->line.dash_type, size);
152 }
153 
154 double
gog_renderer_line_size(GogRenderer const * rend,double width)155 gog_renderer_line_size (GogRenderer const *rend, double width)
156 {
157 	return _line_size (rend, width, TRUE);
158 }
159 
160 double
gog_renderer_pt2r_x(GogRenderer const * rend,double d)161 gog_renderer_pt2r_x (GogRenderer const *rend, double d)
162 {
163 	return d * rend->scale_x;
164 }
165 
166 double
gog_renderer_pt2r_y(GogRenderer const * rend,double d)167 gog_renderer_pt2r_y (GogRenderer const *rend, double d)
168 {
169 	return d * rend->scale_y;
170 }
171 
172 double
gog_renderer_pt2r(GogRenderer const * rend,double d)173 gog_renderer_pt2r (GogRenderer const *rend, double d)
174 {
175 	return d * rend->scale;
176 }
177 
178 /*****************************************************************************/
179 
180 static void
emit_line(GogRenderer * rend,gboolean preserve,GOPathOptions options)181 emit_line (GogRenderer *rend, gboolean preserve, GOPathOptions options)
182 {
183 	GOStyle const *style = rend->cur_style;
184 	cairo_t *cr = rend->cairo;
185 	double width;
186 
187 	if (!go_style_is_line_visible (style)) {
188 		if (!preserve)
189 			cairo_new_path (cr);
190 		return;
191 	}
192 
193 	width = _grc_line_size (rend, style->line.width, options & GO_PATH_OPTIONS_SNAP_WIDTH);
194 	cairo_set_line_width (cr, width);
195 	if (rend->line_dash != NULL)
196 		cairo_set_dash (cr,
197 				rend->line_dash->dash,
198 				rend->line_dash->n_dash,
199 				rend->line_dash->offset);
200 	else
201 		cairo_set_dash (cr, NULL, 0, 0);
202 
203 	cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (style->line.color));
204 	cairo_set_line_cap (cr, (width <= 2.0 && !rend->is_vector) ?
205 			    CAIRO_LINE_CAP_SQUARE : CAIRO_LINE_CAP_ROUND);
206 
207 	if (preserve)
208 		cairo_stroke_preserve (cr);
209 	else
210 		cairo_stroke (cr);
211 }
212 
213 static void
emit_fill(GogRenderer * rend,gboolean preserve)214 emit_fill (GogRenderer *rend, gboolean preserve)
215 {
216 	go_style_fill (rend->cur_style, rend->cairo, preserve);
217 }
218 
219 static void
path_raw_move_to(void * closure,GOPathPoint const * point)220 path_raw_move_to (void *closure, GOPathPoint const *point)
221 {
222 	cairo_move_to ((cairo_t *) closure, point->x, point->y);
223 }
224 
225 static void
path_raw_line_to(void * closure,GOPathPoint const * point)226 path_raw_line_to (void *closure, GOPathPoint const *point)
227 {
228 	cairo_line_to ((cairo_t *) closure, point->x, point->y);
229 }
230 
231 static void
path_snap_even_move_to(void * closure,GOPathPoint const * point)232 path_snap_even_move_to (void *closure, GOPathPoint const *point)
233 {
234 	cairo_move_to ((cairo_t *) closure,
235 		       floor (point->x + .5),
236 		       floor (point->y + .5));
237 }
238 
239 static void
path_snap_even_line_to(void * closure,GOPathPoint const * point)240 path_snap_even_line_to (void *closure, GOPathPoint const *point)
241 {
242 	cairo_line_to ((cairo_t *) closure,
243 		       floor (point->x + .5),
244 		       floor (point->y + .5));
245 }
246 
247 static void
path_snap_odd_move_to(void * closure,GOPathPoint const * point)248 path_snap_odd_move_to (void *closure, GOPathPoint const *point)
249 {
250 	cairo_move_to ((cairo_t *) closure,
251 		       floor (point->x) + .5,
252 		       floor (point->y) + .5);
253 }
254 
255 static void
path_snap_odd_line_to(void * closure,GOPathPoint const * point)256 path_snap_odd_line_to (void *closure, GOPathPoint const *point)
257 {
258 	cairo_line_to ((cairo_t *) closure,
259 		       floor (point->x) + .5,
260 		       floor (point->y) + .5);
261 }
262 
263 static void
path_curve_to(void * closure,GOPathPoint const * point0,GOPathPoint const * point1,GOPathPoint const * point2)264 path_curve_to (void *closure,
265 	       GOPathPoint const *point0,
266 	       GOPathPoint const *point1,
267 	       GOPathPoint const *point2)
268 {
269 	cairo_curve_to ((cairo_t *) closure,
270 			point0->x, point0->y,
271 			point1->x, point1->y,
272 			point2->x, point2->y);
273 }
274 
275 static void
path_close_path(void * closure)276 path_close_path (void *closure)
277 {
278 	cairo_close_path ((cairo_t *) closure);
279 }
280 
281 static void
path_interpret(GogRenderer * rend,GOPath const * path,double line_width)282 path_interpret (GogRenderer 	 *rend,
283 		GOPath const 	 *path,
284 		double 		  line_width)
285 {
286 	if (rend->is_vector
287 	    || (go_path_get_options (path) & GO_PATH_OPTIONS_SNAP_COORDINATES) == 0) {
288 		go_path_interpret (path, GO_PATH_DIRECTION_FORWARD,
289 				   path_raw_move_to,
290 				   path_raw_line_to,
291 				   path_curve_to,
292 				   path_close_path,
293 				   rend->cairo);
294 		return;
295 	}
296 
297 	if (((int) (go_fake_ceil (line_width)) % 2 == 0) && (line_width > 1.0 || line_width <= 0.0))
298 		go_path_interpret (path, GO_PATH_DIRECTION_FORWARD,
299 				   path_snap_even_move_to,
300 				   path_snap_even_line_to,
301 				   path_curve_to,
302 				   path_close_path,
303 				   rend->cairo);
304 	else
305 		go_path_interpret (path, GO_PATH_DIRECTION_FORWARD,
306 				   path_snap_odd_move_to,
307 				   path_snap_odd_line_to,
308 				   path_curve_to,
309 				   path_close_path,
310 				   rend->cairo);
311 }
312 
313 typedef struct {
314 	cairo_t	*cairo;
315 	gboolean first_point;
316 } FillPathData;
317 
318 static void
fill_path_line_to(void * closure,GOPathPoint const * point)319 fill_path_line_to (void *closure, GOPathPoint const *point)
320 {
321 	FillPathData *data = closure;
322 
323 	if (data->first_point) {
324 		cairo_move_to (data->cairo, point->x, point->y);
325 		data->first_point = FALSE;
326 	}
327 	else
328 		cairo_line_to (data->cairo, point->x, point->y);
329 }
330 
331 static void
fill_path_curve_to(void * closure,GOPathPoint const * point0,GOPathPoint const * point1,GOPathPoint const * point2)332 fill_path_curve_to (void *closure,
333 		    GOPathPoint const *point0,
334 		    GOPathPoint const *point1,
335 		    GOPathPoint const *point2)
336 {
337 	FillPathData *data = closure;
338 
339 	cairo_curve_to (data->cairo,
340 			point0->x, point0->y,
341 			point1->x, point1->y,
342 			point2->x, point2->y);
343 }
344 
345 static void
fill_path_close_path(void * closure)346 fill_path_close_path (void *closure)
347 {
348 }
349 
350 static void
fill_path_interpret(GogRenderer * rend,GOPath const * path_0,GOPath const * path_1)351 fill_path_interpret (GogRenderer  *rend,
352 		     GOPath const *path_0,
353 		     GOPath const *path_1)
354 {
355 	FillPathData data;
356 
357 	data.first_point = TRUE;
358 	data.cairo = rend->cairo;
359 
360 	go_path_interpret (path_0,
361 			   GO_PATH_DIRECTION_FORWARD,
362 			   fill_path_line_to,
363 			   fill_path_line_to,
364 			   fill_path_curve_to,
365 			   fill_path_close_path,
366 			   &data);
367 	if (path_1 != NULL)
368 		go_path_interpret (path_1,
369 				   GO_PATH_DIRECTION_BACKWARD,
370 				   fill_path_line_to,
371 				   fill_path_line_to,
372 				   fill_path_curve_to,
373 				   fill_path_close_path,
374 				   &data);
375 	cairo_close_path (rend->cairo);
376 }
377 
378 void
gog_renderer_stroke_serie(GogRenderer * renderer,GOPath const * path)379 gog_renderer_stroke_serie (GogRenderer *renderer,
380 			   GOPath const *path)
381 {
382 	GOStyle const *style;
383 	GOPathOptions line_options;
384 	double width;
385 
386 	g_return_if_fail (GOG_IS_RENDERER (renderer));
387 	g_return_if_fail (renderer->cur_style != NULL);
388 	g_return_if_fail (GO_IS_PATH (path));
389 
390         style = renderer->cur_style;
391 	line_options = go_path_get_options (path);
392 	width = _grc_line_size (renderer,
393 				style->line.width,
394 				line_options & GO_PATH_OPTIONS_SNAP_WIDTH);
395 
396 	if (go_style_is_line_visible (style)) {
397 		path_interpret (renderer, path, width);
398 		emit_line (renderer, FALSE, go_path_get_options (path));
399 	}
400 }
401 
402 void
gog_renderer_fill_serie(GogRenderer * renderer,GOPath const * path,GOPath const * close_path)403 gog_renderer_fill_serie (GogRenderer *renderer,
404 			 GOPath const *path,
405 			 GOPath const *close_path)
406 {
407 	GOStyle const *style;
408 
409 	g_return_if_fail (GOG_IS_RENDERER (renderer));
410 	g_return_if_fail (renderer->cur_style != NULL);
411 	g_return_if_fail (GO_IS_PATH (path));
412 
413 	style = renderer->cur_style;
414 
415 	if (go_style_is_fill_visible (style)) {
416 		fill_path_interpret (renderer, path, close_path);
417 		emit_fill (renderer, FALSE);
418 	}
419 }
420 
421 static void
_draw_shape(GogRenderer * renderer,GOPath const * path,gboolean fill,gboolean stroke)422 _draw_shape (GogRenderer *renderer, GOPath const *path, gboolean fill, gboolean stroke)
423 {
424 	GOStyle const *style;
425 	GOPathOptions line_options;
426 	double width;
427 
428 	g_return_if_fail (GOG_IS_RENDERER (renderer));
429 	g_return_if_fail (renderer->cur_style != NULL);
430 	g_return_if_fail (GO_IS_PATH (path));
431 
432 	style = renderer->cur_style;
433 
434 	line_options = go_path_get_options (path);
435 	width = stroke ? _grc_line_size (renderer, style->line.width,
436 					 line_options & GO_PATH_OPTIONS_SNAP_WIDTH) : 0;
437 
438 	path_interpret (renderer, path, width);
439 
440 	if (fill)
441 		emit_fill (renderer, stroke);
442 
443 	if (stroke)
444 		emit_line (renderer, FALSE, go_path_get_options (path));
445 }
446 
447 void
gog_renderer_draw_shape(GogRenderer * renderer,GOPath const * path)448 gog_renderer_draw_shape (GogRenderer *renderer,
449 			 GOPath const *path)
450 {
451 	_draw_shape (renderer, path, TRUE, TRUE);
452 }
453 
454 void
gog_renderer_stroke_shape(GogRenderer * renderer,GOPath const * path)455 gog_renderer_stroke_shape (GogRenderer *renderer, GOPath const *path)
456 {
457 	_draw_shape (renderer, path, FALSE, TRUE);
458 }
459 
460 void
gog_renderer_fill_shape(GogRenderer * renderer,GOPath const * path)461 gog_renderer_fill_shape (GogRenderer *renderer, GOPath const *path)
462 {
463 	_draw_shape (renderer, path, TRUE, FALSE);
464 }
465 
466 static void
_draw_rotated_shape(GogRenderer * renderer,GOPath const * path,gboolean fill,gboolean stroke,gboolean rotate_bg)467 _draw_rotated_shape (GogRenderer *renderer, GOPath const *path, gboolean fill, gboolean stroke, gboolean rotate_bg)
468 {
469 	GOStyle const *style;
470 	GOPathOptions line_options;
471 	double width;
472 
473 	g_return_if_fail (GOG_IS_RENDERER (renderer));
474 	g_return_if_fail (renderer->cur_style != NULL);
475 	g_return_if_fail (GO_IS_PATH (path));
476 
477         style = renderer->cur_style;
478 
479 	line_options = go_path_get_options (path);
480 	width = stroke ? _grc_line_size (renderer, style->line.width,
481 					 line_options & GO_PATH_OPTIONS_SNAP_WIDTH) : 0;
482 
483 	cairo_save (renderer->cairo);
484 	cairo_rotate (renderer->cairo, -renderer->cur_style->text_layout.angle *M_PI / 180.);
485 	path_interpret (renderer, path, width);
486 
487 	if (fill) {
488 		if (rotate_bg) {
489 			emit_fill (renderer, stroke);
490 			cairo_restore (renderer->cairo);
491 		} else {
492 			cairo_restore (renderer->cairo);
493 			emit_fill (renderer, stroke);
494 		}
495 	} else
496 		cairo_restore (renderer->cairo);
497 
498 	if (stroke)
499 		emit_line (renderer, FALSE, go_path_get_options (path));
500 }
501 
502 /*****************************************************************************/
503 
504 /**
505  * gog_renderer_push_clip:
506  * @rend: #GogRenderer
507  * @path: a #GOPath
508  *
509  * Defines the current clipping region.
510  **/
511 
512 void
gog_renderer_push_clip(GogRenderer * rend,GOPath const * path)513 gog_renderer_push_clip (GogRenderer *rend, GOPath const *path)
514 {
515 	g_return_if_fail (GOG_IS_RENDERER (rend));
516 	g_return_if_fail (GO_IS_PATH (path));
517 
518 	cairo_save (rend->cairo);
519 
520 	path_interpret (rend, path, 0);
521 
522 	cairo_clip (rend->cairo);
523 }
524 
525 /**
526  * gog_renderer_push_clip_rectangle:
527  * @rend: #GogRenderer
528  * @x: left coordinate
529  * @y: top coordinate
530  * @w: width of clipping rectangle
531  * @h: height of clipping rectangle
532  *
533  * Defines a rectangular clipping region. For efficient screen rendering,
534  * this function takes care to round the coordinates.
535  **/
536 
537 void
gog_renderer_push_clip_rectangle(GogRenderer * rend,double x,double y,double w,double h)538 gog_renderer_push_clip_rectangle (GogRenderer *rend, double x, double y, double w, double h)
539 {
540 	GOPath *path;
541 
542 	path = go_path_new ();
543 	if (rend->is_vector)
544 		go_path_rectangle (path, x, y, w, h);
545 	else {
546 		double xx = go_fake_floor (x);
547 		double yy = go_fake_floor (y);
548 		go_path_rectangle (path, xx, yy,
549 				   go_fake_ceil (x + w) - xx,
550 				   go_fake_ceil (y + h) - yy);
551 	}
552 	gog_renderer_push_clip (rend, path);
553 	go_path_free (path);
554 }
555 
556 /**
557  * gog_renderer_pop_clip:
558  * @rend: #GogRenderer
559  *
560  * End the current clipping.
561  **/
562 
563 void
gog_renderer_pop_clip(GogRenderer * rend)564 gog_renderer_pop_clip (GogRenderer *rend)
565 {
566 	g_return_if_fail (GOG_IS_RENDERER (rend));
567 
568 	cairo_restore (rend->cairo);
569 }
570 
571 static void
_draw_circle(GogRenderer * rend,double x,double y,double r,gboolean fill,gboolean stroke)572 _draw_circle (GogRenderer *rend, double x, double y, double r, gboolean fill, gboolean stroke)
573 {
574 	GOStyle const *style;
575 	GOPath *path;
576 	gboolean narrow = r < 1.5;
577 	double o, o_2;
578 
579 	g_return_if_fail (GOG_IS_RENDERER (rend));
580 	g_return_if_fail (GO_IS_STYLE (rend->cur_style));
581 
582 	style = rend->cur_style;
583 	narrow |= !go_style_is_outline_visible (style);
584 
585 	path = go_path_new ();
586 	go_path_set_options (path, GO_PATH_OPTIONS_SHARP);
587 
588 	if (!narrow) {
589 		o = gog_renderer_line_size (rend, style->line.width);
590 		o_2 = o / 2.;
591 	} else
592 		o = o_2 = 0.;
593 
594 	go_path_arc (path, x, y, r, r, 0, 2 * M_PI);
595 
596 	_draw_shape (rend, path, fill, stroke && !narrow);
597 
598 	go_path_free (path);
599 }
600 
601 void
gog_renderer_draw_circle(GogRenderer * rend,double x,double y,double r)602 gog_renderer_draw_circle (GogRenderer *rend, double x, double y, double r)
603 {
604 	_draw_circle (rend, x, y, r, TRUE, TRUE);
605 }
606 
607 void
gog_renderer_stroke_circle(GogRenderer * rend,double x,double y,double r)608 gog_renderer_stroke_circle (GogRenderer *rend, double x, double y, double r)
609 {
610 	_draw_circle (rend, x, y, r, FALSE, TRUE);
611 }
612 
613 void
gog_renderer_fill_circle(GogRenderer * rend,double x,double y,double r)614 gog_renderer_fill_circle (GogRenderer *rend, double x, double y, double r)
615 {
616 	_draw_circle (rend, x, y, r, TRUE, FALSE);
617 }
618 
619 /**
620  * gog_renderer_draw_rectangle:
621  * @rend: a #GogRenderer
622  * @rect: position and extent of rectangle
623  *
624  * A utility routine to build a closed rectangle vpath.
625  *
626  **/
627 
628 static void
_draw_rectangle(GogRenderer * rend,GogViewAllocation const * rect,gboolean fill,gboolean stroke)629 _draw_rectangle (GogRenderer *rend, GogViewAllocation const *rect, gboolean fill, gboolean stroke)
630 {
631 	GOStyle const *style;
632 	GOPath *path;
633 	gboolean narrow = (rect->w < 3.) || (rect->h < 3.);
634 	double o, o_2;
635 
636 	g_return_if_fail (GOG_IS_RENDERER (rend));
637 	g_return_if_fail (GO_IS_STYLE (rend->cur_style));
638 
639 	style = rend->cur_style;
640 	narrow |= !go_style_is_outline_visible (style);
641 
642 	path = go_path_new ();
643 	go_path_set_options (path, GO_PATH_OPTIONS_SHARP);
644 
645 	if (!narrow) {
646 		o = gog_renderer_line_size (rend, style->line.width);
647 		o_2 = o / 2.;
648 	} else
649 		o = o_2 = 0.;
650 
651 	go_path_rectangle (path, rect->x + o_2, rect->y + o_2, rect->w - o, rect->h - o);
652 
653 	_draw_shape (rend, path, fill, stroke && !narrow);
654 
655 	go_path_free (path);
656 }
657 
658 void
gog_renderer_draw_rectangle(GogRenderer * rend,GogViewAllocation const * rect)659 gog_renderer_draw_rectangle (GogRenderer *rend, GogViewAllocation const *rect)
660 {
661 	_draw_rectangle (rend, rect, TRUE, TRUE);
662 }
663 
664 void
gog_renderer_stroke_rectangle(GogRenderer * rend,GogViewAllocation const * rect)665 gog_renderer_stroke_rectangle (GogRenderer *rend, GogViewAllocation const *rect)
666 {
667 	_draw_rectangle (rend, rect, FALSE, TRUE);
668 }
669 
670 void
gog_renderer_fill_rectangle(GogRenderer * rend,GogViewAllocation const * rect)671 gog_renderer_fill_rectangle (GogRenderer *rend, GogViewAllocation const *rect)
672 {
673 	_draw_rectangle (rend, rect, TRUE, FALSE);
674 }
675 
676 static void
_draw_rotated_rectangle(GogRenderer * rend,GogViewAllocation const * rect,gboolean fill,gboolean stroke,gboolean rotate_bg)677 _draw_rotated_rectangle (GogRenderer *rend, GogViewAllocation const *rect, gboolean fill, gboolean stroke, gboolean rotate_bg)
678 {
679 	GOStyle const *style;
680 	GOPath *path;
681 	gboolean narrow = (rect->w < 3.) || (rect->h < 3.);
682 	double o, o_2;
683 
684 	g_return_if_fail (GOG_IS_RENDERER (rend));
685 	g_return_if_fail (GO_IS_STYLE (rend->cur_style));
686 
687 	style = rend->cur_style;
688 	narrow |= !go_style_is_outline_visible (style);
689 
690 	path = go_path_new ();
691 	go_path_set_options (path, GO_PATH_OPTIONS_SHARP);
692 
693 	if (!narrow) {
694 		o = gog_renderer_line_size (rend, style->line.width);
695 		o_2 = o / 2.;
696 	} else
697 		o = o_2 = 0.;
698 
699 	go_path_rectangle (path, 0., 0., rect->w - o, rect->h - o);
700 
701 	cairo_save (rend->cairo);
702 	cairo_translate (rend->cairo, rect->x - o_2, rect->y - o_2);
703 	_draw_rotated_shape (rend, path, fill, stroke && !narrow, rotate_bg);
704 	cairo_restore (rend->cairo);
705 
706 	go_path_free (path);
707 }
708 
709 void
gog_renderer_draw_rotated_rectangle(GogRenderer * rend,GogViewAllocation const * rect,gboolean rotate_bg)710 gog_renderer_draw_rotated_rectangle (GogRenderer *rend, GogViewAllocation const *rect, gboolean rotate_bg)
711 {
712 	_draw_rotated_rectangle (rend, rect, TRUE, TRUE, rotate_bg);
713 }
714 
715 /**
716  * gog_renderer_draw_grip:
717  * @renderer: #GogRenderer
718  * @x: x position of grip
719  * @y: y position of grip
720  *
721  * Draw a grip, used for moving/resizing of objects.
722  **/
723 
724 void
gog_renderer_draw_grip(GogRenderer * renderer,double x,double y)725 gog_renderer_draw_grip (GogRenderer *renderer, double x, double y)
726 {
727 	GogViewAllocation rectangle;
728 
729 	if (renderer->grip_style == NULL) {
730 		GOStyle *style;
731 
732 		style = go_style_new ();
733 		style->line.dash_type = GO_LINE_SOLID;
734 		style->line.width = 0.0;
735 		style->line.color =
736 		style->fill.pattern.back = 0xff000080;
737 		style->fill.pattern.pattern = GO_PATTERN_SOLID;
738 		style->fill.type = GO_STYLE_FILL_PATTERN;
739 		style->interesting_fields = GO_STYLE_FILL | GO_STYLE_OUTLINE;
740 
741 		renderer->grip_style = style;
742 	}
743 
744 	rectangle.x = x - GOG_RENDERER_GRIP_SIZE;
745 	rectangle.y = y - GOG_RENDERER_GRIP_SIZE;
746 	rectangle.w = rectangle.h = 2 * GOG_RENDERER_GRIP_SIZE;
747 
748 	gog_renderer_push_style (renderer, renderer->grip_style);
749 
750 	gog_renderer_draw_rectangle (renderer, &rectangle);
751 
752 	gog_renderer_pop_style (renderer);
753 }
754 
755 void
gog_renderer_draw_selection_rectangle(GogRenderer * renderer,GogViewAllocation const * rectangle)756 gog_renderer_draw_selection_rectangle (GogRenderer *renderer, GogViewAllocation const *rectangle)
757 {
758 	if (renderer->selection_style == NULL) {
759 		GOStyle *style;
760 
761 		style = go_style_new ();
762 		style->line.dash_type = GO_LINE_DOT;
763 		style->line.width = 0.0;
764 		style->line.color = 0x0000ffB0;
765 		style->fill.type = GO_STYLE_FILL_NONE;
766 		style->interesting_fields = GO_STYLE_OUTLINE;
767 
768 		renderer->selection_style = style;
769 	}
770 
771 	gog_renderer_push_style (renderer, renderer->selection_style);
772 
773 	gog_renderer_draw_rectangle (renderer, rectangle);
774 
775 	gog_renderer_pop_style (renderer);
776 }
777 
778 static cairo_surface_t *
_get_marker_surface(GogRenderer * rend)779 _get_marker_surface (GogRenderer *rend)
780 {
781 	GOMarker *marker = rend->marker;
782 	double width;
783 
784 	if (rend->marker_surface != NULL)
785 		return rend->marker_surface;
786 
787 	rend->marker_surface = go_marker_create_cairo_surface (marker, rend->cairo, rend->scale, &width, NULL);
788 
789 	rend->marker_offset = width * 0.5;
790 	return rend->marker_surface;
791 }
792 
793 /**
794  * gog_renderer_draw_marker:
795  * @rend: #GogRenderer
796  * @x: X-coordinate
797  * @y: Y-coordinate
798  **/
799 void
gog_renderer_draw_marker(GogRenderer * rend,double x,double y)800 gog_renderer_draw_marker (GogRenderer *rend, double x, double y)
801 {
802 	cairo_surface_t *surface;
803 
804 	g_return_if_fail (GOG_IS_RENDERER (rend));
805 	g_return_if_fail (rend->cur_style != NULL);
806 
807 	if (rend->marker == NULL) {
808 		if (rend->cur_style->marker.auto_fill_color &&
809 		    !go_marker_is_closed_shape (rend->cur_style->marker.mark)) {
810 			rend->marker = go_marker_dup (rend->cur_style->marker.mark);
811 			go_marker_set_fill_color (rend->marker, 0);
812 		} else
813 			rend->marker = g_object_ref (rend->cur_style->marker.mark);
814 	}
815 
816 	if (rend->is_vector && !rend->marker_as_surface) {
817 		go_marker_render (rend->marker, rend->cairo,
818 				  x, y, rend->scale);
819 		return;
820 	}
821 
822 	surface = _get_marker_surface (rend);
823 
824 	if (surface == NULL)
825 		return;
826 
827 	if (rend->is_vector)
828 		cairo_set_source_surface (rend->cairo, surface,
829 					  x - rend->marker_offset,
830 					  y - rend->marker_offset);
831 	else
832 		cairo_set_source_surface (rend->cairo, surface,
833 					  floor (x - rend->marker_offset),
834 					  floor (y - rend->marker_offset));
835 
836 	cairo_paint (rend->cairo);
837 }
838 
839 /**
840  * GoJustification:
841  * @GO_JUSTIFY_LEFT: The text is placed at the left edge of the label.
842  * @GO_JUSTIFY_RIGHT: The text is placed at the right edge of the label.
843  * @GO_JUSTIFY_CENTER: The text is placed in the center of the label.
844  * @GO_JUSTIFY_FILL: The text is placed is distributed across the label.
845  *
846  * Used for justifying the text inside multiline label.
847  */
848 
849 /**
850  * gog_renderer_draw_gostring:
851  * @rend: #GogRenderer
852  * @str: the #GOString to draw
853  * @pos: #GogViewAllocation
854  * @anchor: #GOAnchorType how to draw relative to @pos
855  * @justification: #GoJustification for multiline string.
856  * @width: if positive, the maximum width to get a multiline string if needed.
857  *
858  * Have @rend draw @layout in the at @pos.{x,y} anchored by the @anchor corner.
859  * If @pos.w or @pos.h are >= 0 then clip the results to less than that size.
860  **/
861 
862 void
gog_renderer_draw_gostring(GogRenderer * rend,GOString * str,GogViewAllocation const * pos,GOAnchorType anchor,GoJustification justification,double width)863 gog_renderer_draw_gostring (GogRenderer *rend, GOString *str,
864 			    GogViewAllocation const *pos, GOAnchorType anchor,
865                             GoJustification justification, double width)
866 {
867 	PangoLayout *layout;
868 	PangoContext *context;
869 	cairo_t *cairo;
870 	GOGeometryOBR obr;
871 	GOGeometryAABR aabr;
872 	GOStyle const *style;
873 	int iw, ih;
874 	PangoAttrList *attr;
875 
876 	g_return_if_fail (str != NULL);
877 	g_return_if_fail (GOG_IS_RENDERER (rend));
878 	g_return_if_fail (rend->cur_style != NULL);
879 
880 	cairo = rend->cairo;
881 	style = rend->cur_style;
882 
883 	/* Note: orig layout may not have been created using cairo! */
884 	layout = pango_cairo_create_layout (cairo);
885 	context = pango_layout_get_context (layout);
886 	pango_layout_set_text (layout, str->str, -1);
887 	if (width > 0)
888 		pango_layout_set_width (layout, width * PANGO_SCALE / rend->scale);
889 	switch (justification) {
890 	case GO_JUSTIFY_CENTER:
891 		pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
892 		break;
893 	case GO_JUSTIFY_LEFT:
894 		pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
895 		break;
896 	case GO_JUSTIFY_RIGHT:
897 		pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
898 		break;
899 	case GO_JUSTIFY_FILL:
900 		pango_layout_set_justify (layout, TRUE);
901 		break;
902 	}
903 	attr = go_string_get_markup (str);
904 	if (attr) {
905 		pango_layout_set_attributes (layout, attr);
906 		go_pango_translate_layout (layout);
907 	}
908 	pango_cairo_context_set_resolution (context, 72.0);
909 	pango_layout_set_font_description (layout, style->font.font->desc);
910 	pango_layout_get_size (layout, &iw, &ih);
911 
912 	obr.w = rend->scale * ((double) iw + (double) PANGO_SCALE / 2.0)
913 		/ (double) PANGO_SCALE;
914 	obr.h = rend->scale * ((double) ih + (double) PANGO_SCALE / 2.0)
915 		/(double) PANGO_SCALE;
916 	obr.alpha = -style->text_layout.angle * M_PI / 180.0;
917 	obr.x = pos->x;
918 	obr.y = pos->y;
919 	go_geometry_OBR_to_AABR (&obr, &aabr);
920 
921 	switch (anchor) {
922 		case GO_ANCHOR_NW: case GO_ANCHOR_W: case GO_ANCHOR_SW:
923 			obr.x += aabr.w / 2.0;
924 			break;
925 		case GO_ANCHOR_NE : case GO_ANCHOR_SE : case GO_ANCHOR_E :
926 			obr.x -= aabr.w / 2.0;
927 			break;
928 		default : break;
929 	}
930 
931 	switch (anchor) {
932 		case GO_ANCHOR_NW: case GO_ANCHOR_N: case GO_ANCHOR_NE:
933 			obr.y += aabr.h / 2.0;
934 			break;
935 		case GO_ANCHOR_SE : case GO_ANCHOR_S : case GO_ANCHOR_SW :
936 			obr.y -= aabr.h / 2.0;
937 			break;
938 		default : break;
939 	}
940 
941 	cairo_save (cairo);
942 	cairo_set_source_rgba (cairo, GO_COLOR_TO_CAIRO (style->font.color));
943 	cairo_move_to (cairo, obr.x - (obr.w / 2.0) * cos (obr.alpha) +
944 		       (obr.h / 2.0) * sin (obr.alpha),
945 		       obr.y - (obr.w / 2.0) * sin (obr.alpha) -
946 		       (obr.h / 2.0) * cos (obr.alpha));
947 	cairo_rotate (cairo, obr.alpha);
948 	cairo_scale (cairo, rend->scale, rend->scale);
949 	pango_cairo_show_layout (cairo, layout);
950 	cairo_restore (cairo);
951 	g_object_unref (layout);
952 }
953 
954 
955 /**
956  * gog_renderer_draw_text:
957  * @rend: #GogRenderer
958  * @text: the string to draw
959  * @pos: #GogViewAllocation
960  * @anchor: #GOAnchorType how to draw relative to @pos
961  * @use_markup: wether to use pango markup
962  * @justification: #GoJustification for multiline text.
963  * @width: if positive, the maximum width to get a multiline text if needed.
964  *
965  * Have @rend draw @text in the at @pos.{x,y} anchored by the @anchor corner.
966  * If @pos.w or @pos.h are >= 0 then clip the results to less than that size.
967  **/
968 
969 void
gog_renderer_draw_text(GogRenderer * rend,char const * text,GogViewAllocation const * pos,GOAnchorType anchor,gboolean use_markup,GoJustification justification,double width)970 gog_renderer_draw_text (GogRenderer *rend, char const *text,
971 			GogViewAllocation const *pos, GOAnchorType anchor,
972 			gboolean use_markup, GoJustification justification,
973                         double width)
974 {
975 	GOString *str;
976 	PangoAttrList *attr_list = NULL;
977 	char *m_text = NULL;
978 
979 	g_return_if_fail (GOG_IS_RENDERER (rend));
980 	g_return_if_fail (text != NULL);
981 
982 	if (*text == '\0')
983 		return;
984 
985 	if (use_markup && pango_parse_markup  (text, -1, 0,
986 					       &attr_list, &m_text,
987 					       NULL, NULL))
988 		str = go_string_new_rich_nocopy (m_text, -1, attr_list, NULL);
989 	else
990 		str = go_string_new (text);
991 	gog_renderer_draw_gostring (rend, str, pos, anchor, justification, width);
992 	go_string_unref (str);
993 }
994 
995 /**
996  * gog_renderer_get_gostring_OBR:
997  * @rend: #GogRenderer
998  * @str: the string to draw
999  * @obr: #GOGeometryOBR to store the Object Bounding Rectangle of @text.
1000  * @max_width: maximum width or -1 for unrestricted
1001  **/
1002 void
gog_renderer_get_gostring_OBR(GogRenderer * rend,GOString * str,GOGeometryOBR * obr,double max_width)1003 gog_renderer_get_gostring_OBR (GogRenderer *rend, GOString *str,
1004                                GOGeometryOBR *obr, double max_width)
1005 {
1006 	GOStyle const *style;
1007 	PangoLayout *layout;
1008 	PangoContext *context;
1009 	PangoRectangle logical;
1010 	cairo_t *cairo;
1011 	PangoAttrList *attr;
1012 
1013 	g_return_if_fail (GOG_IS_RENDERER (rend));
1014 	g_return_if_fail (rend->cur_style != NULL);
1015 	g_return_if_fail (obr != NULL);
1016 
1017 	cairo = rend->cairo;
1018 	obr->x = obr->y = 0;
1019 	if (str->str == NULL || *(str->str) == '\0') {
1020 		/* Make sure invisible things don't skew size */
1021 		obr->w = obr->h = 0;
1022 		obr->alpha = 0;
1023 		return;
1024 	}
1025 
1026 	style = rend->cur_style;
1027 	layout = pango_cairo_create_layout (cairo);
1028 	context = pango_layout_get_context (layout);
1029 	pango_layout_set_text (layout, str->str, -1);
1030 	if (max_width > 0)
1031 		pango_layout_set_width (layout, max_width * PANGO_SCALE / rend->scale);
1032 	attr = go_string_get_markup (str);
1033 	if (attr) {
1034 		pango_layout_set_attributes (layout, attr);
1035 		go_pango_translate_layout (layout);
1036 	}
1037 	pango_cairo_context_set_resolution (context, 72.0);
1038 	pango_layout_set_font_description (layout, style->font.font->desc);
1039 	pango_layout_get_extents (layout, NULL, &logical);
1040 	g_object_unref (layout);
1041 
1042 	obr->w = rend->scale * ((double) logical.width + (double) PANGO_SCALE / 2.0)
1043 		/ (double) PANGO_SCALE;
1044 	obr->h = rend->scale * ((double) logical.height + (double) PANGO_SCALE / 2.0)
1045 		/(double) PANGO_SCALE;
1046 
1047 	/* Make sure invisible things don't skew size */
1048 	if (obr->w == 0)
1049 		obr->h = 0;
1050 	else if (obr->h == 0)
1051 		obr->w = 0;
1052 
1053 	obr->alpha = - style->text_layout.angle * M_PI / 180.0;
1054 }
1055 
1056 
1057 /**
1058  * gog_renderer_get_text_OBR:
1059  * @rend: #GogRenderer
1060  * @text: the string to draw
1061  * @use_markup: wether to use pango markup
1062  * @obr: #GOGeometryOBR to store the Object Bounding Rectangle of @text.
1063  * @max_width: maximum width or -1 for unrestricted
1064  **/
1065 void
gog_renderer_get_text_OBR(GogRenderer * rend,char const * text,gboolean use_markup,GOGeometryOBR * obr,double max_width)1066 gog_renderer_get_text_OBR (GogRenderer *rend, char const *text,
1067 			   gboolean use_markup, GOGeometryOBR *obr,
1068                            double max_width)
1069 {
1070 	GOString *str;
1071 	PangoAttrList *attr_list = NULL;
1072 	char *m_text = NULL;
1073 
1074 	g_return_if_fail (GOG_IS_RENDERER (rend));
1075 	g_return_if_fail (text != NULL);
1076 
1077 	if (use_markup && pango_parse_markup  (text, -1, 0,
1078 					       &attr_list, &m_text,
1079 					       NULL, NULL))
1080 		str = go_string_new_rich_nocopy (m_text, -1, attr_list, NULL);
1081 	else
1082 		str = go_string_new (text);
1083 	gog_renderer_get_gostring_OBR (rend, str, obr, max_width);
1084 	go_string_unref (str);
1085 
1086 }
1087 
1088 /**
1089  * gog_renderer_get_text_AABR:
1090  * @rend: #GogRenderer
1091  * @text: the string to draw
1092  * @use_markup: whether to use pango markup
1093  * @aabr: #GOGeometryAABR to store the Axis Aligned Bounding Rectangle of @text.
1094  * @max_width: maximum width or -1 for unrestricted
1095  **/
1096 void
gog_renderer_get_text_AABR(GogRenderer * rend,char const * text,gboolean use_markup,GOGeometryAABR * aabr,double max_width)1097 gog_renderer_get_text_AABR (GogRenderer *rend, char const *text,
1098 			    gboolean use_markup, GOGeometryAABR *aabr,
1099                             double max_width)
1100 {
1101 	GOGeometryOBR obr;
1102 
1103 	gog_renderer_get_text_OBR (rend, text, use_markup, &obr, max_width);
1104 	go_geometry_OBR_to_AABR (&obr, aabr);
1105 }
1106 
1107 /**
1108  * gog_renderer_get_gostring_AABR:
1109  * @rend: #GogRenderer
1110  * @str: the string to draw
1111  * @aabr: #GOGeometryAABR to store the Axis Aligned Bounding Rectangle of @text.
1112  * @max_width: maximum width or -1 for unrestricted
1113  **/
1114 void
gog_renderer_get_gostring_AABR(GogRenderer * rend,GOString * str,GOGeometryAABR * aabr,double max_width)1115 gog_renderer_get_gostring_AABR (GogRenderer *rend, GOString *str,
1116 				GOGeometryAABR *aabr, double max_width)
1117 {
1118 	GOGeometryOBR obr;
1119 
1120 	gog_renderer_get_gostring_OBR (rend, str, &obr, max_width);
1121 	go_geometry_OBR_to_AABR (&obr, aabr);
1122 }
1123 
1124 void
gog_renderer_draw_data_label(GogRenderer * rend,GogSeriesLabelElt const * elt,GogViewAllocation const * pos,GOAnchorType anchor,GOStyle * legend_style)1125 gog_renderer_draw_data_label (GogRenderer *rend, GogSeriesLabelElt const *elt,
1126                               GogViewAllocation const *pos, GOAnchorType anchor,
1127                               GOStyle *legend_style)
1128 {
1129 	/* things are a bit different from gog_renderer_draw_gostring, so the
1130 	 * code must be copied */
1131 	PangoLayout *layout;
1132 	PangoContext *context;
1133 	cairo_t *cairo;
1134 	GOGeometryOBR obr;
1135 	GOGeometryAABR aabr;
1136 	GOStyle const *style;
1137 	int iw, ih;
1138 	PangoAttrList *attrs;
1139 	PangoRectangle rect;
1140 
1141 	g_return_if_fail (elt != NULL && elt->str != NULL);
1142 	g_return_if_fail (GOG_IS_RENDERER (rend));
1143 	g_return_if_fail (rend->cur_style != NULL);
1144 
1145 	cairo = rend->cairo;
1146 	style = (GO_IS_STYLED_OBJECT (elt->point))?
1147 		go_styled_object_get_style (GO_STYLED_OBJECT (elt->point)):
1148 		rend->cur_style;
1149 
1150 	/* Note: orig layout may not have been created using cairo! */
1151 	layout = pango_cairo_create_layout (cairo);
1152 	pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
1153 	context = pango_layout_get_context (layout);
1154 	pango_layout_set_text (layout, elt->str->str, -1);
1155 	attrs = pango_attr_list_copy (go_string_get_markup (elt->str));
1156 	pango_layout_set_font_description (layout, style->font.font->desc);
1157 	if (attrs)
1158 		pango_layout_set_attributes (layout, attrs);
1159 	pango_cairo_context_set_resolution (context, 72.0);
1160 	/*now get the real size */
1161 	pango_layout_get_size (layout, &iw, &ih);
1162 	if (elt->legend_pos >= 0) {
1163 		/* we need to add enough room to draw the legend entry */
1164 		PangoRectangle rect;
1165 		PangoAttribute *attr;
1166 		rect.x = rect.y = 0;
1167 		pango_layout_get_size (layout, &iw, &ih);
1168 		rect.height = 1; /* only the width is important */
1169 		rect.width = (legend_style->interesting_fields & GO_STYLE_LINE)? 2 * ih: ih;
1170 		attr = pango_attr_shape_new (&rect, &rect);
1171 		attr->start_index = elt->legend_pos;
1172 		attr->end_index = elt->legend_pos + 1;
1173 		pango_attr_list_insert (attrs, attr);
1174 		pango_layout_set_attributes (layout, attrs);
1175 		pango_layout_get_size (layout, &iw, &ih);
1176 	}
1177 	pango_attr_list_unref (attrs);
1178 
1179 	obr.w = rend->scale * ((double) iw + (double) PANGO_SCALE / 2.0)
1180 		/ (double) PANGO_SCALE;
1181 	obr.h = rend->scale * ((double) ih + (double) PANGO_SCALE / 2.0)
1182 		/(double) PANGO_SCALE;
1183 	obr.alpha = -style->text_layout.angle * M_PI / 180.0;
1184 	obr.x = pos->x;
1185 	obr.y = pos->y;
1186 	go_geometry_OBR_to_AABR (&obr, &aabr);
1187 
1188 	switch (anchor) {
1189 		case GO_ANCHOR_NW: case GO_ANCHOR_W: case GO_ANCHOR_SW:
1190 			obr.x += aabr.w / 2.0;
1191 			break;
1192 		case GO_ANCHOR_NE : case GO_ANCHOR_SE : case GO_ANCHOR_E :
1193 			obr.x -= aabr.w / 2.0;
1194 			break;
1195 		default : break;
1196 	}
1197 
1198 	switch (anchor) {
1199 		case GO_ANCHOR_NW: case GO_ANCHOR_N: case GO_ANCHOR_NE:
1200 			obr.y += aabr.h / 2.0;
1201 			break;
1202 		case GO_ANCHOR_SE : case GO_ANCHOR_S : case GO_ANCHOR_SW :
1203 			obr.y -= aabr.h / 2.0;
1204 			break;
1205 		default : break;
1206 	}
1207 
1208 	cairo_save (cairo);
1209 	cairo_set_source_rgba (cairo, GO_COLOR_TO_CAIRO (style->font.color));
1210 	cairo_translate (cairo, obr.x - (obr.w / 2.0) * cos (obr.alpha) +
1211 		       (obr.h / 2.0) * sin (obr.alpha),
1212 		       obr.y - (obr.w / 2.0) * sin (obr.alpha) -
1213 		       (obr.h / 2.0) * cos (obr.alpha));
1214 	cairo_rotate (cairo, obr.alpha);
1215 	/* now draw the legen entry if needed */
1216 	if (elt->legend_pos >= 0 && legend_style != NULL) {
1217 		GOStyle *style = go_style_dup (legend_style);
1218 		GogViewAllocation rectangle;
1219 		double x, y,w, h;
1220 		pango_layout_index_to_pos (layout, elt->legend_pos, &rect);
1221 		x = (double) rect.x / PANGO_SCALE * rend->scale;
1222 		y = (double) rect.y / PANGO_SCALE * rend->scale;
1223 		w = (double) rect.width / PANGO_SCALE * rend->scale;
1224 		h = (double) rect.height / PANGO_SCALE * rend->scale;
1225 		if (style->interesting_fields & GO_STYLE_LINE) { /* line and marker */
1226 			GOPath *line_path;
1227 			if (style->line.width > h / 3.)
1228 				style->line.width = h / 3.;
1229 			if (go_marker_get_size (style->marker.mark) > h)
1230 				go_marker_set_size (style->marker.mark, h);
1231 			gog_renderer_push_style (rend, style);
1232 			line_path = go_path_new ();
1233 			y += h / 2.;
1234 			go_path_move_to (line_path, x, y);
1235 			go_path_line_to (line_path, x + w, y);
1236 			if (style->interesting_fields & GO_STYLE_FILL) {
1237 				rectangle.x = x;
1238 				rectangle.y = y;
1239 				rectangle.w = w;
1240 				rectangle.h = h / 2.0;
1241 				gog_renderer_fill_rectangle (rend, &rectangle);
1242 			}
1243 			gog_renderer_stroke_serie (rend, line_path);
1244 			go_path_free (line_path);
1245 			gog_renderer_draw_marker (rend, x + w / 2., y);
1246 		} else if (style->interesting_fields & GO_STYLE_FILL) {/* area */
1247 			if (style->line.width > h / 3.)
1248 				style->line.width = h / 3.;
1249 
1250 			rectangle.x = x;
1251 			rectangle.y = y;
1252 			rectangle.w = w;
1253 			rectangle.h = h;
1254 
1255 			gog_renderer_push_style (rend, style);
1256 			gog_renderer_draw_rectangle (rend, &rectangle);
1257 		} else if (style->interesting_fields & GO_STYLE_MARKER) {					/* markers only */
1258 			if (go_marker_get_size (style->marker.mark) > h)
1259 				go_marker_set_size (style->marker.mark, h);
1260 			gog_renderer_push_style (rend, style);
1261 			gog_renderer_draw_marker (rend, x + w / 2., y + h / 2.);
1262 		}
1263 		gog_renderer_pop_style (rend);
1264 		g_object_unref (style);
1265 	}
1266 	cairo_scale (cairo, rend->scale, rend->scale);
1267 	pango_cairo_show_layout (cairo, layout);
1268 	cairo_restore (cairo);
1269 	g_object_unref (layout);
1270 }
1271 
1272 static void
_free_marker_data(GogRenderer * rend)1273 _free_marker_data (GogRenderer *rend)
1274 {
1275 	if (rend->marker_surface != NULL) {
1276 		cairo_surface_destroy (rend->marker_surface);
1277 		rend->marker_surface = NULL;
1278 	}
1279 	if (rend->marker) {
1280 		g_object_unref (rend->marker);
1281 		rend->marker = NULL;
1282 	}
1283 }
1284 
1285 void
gog_renderer_draw_color_map(GogRenderer * rend,GogAxisColorMap const * map,int discrete,gboolean horizontal,GogViewAllocation const * rect)1286 gog_renderer_draw_color_map (GogRenderer *rend, GogAxisColorMap const *map,
1287                              int discrete, gboolean horizontal,
1288                              GogViewAllocation const *rect)
1289 {
1290 	cairo_save (rend->cairo);
1291 	cairo_translate (rend->cairo, rect->x, rect->y);
1292 	gog_axis_color_map_to_cairo (map, rend->cairo, discrete, horizontal,
1293 	                             rect->w, rect->h);
1294 	cairo_restore (rend->cairo);
1295 }
1296 
1297 void
gog_renderer_push_style(GogRenderer * rend,GOStyle const * style)1298 gog_renderer_push_style (GogRenderer *rend, GOStyle const *style)
1299 {
1300 	g_return_if_fail (GOG_IS_RENDERER (rend));
1301 	g_return_if_fail (GO_IS_STYLE (style));
1302 
1303 	if (rend->cur_style != NULL)
1304 		rend->style_stack = g_slist_prepend (
1305 			rend->style_stack, (gpointer)rend->cur_style);
1306 	g_object_ref ((gpointer)style);
1307 	rend->cur_style = style;
1308 
1309 	_free_marker_data (rend);
1310 
1311 	_update_dash (rend);
1312 }
1313 
1314 void
gog_renderer_pop_style(GogRenderer * rend)1315 gog_renderer_pop_style (GogRenderer *rend)
1316 {
1317 	g_return_if_fail (GOG_IS_RENDERER (rend));
1318 	g_return_if_fail (GO_IS_STYLE (rend->cur_style));
1319 
1320 	g_object_unref ((gpointer)rend->cur_style);
1321 	if (rend->style_stack != NULL) {
1322 		rend->cur_style = rend->style_stack->data;
1323 		rend->style_stack = g_slist_remove (rend->style_stack,
1324 			rend->cur_style);
1325 	} else
1326 		rend->cur_style = NULL;
1327 
1328 	_free_marker_data (rend);
1329 
1330 	_update_dash (rend);
1331 }
1332 
1333 void
gog_renderer_request_update(GogRenderer * renderer)1334 gog_renderer_request_update (GogRenderer *renderer)
1335 {
1336 	g_return_if_fail (GOG_IS_RENDERER (renderer));
1337 
1338 	if (renderer->needs_update)
1339 		return;
1340 	renderer->needs_update = TRUE;
1341 	g_signal_emit (G_OBJECT (renderer),
1342 		renderer_signals [RENDERER_SIGNAL_REQUEST_UPDATE], 0);
1343 }
1344 
1345 /**
1346  * gog_renderer_update:
1347  * @renderer: a #GogRenderer
1348  * @w: requested width
1349  * @h: requested height
1350  *
1351  * Requests a renderer update, only useful for pixbuf based renderer.
1352  *
1353  * Returns: %TRUE if a redraw is necessary.
1354  **/
1355 gboolean
gog_renderer_update(GogRenderer * rend,double w,double h)1356 gog_renderer_update (GogRenderer *rend, double w, double h)
1357 {
1358 	GogGraph *graph;
1359 	GogView *view;
1360 	GogViewAllocation allocation;
1361 	gboolean redraw = TRUE;
1362 	gboolean size_changed;
1363 
1364 	if (w <= 0 || h <= 0)
1365 		return FALSE;
1366 	g_return_val_if_fail (GOG_IS_RENDERER (rend), FALSE);
1367 	g_return_val_if_fail (GOG_IS_VIEW (rend->view), FALSE);
1368 
1369 	size_changed = rend->w != w || rend->h != h;
1370 	if (size_changed) {
1371 		if (rend->cairo_surface != NULL) {
1372 			cairo_surface_destroy (rend->cairo_surface);
1373 			rend->cairo_surface = NULL;
1374 		}
1375 
1376 		if (w == 0 || h == 0)
1377 			return FALSE;
1378 
1379 		rend->w = w;
1380 		rend->h = h;
1381 
1382 		rend->cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, rend->w, rend->h);
1383 	}
1384 
1385 	view = rend->view;
1386 	graph = GOG_GRAPH (view->model);
1387 	gog_graph_force_update (graph);
1388 
1389 	allocation.x = allocation.y = 0.;
1390 	allocation.w = rend->w;
1391 	allocation.h = rend->h;
1392 
1393 	rend->cairo = cairo_create (rend->cairo_surface);
1394 
1395 	/* we need to update scale even if size did not change since graph size might have changed (#599887) */
1396 	rend->scale_x = (graph->width >= 1.) ? (rend->w / graph->width) : 1.;
1397 	rend->scale_y = (graph->height >= 1.) ? (rend->h / graph->height) : 1.;
1398 	rend->scale = MIN (rend->scale_x, rend->scale_y);
1399 
1400 	if (size_changed) {
1401 		/* make sure we dont try to queue an update while updating */
1402 		rend->needs_update = TRUE;
1403 
1404 		/* scale just changed need to recalculate sizes */
1405 		gog_renderer_request_update (rend);
1406 		gog_view_size_allocate (view, &allocation);
1407 	} else
1408 		if (rend->w != view->allocation.w || rend->h != view->allocation.h)
1409 			gog_view_size_allocate (view, &allocation);
1410 		else
1411 			redraw = gog_view_update_sizes (view);
1412 
1413 	redraw |= rend->needs_update;
1414 	rend->needs_update = FALSE;
1415 
1416 	if (redraw) {
1417 		if (rend->pixbuf) {
1418 			g_object_unref (rend->pixbuf);
1419 			rend->pixbuf = NULL;
1420 		}
1421 
1422 		if (!size_changed) {
1423 			cairo_set_operator (rend->cairo, CAIRO_OPERATOR_CLEAR);
1424 			cairo_paint (rend->cairo);
1425 		}
1426 
1427 		cairo_set_operator (rend->cairo, CAIRO_OPERATOR_OVER);
1428 
1429 		cairo_set_line_join (rend->cairo, CAIRO_LINE_JOIN_ROUND);
1430 		cairo_set_line_cap (rend->cairo, CAIRO_LINE_CAP_ROUND);
1431 
1432 		rend->is_vector = FALSE;
1433 		gog_view_render	(view, NULL);
1434 	}
1435 
1436 	cairo_destroy (rend->cairo);
1437 	rend->cairo = NULL;
1438 
1439 	return redraw;
1440 }
1441 
1442 /**
1443  * gog_renderer_get_pixbuf:
1444  * @renderer: #GogRenderer
1445  *
1446  * Returns: (transfer none): current pixbuf buffer from a renderer that can render into a pixbuf.
1447  * 	or %NULL on error.
1448  **/
1449 GdkPixbuf *
gog_renderer_get_pixbuf(GogRenderer * rend)1450 gog_renderer_get_pixbuf (GogRenderer *rend)
1451 {
1452 	g_return_val_if_fail (GOG_IS_RENDERER (rend), NULL);
1453 
1454 	if (rend->cairo_surface == NULL)
1455 		return NULL;
1456 
1457 	if (rend->pixbuf == NULL) {
1458 		int width = cairo_image_surface_get_width (rend->cairo_surface);
1459 		int height = cairo_image_surface_get_height (rend->cairo_surface);
1460 
1461 		if (width <= 0 || height <= 0)
1462 			rend->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
1463 		else {
1464 			int rowstride = cairo_image_surface_get_stride (rend->cairo_surface);
1465 			unsigned char *data = cairo_image_surface_get_data (rend->cairo_surface);
1466 			rend->pixbuf = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE, 8,
1467 								 width, height, rowstride, NULL, NULL);
1468 			go_cairo_convert_data_to_pixbuf (data, NULL, width, height, rowstride);
1469 		}
1470 	}
1471 
1472 	return rend->pixbuf;
1473 }
1474 
1475 cairo_surface_t *
gog_renderer_get_cairo_surface(GogRenderer * rend)1476 gog_renderer_get_cairo_surface (GogRenderer *rend)
1477 {
1478 	g_return_val_if_fail (GOG_IS_RENDERER (rend), NULL);
1479 
1480 	return rend->cairo_surface;
1481 }
1482 
1483 static gboolean
_gsf_gdk_pixbuf_save(const gchar * buf,gsize count,GError ** error,gpointer data)1484 _gsf_gdk_pixbuf_save (const gchar *buf,
1485 		      gsize count,
1486 		      GError **error,
1487 		      gpointer data)
1488 {
1489 	GsfOutput *output = GSF_OUTPUT (data);
1490 	gboolean ok = gsf_output_write (output, count, buf);
1491 
1492 	if (!ok && error)
1493 		*error = g_error_copy (gsf_output_error (output));
1494 
1495 	return ok;
1496 }
1497 
1498 static cairo_status_t
_cairo_write_func(void * closure,const unsigned char * data,unsigned int length)1499 _cairo_write_func (void *closure,
1500 		   const unsigned char *data,
1501 		   unsigned int length)
1502 {
1503 	gboolean result;
1504 	GsfOutput *output = GSF_OUTPUT (closure);
1505 
1506 	result = gsf_output_write (output, length, data);
1507 
1508 	return result ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
1509 }
1510 
1511 gboolean
gog_renderer_render_to_cairo(GogRenderer * renderer,cairo_t * cairo,double width,double height)1512 gog_renderer_render_to_cairo (GogRenderer *renderer, cairo_t *cairo, double width, double height)
1513 {
1514 	GogViewAllocation allocation;
1515 	double width_in_pts, height_in_pts;
1516 
1517 	g_return_val_if_fail (GOG_IS_RENDERER (renderer), FALSE);
1518 	g_return_val_if_fail (GOG_IS_VIEW (renderer->view), FALSE);
1519 	g_return_val_if_fail (GOG_IS_GRAPH (renderer->model), FALSE);
1520 
1521 	gog_graph_force_update (renderer->model);
1522 	gog_graph_get_size (renderer->model, &width_in_pts, &height_in_pts);
1523 
1524 	renderer->cairo = cairo;
1525 	renderer->is_vector = go_cairo_surface_is_vector (cairo_get_target (cairo));
1526 
1527 	cairo_set_line_join (renderer->cairo, CAIRO_LINE_JOIN_ROUND);
1528 	cairo_set_line_cap (renderer->cairo, CAIRO_LINE_CAP_ROUND);
1529 
1530 	renderer->w = width;
1531 	renderer->h = height;
1532 
1533 	allocation.x = 0.;
1534 	allocation.y = 0.;
1535 	allocation.w = width;
1536 	allocation.h = height;
1537 
1538 	renderer->scale_x = (width_in_pts >= 1.) ? (width / width_in_pts) : 1.;
1539 	renderer->scale_y = (height_in_pts >= 1.) ? (height / height_in_pts) : 1.;
1540 	renderer->scale = MIN (renderer->scale_x, renderer->scale_y);
1541 
1542 	gog_view_size_allocate (renderer->view, &allocation);
1543 	gog_view_render	(renderer->view, NULL);
1544 
1545 	renderer->cairo = NULL;
1546 
1547 	return cairo_status (cairo) == CAIRO_STATUS_SUCCESS;
1548 }
1549 
1550 /**
1551  * gog_renderer_export_image:
1552  * @renderer: a #GogRenderer
1553  * @format: image format for export
1554  * @output: a #GsfOutput stream
1555  * @x_dpi: x resolution of exported graph
1556  * @y_dpi: y resolution of exported graph
1557  *
1558  * Exports an image of @graph in given @format, writing results in a #GsfOutput stream.
1559  * If export format type is a bitmap one, it computes image size with x_dpi, y_dpi and
1560  * @graph size (see gog_graph_get_size()).
1561  *
1562  * returns: %TRUE if export succeed.
1563  **/
1564 
1565 gboolean
gog_renderer_export_image(GogRenderer * rend,GOImageFormat format,GsfOutput * output,double x_dpi,double y_dpi)1566 gog_renderer_export_image (GogRenderer *rend, GOImageFormat format,
1567 			   GsfOutput *output, double x_dpi, double y_dpi)
1568 {
1569 	GOImageFormatInfo const *format_info;
1570 	cairo_t *cairo;
1571 	cairo_surface_t *surface = NULL;
1572 	gboolean status;
1573 	GdkPixbuf *pixbuf;
1574 	GdkPixbuf *output_pixbuf;
1575 	gboolean result;
1576 	double width_in_pts, height_in_pts;
1577 
1578 	g_return_val_if_fail (GOG_IS_RENDERER (rend), FALSE);
1579 
1580 	if (x_dpi <= 0.)
1581 		x_dpi = 96.;
1582 	if (y_dpi <= 0.)
1583 		y_dpi = 96.;
1584 	gog_graph_force_update (rend->model);
1585 
1586 	gog_graph_get_size (rend->model, &width_in_pts, &height_in_pts);
1587 
1588 	/* Prevent Cairo from faulting.  */
1589 	width_in_pts = CLAMP (width_in_pts, 1, 32767 * 72.0 / x_dpi);
1590 	height_in_pts = CLAMP (height_in_pts, 1, 32767 * 72.0 / y_dpi);
1591 
1592 	switch (format) {
1593 		case GO_IMAGE_FORMAT_EPS:
1594 			rend->marker_as_surface = FALSE;
1595 			surface = cairo_ps_surface_create_for_stream
1596 				(_cairo_write_func,
1597 				 output, width_in_pts, height_in_pts);
1598 			cairo_ps_surface_set_eps (surface, TRUE);
1599 			cairo_surface_set_fallback_resolution (surface, x_dpi, y_dpi);
1600 			goto do_export_vectorial;
1601 		case GO_IMAGE_FORMAT_PDF:
1602 			rend->marker_as_surface = FALSE;
1603 			surface = cairo_pdf_surface_create_for_stream
1604 				(_cairo_write_func,
1605 				 output, width_in_pts, height_in_pts);
1606 			cairo_surface_set_fallback_resolution (surface, x_dpi, y_dpi);
1607 			goto do_export_vectorial;
1608 		case GO_IMAGE_FORMAT_PS:
1609 			rend->marker_as_surface = FALSE;
1610 			surface = cairo_ps_surface_create_for_stream
1611 				(_cairo_write_func,
1612 				 output, width_in_pts, height_in_pts);
1613 			cairo_surface_set_fallback_resolution (surface, x_dpi, y_dpi);
1614 			goto do_export_vectorial;
1615 		case GO_IMAGE_FORMAT_SVG:
1616 #ifdef CAIRO_BUG_63633_FIXED
1617 			rend->marker_as_surface = TRUE;
1618 #else
1619 			rend->marker_as_surface = FALSE;
1620 #endif
1621 			surface = cairo_svg_surface_create_for_stream
1622 				(_cairo_write_func,
1623 				 output, width_in_pts, height_in_pts);
1624 			cairo_surface_set_fallback_resolution (surface, x_dpi, y_dpi);
1625 do_export_vectorial:
1626 			rend->scale = 1.0;
1627 			cairo = cairo_create (surface);
1628 			cairo_surface_destroy (surface);
1629 			status = gog_renderer_render_to_cairo (rend, cairo, width_in_pts, height_in_pts);
1630 			cairo_destroy (cairo);
1631 
1632 			return status;
1633 
1634 			break;
1635 		default:
1636 			format_info = go_image_get_format_info (format);
1637 			if (!format_info->has_pixbuf_saver) {
1638 				g_warning ("[GogRendererCairo:export_image] unsupported format");
1639 				return FALSE;
1640 			}
1641 
1642 			gog_renderer_update (rend, width_in_pts * x_dpi / 72.0, height_in_pts * y_dpi / 72.0);
1643 			pixbuf = gog_renderer_get_pixbuf (rend);
1644 			if (pixbuf == NULL)
1645 				return FALSE;
1646 			format_info = go_image_get_format_info (format);
1647 			if (!format_info->alpha_support)
1648 				output_pixbuf = gdk_pixbuf_composite_color_simple
1649 					(pixbuf,
1650 					 gdk_pixbuf_get_width (pixbuf),
1651 					 gdk_pixbuf_get_height (pixbuf),
1652 					 GDK_INTERP_NEAREST,
1653 					 255, 256, 0xffffffff,
1654 					 0xffffffff);
1655 			else
1656 				output_pixbuf = pixbuf;
1657 			result = gdk_pixbuf_save_to_callback (output_pixbuf,
1658 							      _gsf_gdk_pixbuf_save,
1659 							      output, format_info->name,
1660 							      NULL, NULL);
1661 			if (!format_info->alpha_support)
1662 				g_object_unref (output_pixbuf);
1663 			return result;
1664 	}
1665 
1666 	return FALSE;
1667 }
1668 
1669 /**
1670  * gog_renderer_get_hairline_width_pts:
1671  * @rend: a #GogRenderer
1672  *
1673  * Returns: the hairline width in pts.
1674  **/
1675 double
gog_renderer_get_hairline_width_pts(GogRenderer const * rend)1676 gog_renderer_get_hairline_width_pts (GogRenderer const *rend)
1677 {
1678 	g_return_val_if_fail (GOG_IS_RENDERER (rend), GOG_RENDERER_HAIRLINE_WIDTH_PTS);
1679 
1680 	if (rend->is_vector
1681 	    || go_sub_epsilon (rend->scale) <= 0.0)
1682 	       return GOG_RENDERER_HAIRLINE_WIDTH_PTS;
1683 
1684 	return 1.0 / rend->scale;
1685 }
1686 
1687 #ifdef GOFFICE_WITH_LASEM
1688 
1689 void
gog_renderer_draw_equation(GogRenderer * renderer,LsmDomView * mathml_view,double x,double y)1690 gog_renderer_draw_equation (GogRenderer *renderer, LsmDomView *mathml_view, double x, double y)
1691 {
1692 	cairo_t *cairo;
1693 	double w, h, alpha;
1694 
1695 	g_return_if_fail (GOG_IS_RENDERER (renderer));
1696 	g_return_if_fail (LSM_IS_DOM_VIEW (mathml_view));
1697 	g_return_if_fail (renderer->cur_style != NULL);
1698 
1699 	cairo = renderer->cairo;
1700 
1701 	alpha = -renderer->cur_style->text_layout.angle * M_PI / 180.0;
1702 	lsm_dom_view_get_size (LSM_DOM_VIEW (mathml_view), &w, &h, NULL);
1703 	w *= renderer->scale;
1704 	h *= renderer->scale;
1705 	x = x - (w / 2.0) * cos (alpha) + (h / 2.0) * sin (alpha);
1706 	y = y - (w / 2.0) * sin (alpha) - (h / 2.0) * cos (alpha);
1707 
1708 	cairo_save (cairo);
1709 
1710 	cairo_translate (cairo, x, y);
1711 	cairo_rotate (cairo, alpha);
1712 	cairo_scale (cairo, renderer->scale_x, renderer->scale_y);
1713 
1714 	lsm_dom_view_render (LSM_DOM_VIEW (mathml_view), cairo, 0., 0.);
1715 
1716 	cairo_restore (cairo);
1717 }
1718 
1719 #endif
1720 
1721 /**
1722  * gog_renderer_new:
1723  * @graph: graph model
1724  *
1725  * Returns:  a new #GogRenderer which can render into a pixbuf, and sets @graph
1726  * 	as its model.
1727  **/
1728 GogRenderer*
gog_renderer_new(GogGraph * graph)1729 gog_renderer_new (GogGraph *graph)
1730 {
1731 	return g_object_new (GOG_TYPE_RENDERER, "model", graph, NULL);
1732 }
1733 
1734 static void
_cb_font_removed(GogRenderer * rend,GOFont const * font)1735 _cb_font_removed (GogRenderer *rend, GOFont const *font)
1736 {
1737 	g_return_if_fail (GOG_IS_RENDERER (rend));
1738 
1739 	gog_debug (0, g_warning ("notify a '%s' that %p is invalid",
1740 				 G_OBJECT_TYPE_NAME (rend), font););
1741 }
1742 
1743 static void
gog_renderer_init(GogRenderer * rend)1744 gog_renderer_init (GogRenderer *rend)
1745 {
1746 	rend->cairo = NULL;
1747 	rend->cairo_surface = NULL;
1748 	rend->marker_surface = NULL;
1749 	rend->w = rend->h = 0;
1750 	rend->is_vector = FALSE;
1751 
1752 	rend->line_dash = NULL;
1753 
1754 	rend->needs_update = FALSE;
1755 	rend->cur_style    = NULL;
1756 	rend->style_stack  = NULL;
1757 	rend->font_watcher = g_cclosure_new_swap (G_CALLBACK (_cb_font_removed),
1758 		rend, NULL);
1759 	go_font_cache_register (rend->font_watcher);
1760 
1761 	rend->grip_style = NULL;
1762 	rend->selection_style = NULL;
1763 
1764 	rend->pixbuf = NULL;
1765 }
1766 
1767 static void
gog_renderer_finalize(GObject * obj)1768 gog_renderer_finalize (GObject *obj)
1769 {
1770 	GogRenderer *rend = GOG_RENDERER (obj);
1771 
1772 	if (rend->pixbuf != NULL) {
1773 		g_object_unref (rend->pixbuf);
1774 		rend->pixbuf = NULL;
1775 	}
1776 
1777 	if (rend->cairo_surface != NULL) {
1778 		cairo_surface_destroy (rend->cairo_surface);
1779 		rend->cairo_surface = NULL;
1780 	}
1781 
1782 	_free_marker_data (rend);
1783 
1784 	go_line_dash_sequence_free (rend->line_dash);
1785 	rend->line_dash = NULL;
1786 
1787 	if (rend->grip_style != NULL) {
1788 		g_object_unref (rend->grip_style);
1789 		rend->grip_style = NULL;
1790 	}
1791 
1792 	if (rend->selection_style != NULL) {
1793 		g_object_unref (rend->selection_style);
1794 		rend->selection_style = NULL;
1795 	}
1796 
1797 	if (rend->cur_style != NULL) {
1798 		g_warning ("Missing calls to gog_renderer_style_pop left dangling style references");
1799 		g_slist_foreach (rend->style_stack,
1800 			(GFunc)g_object_unref, NULL);
1801 		g_slist_free (rend->style_stack);
1802 		rend->style_stack = NULL;
1803 		g_object_unref ((gpointer)rend->cur_style);
1804 		rend->cur_style = NULL;
1805 	}
1806 
1807 	if (rend->view != NULL) {
1808 		g_object_unref (rend->view);
1809 		rend->view = NULL;
1810 	}
1811 
1812 	if (rend->font_watcher != NULL) {
1813 		go_font_cache_unregister (rend->font_watcher);
1814 		g_closure_unref (rend->font_watcher);
1815 		rend->font_watcher = NULL;
1816 	}
1817 
1818 	(*parent_klass->finalize) (obj);
1819 }
1820 
1821 static void
gog_renderer_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)1822 gog_renderer_set_property (GObject *obj, guint param_id,
1823 			   GValue const *value, GParamSpec *pspec)
1824 {
1825 	GogRenderer *rend = GOG_RENDERER (obj);
1826 
1827 	switch (param_id) {
1828 	case RENDERER_PROP_MODEL:
1829 		rend->model = GOG_GRAPH (g_value_get_object (value));
1830 		if (rend->view != NULL)
1831 			g_object_unref (rend->view);
1832 		rend->view = g_object_new (gog_graph_view_get_type (),
1833 					   "renderer",	rend,
1834 					   "model",	rend->model,
1835 					   NULL);
1836 		gog_renderer_request_update (rend);
1837 		break;
1838 
1839 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1840 		 return; /* NOTE : RETURN */
1841 	}
1842 }
1843 
1844 static void
gog_renderer_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)1845 gog_renderer_get_property (GObject *obj, guint param_id,
1846 			   GValue *value, GParamSpec *pspec)
1847 {
1848 	GogRenderer const *rend = GOG_RENDERER (obj);
1849 
1850 	switch (param_id) {
1851 	case RENDERER_PROP_MODEL:
1852 		g_value_set_object (value, rend->model);
1853 		break;
1854 	case RENDERER_PROP_VIEW:
1855 		g_value_set_object (value, rend->view);
1856 		break;
1857 	default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1858 		 break;
1859 	}
1860 }
1861 
1862 static void
gog_renderer_class_init(GogRendererClass * renderer_klass)1863 gog_renderer_class_init (GogRendererClass *renderer_klass)
1864 {
1865 	GObjectClass *gobject_klass = (GObjectClass *)renderer_klass;
1866 
1867 	parent_klass = g_type_class_peek_parent (gobject_klass);
1868 	gobject_klass->finalize	    = gog_renderer_finalize;
1869 	gobject_klass->set_property = gog_renderer_set_property;
1870 	gobject_klass->get_property = gog_renderer_get_property;
1871 
1872 	g_object_class_install_property (gobject_klass, RENDERER_PROP_MODEL,
1873 		g_param_spec_object ("model",
1874 			_("Model"),
1875 			_("The GogGraph this renderer displays"),
1876 			GOG_TYPE_GRAPH,
1877 			GSF_PARAM_STATIC | G_PARAM_READWRITE));
1878 	g_object_class_install_property (gobject_klass, RENDERER_PROP_VIEW,
1879 		g_param_spec_object ("view",
1880 			_("View"),
1881 			_("the GogView this renderer is displaying"),
1882 			GOG_TYPE_VIEW,
1883 			GSF_PARAM_STATIC | G_PARAM_READABLE));
1884 
1885 	renderer_signals [RENDERER_SIGNAL_REQUEST_UPDATE] = g_signal_new ("request-update",
1886 		G_TYPE_FROM_CLASS (renderer_klass),
1887 		G_SIGNAL_RUN_LAST,
1888 		G_STRUCT_OFFSET (GogRendererClass, request_update),
1889 		NULL, NULL,
1890 		g_cclosure_marshal_VOID__VOID,
1891 		G_TYPE_NONE, 0);
1892 }
1893 
1894 double
gog_renderer_get_scale(GogRenderer * renderer)1895 gog_renderer_get_scale (GogRenderer *renderer)
1896 {
1897 	return renderer->scale;
1898 }
1899 
1900 GSF_CLASS (GogRenderer, gog_renderer,
1901 	   gog_renderer_class_init, gog_renderer_init,
1902 	   G_TYPE_OBJECT)
1903