1 /*
2  * go-style.c :
3  *
4  * Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
5  * Copyright (C) 2006-2007 Emmanuel Pacaud (emmanuel.pacaud@lapp.in2p3.fr)
6  *
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.
11  *
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.
16  *
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
21  */
22 
23 #include <goffice/goffice-config.h>
24 #include <goffice/goffice.h>
25 
26 #ifdef GOFFICE_WITH_GTK
27 #include <gdk-pixbuf/gdk-pixdata.h>
28 #endif
29 
30 #include <gsf/gsf-impl-utils.h>
31 #include <glib/gi18n-lib.h>
32 #include <string.h>
33 #include <math.h>
34 
35 #define CXML2C(s) ((char const *)(s))
36 
37 static inline gboolean
attr_eq(const xmlChar * a,const char * s)38 attr_eq (const xmlChar *a, const char *s)
39 {
40 	return !strcmp (CXML2C (a), s);
41 }
42 
43 /**
44  * GOImageType:
45  * @GO_IMAGE_STRETCHED: stretch the image so that it fills the whole area.
46  * @GO_IMAGE_WALLPAPER: repeat the image starting from top left.
47  * @GO_IMAGE_CENTERED: use only one image at its natural size, centered in the area.
48  * @GO_IMAGE_CENTERED_WALLPAPER:  repeat the image starting from one at center.
49  **/
50 
51 /**
52  * GOStyleFill:
53  * @GO_STYLE_FILL_NONE: no filling.
54  * @GO_STYLE_FILL_PATTERN: fill with pattern.
55  * @GO_STYLE_FILL_GRADIENT: fill with a gradient.
56  * @GO_STYLE_FILL_IMAGE: fill with an image.
57  **/
58 
59 /**
60  * GOStyleFlag:
61  * @GO_STYLE_OUTLINE: outline.
62  * @GO_STYLE_FILL: fill.
63  * @GO_STYLE_LINE: line.
64  * @GO_STYLE_MARKER: marker.
65  * @GO_STYLE_FONT: font.
66  * @GO_STYLE_TEXT_LAYOUT: text orientaiton.
67  * @GO_STYLE_INTERPOLATION: curve interpolation.
68  * @GO_STYLE_MARKER_NO_COLOR: marker with no color.
69  * @GO_STYLE_ALL: all elements
70  *
71  * Gives the meaningful fields in @GOStyle. Anyw combination can be used, except
72  * that @GO_STYLE_OUTLINE and @GO_STYLE_OUTLINE should never coexist.
73  **/
74 
75 /**
76  * GOStyleLine:
77  * @width: line width:
78  * <0 == no line,
79  * =0 == hairline : unscaled, minimum useful (can be bigger than visible) size.
80  * >0 in pts.
81  * @auto_width: automatic width
82  * @dash_type: #GOLineDashType.
83  * @auto_dash: automatic dash type.
84  * @color: color is used as background for compatibility
85  * (pattern == 0 means filled with background color).
86  * @fore: second color used for patterned lines.
87  * @auto_color: color is automatic.
88  * @auto_fore: fore is automatic.
89  * @pattern: pattern.
90  * @cap: cap style.
91  * @join: join style.
92  * @miter_limit: mitter limit.
93  **/
94 
95 /**
96  * GOStyleMark:
97  * @mark: the used #GOMarker.
98  * @auto_shape: automatic @marc.
99  * @auto_outline_color: automatic outline color.
100  * @auto_fill_color: automatic fill color.
101  **/
102 
103 #define HSCALE 100
104 #define VSCALE 120
105 
106 typedef GObjectClass GOStyleClass;
107 
108 static GObjectClass *parent_klass;
109 
110 /*
111  * I would have liked to do this differently and have a tighter binding between theme element and style
112  * 	eg go_style_new (theme_element)
113  * However that will not work easily in the context of xls import where we do
114  * not know what the type is destined for until later.  This structure melds
115  * smoothly with both approaches at the expense of a bit of power.
116  */
117 
118 /*************************************************************************/
119 
120 #ifdef GOFFICE_WITH_GTK
121 typedef struct {
122 	GtkBuilder  	*gui;
123 	GtkBuilder  	*font_gui;
124 	GOStyle  	*style;
125 	GOStyle  	*default_style;
126 	GObject		*object_with_style;
127 	gboolean   	 enable_edit;
128 	gulong     	 style_changed_handler;
129 	struct {
130 		GtkSizeGroup *size_group;
131 		GtkWidget *background;
132 		GtkWidget *background_box;
133 		GtkWidget *background_label;
134 		GtkWidget *foreground;
135 		GtkWidget *foreground_box;
136 		GtkWidget *foreground_label;
137 		GtkWidget *notebook;
138 		struct {
139 			GtkWidget *selector;
140 			GtkWidget *box;
141 		} pattern;
142 		struct {
143 			GtkWidget *selector;
144 			GtkWidget *box;
145 			GtkWidget *brightness;
146 			GtkWidget *brightness_box;
147 		} gradient;
148 		struct {
149 			GOImage *image;
150 		} image;
151 	} fill;
152 	struct {
153 		GtkWidget *color;
154 	} line;
155 	struct {
156 		GtkWidget *selector;
157 		GtkWidget *fill;
158 		GtkWidget *outline;
159 	} marker;
160 	GODoc *doc;
161 } StylePrefState;
162 
163 static void
set_style(StylePrefState const * state)164 set_style (StylePrefState const *state)
165 {
166 	if (state->object_with_style != NULL) {
167 		if (state->style_changed_handler)
168 			g_signal_handler_block (state->object_with_style, state->style_changed_handler);
169 		g_object_set (G_OBJECT (state->object_with_style), "style", state->style, NULL);
170 		if (state->style_changed_handler)
171 			g_signal_handler_unblock (state->object_with_style, state->style_changed_handler);
172 	}
173 }
174 
175 static GtkWidget *
create_go_combo_color(StylePrefState * state,GOColor initial_color,GOColor automatic_color,GtkBuilder * gui,char const * group,char const * label_name,GCallback func)176 create_go_combo_color (StylePrefState *state,
177 		       GOColor initial_color, GOColor automatic_color,
178 		       GtkBuilder *gui,
179 		       char const *group, char const *label_name,
180 		       GCallback func)
181 {
182 	GtkWidget *w;
183 
184 	w = go_selector_new_color (initial_color, automatic_color, group);
185 	gtk_widget_set_halign (w, GTK_ALIGN_START);
186 	gtk_label_set_mnemonic_widget (
187 		GTK_LABEL (gtk_builder_get_object (gui, label_name)), w);
188 	g_signal_connect (G_OBJECT (w), "activate", G_CALLBACK (func), state);
189 	return w;
190 }
191 
192 static void
go_style_set_image_preview(GOImage * pix,StylePrefState * state)193 go_style_set_image_preview (GOImage *pix, StylePrefState *state)
194 {
195 	GdkPixbuf *scaled;
196 	int width, height;
197 	char *size;
198 	GtkWidget *w;
199 
200 	if (state->fill.image.image != pix) {
201 		if (state->fill.image.image != NULL)
202 			g_object_unref (state->fill.image.image);
203 		state->fill.image.image = pix;
204 		if (state->fill.image.image != NULL)
205 			g_object_ref (state->fill.image.image);
206 	}
207 
208 	if (pix == NULL) {
209 		gtk_widget_hide (go_gtk_builder_get_widget (state->gui, "fill_image_sample"));
210 		gtk_label_set_text (GTK_LABEL (go_gtk_builder_get_widget (state->gui, "image-size-label")), _("No image!"));
211 		return;
212 	}
213 
214 	w = go_gtk_builder_get_widget (state->gui, "fill_image_sample");
215 
216 	scaled = go_image_get_scaled_pixbuf (pix, HSCALE, VSCALE);
217 	gtk_image_set_from_pixbuf (GTK_IMAGE (w), scaled);
218 	g_object_unref (scaled);
219 	gtk_widget_show (w);
220 
221 	w = go_gtk_builder_get_widget (state->gui, "image-size-label");
222 	g_object_get (pix, "width", &width, "height", &height, NULL);
223 
224 	size = g_strdup_printf (_("%d x %d"), width, height);
225 	gtk_label_set_text (GTK_LABEL (w), size);
226 	g_free (size);
227 }
228 
229 /************************************************************************/
230 
231 static void
cb_outline_dash_type_changed(GOSelector * selector,StylePrefState const * state)232 cb_outline_dash_type_changed (GOSelector *selector, StylePrefState const *state)
233 {
234 	GOStyleLine *line = &state->style->line;
235 
236 	line->dash_type = go_selector_get_active (selector, &line->auto_dash);
237 	set_style (state);
238 }
239 
240 static void
cb_outline_size_changed(GtkAdjustment * adj,StylePrefState * state)241 cb_outline_size_changed (GtkAdjustment *adj, StylePrefState *state)
242 {
243 	GOStyle *style = state->style;
244 
245 	g_return_if_fail (style != NULL);
246 
247 	style->line.width = go_rint (gtk_adjustment_get_value (adj) * 100.) / 100.;
248 	style->line.auto_width = style->line.width == state->default_style->line.width;
249 	set_style (state);
250 }
251 
252 static void
cb_outline_color_changed(GOSelector * selector,StylePrefState * state)253 cb_outline_color_changed (GOSelector *selector,
254 			  StylePrefState *state)
255 {
256 	GOStyle *style = state->style;
257 
258 	g_return_if_fail (style != NULL);
259 
260 	style->line.color = go_color_selector_get_color (selector,
261 							 &style->line.auto_color);
262 	set_style (state);
263 }
264 
265 static void
outline_init(StylePrefState * state,gboolean enable,GOEditor * editor)266 outline_init (StylePrefState *state, gboolean enable, GOEditor *editor)
267 {
268 	GOStyle *style = state->style;
269 	GOStyle *default_style = state->default_style;
270 	GtkWidget *w, *grid;
271 
272 	grid = go_gtk_builder_get_widget (state->gui, "outline-grid");
273 	if (!enable) {
274 		gtk_widget_hide (grid);
275 		w = go_gtk_builder_get_widget (state->gui, "outline-label");
276 		gtk_widget_hide (w);
277 		return;
278 	}
279 
280 	go_editor_register_widget (editor, grid);
281 
282 	/* DashType */
283 	w = go_selector_new_line_dash (style->line.dash_type,
284 				       default_style->line.dash_type);
285 	gtk_widget_set_halign (w, GTK_ALIGN_START);
286 	gtk_grid_attach (GTK_GRID (grid), w, 1, 0, 2, 1);
287 	g_signal_connect (G_OBJECT (w), "activate",
288 			  G_CALLBACK (cb_outline_dash_type_changed), state);
289 	/* Size */
290 	w = go_gtk_builder_get_widget (state->gui, "outline_size_spin");
291 	gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), style->line.width);
292 	g_signal_connect (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)),
293 		"value_changed",
294 		G_CALLBACK (cb_outline_size_changed), state);
295 	/* Color */
296 	state->line.color = w = create_go_combo_color (state,
297 		style->line.color, default_style->line.color,
298 		state->gui,
299 		"outline_color", "outline_color_label",
300 		G_CALLBACK (cb_outline_color_changed));
301 	gtk_grid_attach (GTK_GRID (grid), w, 1, 1, 1, 1);
302 	gtk_widget_show_all (grid);
303 }
304 
305 
306 /************************************************************************/
307 
308 static void
cb_line_dash_type_changed(GOSelector * palette,StylePrefState const * state)309 cb_line_dash_type_changed (GOSelector *palette, StylePrefState const *state)
310 {
311 	GOStyleLine *line = &state->style->line;
312 
313 	line->dash_type = go_selector_get_active (palette, &line->auto_dash);
314 	set_style (state);
315 }
316 
317 static void
cb_line_size_changed(GtkAdjustment * adj,StylePrefState const * state)318 cb_line_size_changed (GtkAdjustment *adj, StylePrefState const *state)
319 {
320 	GOStyle *style = state->style;
321 
322 	g_return_if_fail (style != NULL);
323 
324 	style->line.width = go_rint (gtk_adjustment_get_value (adj) * 100.) / 100.;
325 	style->line.auto_width = style->line.width == state->default_style->line.width;
326 	set_style (state);
327 }
328 
329 static void
cb_line_color_changed(GOSelector * selector,StylePrefState * state)330 cb_line_color_changed (GOSelector *selector,
331 		       StylePrefState *state)
332 {
333 	GOStyle *style = state->style;
334 
335 	g_return_if_fail (style != NULL);
336 
337 	style->line.color = go_color_selector_get_color (selector, &style->line.auto_color);
338 	set_style (state);
339 }
340 
341 static void
line_init(StylePrefState * state,gboolean enable,GOEditor * editor)342 line_init (StylePrefState *state, gboolean enable, GOEditor *editor)
343 {
344 	GOStyle *style = state->style;
345 	GOStyle *default_style = state->default_style;
346 	GtkWidget *w, *grid;
347 
348 	grid = go_gtk_builder_get_widget (state->gui, "line-grid");
349 	if (!enable) {
350 		gtk_widget_hide (grid);
351 		w = go_gtk_builder_get_widget (state->gui, "line-label");
352 		gtk_widget_hide (w);
353 		return;
354 	}
355 
356 	go_editor_register_widget (editor, grid);
357 
358 	/* DashType */
359 	w = go_selector_new_line_dash (style->line.dash_type,
360 				       default_style->line.dash_type);
361 	gtk_widget_set_halign (w, GTK_ALIGN_START);
362 	gtk_grid_attach (GTK_GRID (grid), w, 1, 0, 2, 1);
363 	g_signal_connect (G_OBJECT (w), "activate",
364 			  G_CALLBACK (cb_line_dash_type_changed), state);
365 
366 	/* Size */
367 	w = go_gtk_builder_get_widget (state->gui, "line_size_spin");
368 	gtk_spin_button_set_value (GTK_SPIN_BUTTON (w), style->line.width);
369 	g_signal_connect (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)),
370 		"value_changed",
371 		G_CALLBACK (cb_line_size_changed), state);
372 
373 	/* Colour */
374 	state->line.color = w = create_go_combo_color (state,
375 		style->line.color, default_style->line.color,
376 		state->gui,
377 		"line_color", "line_color_label",
378 		G_CALLBACK (cb_line_color_changed));
379 	gtk_grid_attach (GTK_GRID (grid), w, 1, 1, 1, 1);
380 	gtk_widget_show_all (grid);
381 }
382 
383 static void cb_fill_background_color (GOSelector *selector, StylePrefState *state);
384 static void cb_fill_foreground_color (GOSelector *selector, StylePrefState *state);
385 
386 static void
fill_update_selectors(StylePrefState const * state)387 fill_update_selectors (StylePrefState const *state)
388 {
389 	GOStyle *style = state->style;
390 
391 	go_pattern_selector_set_colors (GO_SELECTOR (state->fill.pattern.selector),
392 				        style->fill.pattern.fore,
393 					style->fill.pattern.back);
394 	go_gradient_selector_set_colors (GO_SELECTOR (state->fill.gradient.selector),
395 					 style->fill.pattern.back,
396 					 style->fill.pattern.fore);
397 
398 	g_signal_handlers_block_by_func (state->fill.background, cb_fill_background_color,
399 					 (gpointer) state);
400 	g_signal_handlers_block_by_func (state->fill.foreground, cb_fill_background_color,
401 					 (gpointer) state);
402 
403 	go_color_selector_set_color (GO_SELECTOR (state->fill.background),
404 				     style->fill.pattern.back);
405 	go_color_selector_set_color (GO_SELECTOR (state->fill.foreground),
406 				     style->fill.pattern.fore);
407 
408 	g_signal_handlers_unblock_by_func (state->fill.background, cb_fill_background_color,
409 					   (gpointer) state);
410 	g_signal_handlers_unblock_by_func (state->fill.foreground, cb_fill_background_color,
411 					   (gpointer) state);
412 }
413 
414 /************************************************************************/
415 
416 static void
cb_pattern_type_activate(GtkWidget * selector,StylePrefState const * state)417 cb_pattern_type_activate (GtkWidget *selector, StylePrefState const *state)
418 {
419 	GOStyle *style = state->style;
420 
421 	style->fill.pattern.pattern = go_selector_get_active (GO_SELECTOR (selector), &style->fill.auto_pattern);
422 	set_style (state);
423 }
424 
425 static void
fill_pattern_init(StylePrefState * state)426 fill_pattern_init (StylePrefState *state)
427 {
428 	GOStyle *style = state->style;
429 	GOStyle *default_style = state->default_style;
430 	GtkWidget *selector;
431 	GtkWidget *label;
432 
433 	state->fill.pattern.selector = selector =
434 		go_pattern_selector_new (style->fill.pattern.pattern,
435 					 default_style->fill.pattern.pattern);
436 	go_pattern_selector_set_colors (GO_SELECTOR (selector), style->fill.pattern.fore,
437 					style->fill.pattern.back);
438 
439 	label = go_gtk_builder_get_widget (state->gui, "fill_pattern_label");
440 	gtk_size_group_add_widget (state->fill.size_group, label);
441 	gtk_label_set_mnemonic_widget (GTK_LABEL (label), selector);
442 	state->fill.pattern.box = go_gtk_builder_get_widget (state->gui, "fill_pattern_box");
443 	gtk_box_pack_start (GTK_BOX (state->fill.pattern.box), selector, FALSE, FALSE, 0);
444 
445 	g_signal_connect (G_OBJECT (selector), "activate",
446 			  G_CALLBACK (cb_pattern_type_activate), state);
447 	gtk_widget_show (selector);
448 }
449 
450 /************************************************************************/
451 
452 static void
cb_brightness_changed(GtkRange * range,StylePrefState * state)453 cb_brightness_changed (GtkRange *range, StylePrefState *state)
454 {
455 	GOStyle *style = state->style;
456 
457 	go_style_set_fill_brightness (style, gtk_range_get_value (range));
458 	style->fill.gradient.auto_brightness = state->default_style->fill.gradient.auto_brightness;
459 	set_style (state);
460 	fill_update_selectors (state);
461 }
462 
463 static void
cb_gradient_type_changed(GOSelector * selector,StylePrefState const * state)464 cb_gradient_type_changed (GOSelector *selector, StylePrefState const *state)
465 {
466 	GOStyle *style = state->style;
467 	style->fill.gradient.dir = go_selector_get_active (selector, &style->fill.gradient.auto_dir);
468 	set_style (state);
469 }
470 
471 static void
fill_gradient_init(StylePrefState * state)472 fill_gradient_init (StylePrefState *state)
473 {
474 	GOStyle *style = state->style;
475 	GtkWidget *selector;
476 	GtkWidget *brightness;
477 	GtkWidget *label;
478 
479 	state->fill.gradient.selector = selector =
480 		go_selector_new_gradient (style->fill.gradient.dir,
481 					  style->fill.gradient.dir);
482 	go_gradient_selector_set_colors (GO_SELECTOR (selector),
483 					 style->fill.pattern.back,
484 					 style->fill.pattern.fore);
485 	label = go_gtk_builder_get_widget (state->gui, "fill_gradient_label");
486 	gtk_size_group_add_widget (state->fill.size_group, label);
487 	gtk_label_set_mnemonic_widget (GTK_LABEL (label), selector);
488 	state->fill.gradient.box = go_gtk_builder_get_widget (state->gui, "fill_gradient_box");
489 	gtk_box_pack_start (GTK_BOX (state->fill.gradient.box), selector, FALSE, FALSE, 0);
490 
491 	state->fill.gradient.brightness = brightness =
492 		go_gtk_builder_get_widget (state->gui, "fill_brightness_scale");
493 	gtk_range_set_value (GTK_RANGE (brightness), state->style->fill.gradient.brightness);
494 	label = go_gtk_builder_get_widget (state->gui, "fill_brightness_label");
495 	gtk_size_group_add_widget (state->fill.size_group, label);
496 	gtk_label_set_mnemonic_widget (GTK_LABEL (label), brightness);
497 	state->fill.gradient.brightness_box = go_gtk_builder_get_widget (state->gui, "fill_brightness_box");
498 
499 	g_signal_connect (selector, "activate",
500 			  G_CALLBACK (cb_gradient_type_changed), state);
501 	g_signal_connect (brightness, "value_changed",
502 			   G_CALLBACK (cb_brightness_changed), state);
503 	gtk_widget_show (selector);
504 	gtk_widget_show (brightness);
505 }
506 
507 /************************************************************************/
508 
509 static void
cb_fill_background_color(GOSelector * selector,StylePrefState * state)510 cb_fill_background_color (GOSelector *selector, StylePrefState *state)
511 {
512 	GOStyle *style = state->style;
513 
514 	style->fill.pattern.back = go_color_selector_get_color (selector,
515 								&style->fill.auto_back);
516 	set_style (state);
517 	fill_update_selectors (state);
518 }
519 
520 static void
cb_fill_foreground_color(GOSelector * selector,StylePrefState * state)521 cb_fill_foreground_color (GOSelector *selector, StylePrefState *state)
522 {
523 	GOStyle *style = state->style;
524 
525 	style->fill.pattern.fore = go_color_selector_get_color (selector,
526 								&style->fill.auto_fore);
527 	style->fill.gradient.brightness = -1;
528 	set_style (state);
529 	fill_update_selectors (state);
530 }
531 
532 static void
fill_color_init(StylePrefState * state)533 fill_color_init (StylePrefState *state)
534 {
535 	GOStyle *style = state->style;
536 	GOStyle *default_style = state->default_style;
537 	GtkWidget *w;
538 
539 	state->fill.background = w = create_go_combo_color (state,
540 		style->fill.pattern.back,
541 		default_style->fill.pattern.back,
542 		state->gui, "pattern_background", "fill_background_label",
543 		G_CALLBACK (cb_fill_background_color));
544 	state->fill.background_box = go_gtk_builder_get_widget (state->gui, "fill_background_box");
545 	gtk_box_pack_start (GTK_BOX (state->fill.background_box), w, FALSE, FALSE, 0);
546 	gtk_widget_show (w);
547 
548 	state->fill.foreground = w = create_go_combo_color (state,
549 		style->fill.pattern.fore,
550 		default_style->fill.pattern.fore,
551 		state->gui, "pattern_foreground", "fill_foreground_label",
552 		G_CALLBACK (cb_fill_foreground_color));
553 	state->fill.foreground_box = go_gtk_builder_get_widget (state->gui, "fill_foreground_box");
554 	gtk_box_pack_start (GTK_BOX (state->fill.foreground_box), w, FALSE, FALSE, 0);
555 	gtk_widget_show (w);
556 
557 	state->fill.foreground_label = go_gtk_builder_get_widget (state->gui, "fill_foreground_label");
558 	gtk_size_group_add_widget (state->fill.size_group, state->fill.foreground_label);
559 	state->fill.background_label = go_gtk_builder_get_widget (state->gui, "fill_background_label");
560 	gtk_size_group_add_widget (state->fill.size_group, state->fill.background_label);
561 }
562 /************************************************************************/
563 
564 static void
cb_image_select(GtkWidget * cc,StylePrefState * state)565 cb_image_select (GtkWidget *cc, StylePrefState *state)
566 {
567 	GOStyle *style = state->style;
568 	GtkWidget *w;
569 
570 	g_return_if_fail (style != NULL);
571 	g_return_if_fail (GO_STYLE_FILL_IMAGE == style->fill.type);
572 
573 	w = go_image_sel_new (state->doc, NULL, &style->fill.image.image);
574 	gtk_window_set_transient_for (GTK_WINDOW (w), GTK_WINDOW (gtk_widget_get_toplevel (cc)));
575 	/* we need to call gtk_run_dialog until it returns a non NULL response */
576 	while (! gtk_dialog_run (GTK_DIALOG (w)));
577 
578 	go_style_set_image_preview (style->fill.image.image, state);
579 	set_style (state);
580 }
581 
582 static void
cb_image_style_changed(GtkWidget * w,StylePrefState * state)583 cb_image_style_changed (GtkWidget *w, StylePrefState *state)
584 {
585 	GOStyle *style = state->style;
586 	g_return_if_fail (style != NULL);
587 	g_return_if_fail (GO_STYLE_FILL_IMAGE == style->fill.type);
588 	style->fill.image.type = gtk_combo_box_get_active (GTK_COMBO_BOX (w));
589 	set_style (state);
590 }
591 
592 static void
fill_image_init(StylePrefState * state)593 fill_image_init (StylePrefState *state)
594 {
595 	GtkWidget *w, *sample, *type;
596 	GOStyle *style = state->style;
597 
598 	w = go_gtk_builder_get_widget (state->gui, "fill_image_select_picture");
599 	g_signal_connect (G_OBJECT (w),
600 		"clicked",
601 		G_CALLBACK (cb_image_select), state);
602 
603 	sample = go_gtk_builder_get_widget (state->gui, "fill_image_sample");
604 	gtk_widget_set_size_request (sample, HSCALE + 10, VSCALE + 10);
605 	type   = go_gtk_builder_get_widget (state->gui, "fill-image-fit");
606 
607 	state->fill.image.image = NULL;
608 	go_style_set_image_preview (style->fill.image.image, state);
609 
610 	if (GO_STYLE_FILL_IMAGE == style->fill.type) {
611 		gtk_combo_box_set_active (GTK_COMBO_BOX (type),
612 			style->fill.image.type);
613 		state->fill.image.image = style->fill.image.image;
614 		if (state->fill.image.image)
615 			g_object_ref (state->fill.image.image);
616 	} else
617 		gtk_combo_box_set_active (GTK_COMBO_BOX (type), 0);
618 	g_signal_connect (G_OBJECT (type),
619 		"changed",
620 		G_CALLBACK (cb_image_style_changed), state);
621 }
622 
623 /************************************************************************/
624 
625 typedef enum {
626 	FILL_TYPE_NONE,
627 	FILL_TYPE_PATTERN,
628 	FILL_TYPE_GRADIENT_BICOLOR,
629 	FILL_TYPE_GRADIENT_UNICOLOR,
630 	FILL_TYPE_IMAGE
631 } FillType;
632 
633 static struct {
634 	GOStyleFill 	type;
635 	int		page;
636 	gboolean	show_pattern;
637 	gboolean	show_gradient;
638 	gboolean	show_brightness;
639 } fill_infos[] = {
640 	{GO_STYLE_FILL_NONE,		0, FALSE, FALSE, FALSE},
641 	{GO_STYLE_FILL_PATTERN,	1, TRUE,  FALSE, FALSE},
642 	{GO_STYLE_FILL_GRADIENT,	1, FALSE, TRUE,  FALSE},
643 	{GO_STYLE_FILL_GRADIENT,	1, FALSE, TRUE,  TRUE},
644 	{GO_STYLE_FILL_IMAGE,		2, FALSE, FALSE, FALSE}
645 };
646 
647 static void
fill_update_visibilies(FillType type,StylePrefState * state)648 fill_update_visibilies (FillType type, StylePrefState *state)
649 {
650 	g_object_set (state->fill.pattern.box, "visible" , fill_infos[type].show_pattern, NULL);
651 	g_object_set (state->fill.gradient.box, "visible", fill_infos[type].show_gradient, NULL);
652 	g_object_set (state->fill.gradient.brightness_box, "visible",
653 		      fill_infos[type].show_brightness, NULL);
654 	g_object_set (state->fill.foreground_box, "visible",
655 		      !fill_infos[type].show_brightness, NULL);
656 
657 	if (fill_infos[type].show_gradient) {
658 		gtk_label_set_text (GTK_LABEL (state->fill.foreground_label), _("Start:"));
659 		gtk_label_set_text (GTK_LABEL (state->fill.background_label), _("End:"));
660 	} else {
661 		gtk_label_set_text (GTK_LABEL (state->fill.foreground_label), _("Foreground:"));
662 		gtk_label_set_text (GTK_LABEL (state->fill.background_label), _("Background:"));
663 	}
664 
665 	gtk_notebook_set_current_page (GTK_NOTEBOOK (state->fill.notebook), fill_infos[type].page);
666 }
667 
668 static void
cb_fill_type_changed(GtkWidget * menu,StylePrefState * state)669 cb_fill_type_changed (GtkWidget *menu, StylePrefState *state)
670 {
671 	int index;
672 
673 	index = CLAMP (gtk_combo_box_get_active (GTK_COMBO_BOX (menu)), 0, (int) G_N_ELEMENTS (fill_infos) - 1);
674 
675 	if (state->style->fill.type == GO_STYLE_FILL_GRADIENT)
676 	/* we need to set brightness to 0 because of unicolor gradients recognition */
677 		gtk_range_set_value (GTK_RANGE (state->fill.gradient.brightness), 0.);
678 	state->style->fill.type = fill_infos[index].type;
679 	state->style->fill.auto_type = state->style->fill.type == state->default_style->fill.type;
680 	set_style (state);
681 
682 	fill_update_visibilies (index, state);
683 }
684 
685 static void
fill_init(StylePrefState * state,gboolean enable,GOEditor * editor)686 fill_init (StylePrefState *state, gboolean enable, GOEditor *editor)
687 {
688 	GtkWidget *w;
689 	FillType type;
690 
691 	if (!enable) {
692 		gtk_widget_hide (go_gtk_builder_get_widget (state->gui, "fill-grid"));
693 		gtk_widget_hide (go_gtk_builder_get_widget (state->gui, "fill-label"));
694 		return;
695 	}
696 
697 	state->fill.size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
698 
699 	fill_color_init (state);
700 	fill_pattern_init (state);
701 	fill_gradient_init (state);
702 	if (state->doc != NULL)
703 		fill_image_init (state);
704 	fill_update_selectors (state);
705 
706 	state->fill.notebook = go_gtk_builder_get_widget (state->gui, "fill-notebook");
707 
708 	switch (state->style->fill.type) {
709 		case GO_STYLE_FILL_PATTERN:
710 			type = FILL_TYPE_PATTERN;
711 			break;
712 		case GO_STYLE_FILL_GRADIENT:
713 			if (state->style->fill.gradient.brightness >= 0)
714 				type = FILL_TYPE_GRADIENT_UNICOLOR;
715 			else
716 				type = FILL_TYPE_GRADIENT_BICOLOR;
717 			break;
718 		case GO_STYLE_FILL_IMAGE:
719 			if (state->doc != NULL) {
720 				type = FILL_TYPE_IMAGE;
721 				break;
722 			} else
723 				state->style->fill.type = GO_STYLE_FILL_NONE;
724 		case GO_STYLE_FILL_NONE:
725 		default:
726 			type = FILL_TYPE_NONE;
727 			break;
728 	}
729 	fill_update_visibilies (type, state);
730 
731 	w = go_gtk_builder_get_widget (state->gui, "fill-type-menu");
732 	if (state->doc == NULL)
733 		gtk_combo_box_text_remove (GTK_COMBO_BOX_TEXT (w), FILL_TYPE_IMAGE);
734 	gtk_combo_box_set_active (GTK_COMBO_BOX (w), type);
735 	g_signal_connect (G_OBJECT (w),
736 		"changed",
737 		G_CALLBACK (cb_fill_type_changed), state);
738 
739 	w = go_gtk_builder_get_widget (state->gui, "fill-grid");
740 	go_editor_register_widget (editor, w);
741 	gtk_widget_show (GTK_WIDGET (w));
742 
743 	g_object_unref (state->fill.size_group);
744 	state->fill.size_group = NULL;
745 }
746 
747 /************************************************************************/
748 
749 static void
cb_marker_shape_changed(GOSelector * selector,StylePrefState const * state)750 cb_marker_shape_changed (GOSelector *selector, StylePrefState const *state)
751 {
752 	GOStyle *style = state->style;
753 	GOMarkerShape shape;
754 	gboolean is_auto;
755 
756 	shape = go_selector_get_active (selector, &is_auto);
757 	go_marker_set_shape (style->marker.mark, shape);
758 	style->marker.auto_shape = is_auto;
759 	set_style (state);
760 }
761 
762 static void
cb_marker_outline_color_changed(GOSelector * selector,StylePrefState * state)763 cb_marker_outline_color_changed (GOSelector *selector,
764 				 StylePrefState *state)
765 {
766 	GOStyle *style = state->style;
767 	GOColor color;
768 	gboolean is_auto;
769 
770 	color = go_color_selector_get_color (selector, &is_auto);
771 	go_marker_set_outline_color (style->marker.mark, color);
772 	style->marker.auto_outline_color = is_auto;
773 	set_style (state);
774 
775 	go_marker_selector_set_colors (GO_SELECTOR (state->marker.selector),
776 				       color,
777 				       go_marker_get_fill_color (style->marker.mark));
778 }
779 
780 static void
cb_marker_fill_color_changed(GOSelector * selector,StylePrefState * state)781 cb_marker_fill_color_changed (GOSelector *selector,
782 			      StylePrefState *state)
783 {
784 	GOStyle *style = state->style;
785 	GOColor color;
786 	gboolean is_auto;
787 
788 	color = go_color_selector_get_color (selector, &is_auto);
789 	go_marker_set_fill_color (style->marker.mark, color);
790 	style->marker.auto_fill_color = is_auto;
791 	go_marker_selector_set_auto_fill (GO_SELECTOR (state->marker.selector), is_auto);
792 	set_style (state);
793 
794 	go_marker_selector_set_colors (GO_SELECTOR (state->marker.selector),
795 				       go_marker_get_outline_color (style->marker.mark),
796 				       color);
797 }
798 
799 static void
cb_marker_size_changed(GtkAdjustment * adj,StylePrefState * state)800 cb_marker_size_changed (GtkAdjustment *adj, StylePrefState *state)
801 {
802 	go_marker_set_size (state->style->marker.mark, gtk_adjustment_get_value (adj));
803 	set_style (state);
804 }
805 
806 static void go_style_pref_state_free (StylePrefState *state);
807 
808 static void
marker_init(StylePrefState * state,gboolean enable,GOEditor * editor,GOCmdContext * cc)809 marker_init (StylePrefState *state, gboolean enable, GOEditor *editor, GOCmdContext *cc)
810 {
811 	GOStyle *style = state->style;
812 	GOStyle *default_style = state->default_style;
813 	GtkWidget *grid, *w;
814 	GtkWidget *selector;
815 	GtkBuilder *gui;
816 
817 	if (!enable)
818 		return;
819 
820 	gui = go_gtk_builder_load_internal ("res:go:utils/go-style-prefs.ui", GETTEXT_PACKAGE, cc);
821 	if (gui == NULL)
822 		return;
823 
824 	grid = go_gtk_builder_get_widget (gui, "go-style-marker-prefs");
825 
826 	state->marker.selector = selector =
827 		go_marker_selector_new (go_marker_get_shape (style->marker.mark),
828 					go_marker_get_shape (state->default_style->marker.mark));
829 	if ((style->interesting_fields & GO_STYLE_MARKER_NO_COLOR )== 0)
830 		go_marker_selector_set_colors (GO_SELECTOR (selector),
831 					       go_marker_get_outline_color (style->marker.mark),
832 					       go_marker_get_fill_color (style->marker.mark));
833 	else
834 		go_marker_selector_set_colors (GO_SELECTOR (selector),
835 					       GO_COLOR_BLUE, GO_COLOR_BLUE);
836 	go_marker_selector_set_auto_fill (GO_SELECTOR (selector), style->marker.auto_fill_color);
837 	w = go_gtk_builder_get_widget (gui, "marker_shape_label");
838 	gtk_label_set_mnemonic_widget (GTK_LABEL (w), selector);
839 	gtk_grid_attach (GTK_GRID (grid), selector, 1, 1, 1, 1);
840 	g_signal_connect (G_OBJECT (selector), "activate",
841 			  G_CALLBACK (cb_marker_shape_changed), state);
842 	gtk_widget_show (selector);
843 
844 	if ((style->interesting_fields & GO_STYLE_MARKER_NO_COLOR ) == 0)
845 		state->marker.fill = w = create_go_combo_color (state,
846 			go_marker_get_fill_color (style->marker.mark),
847 			go_marker_get_fill_color (default_style->marker.mark),
848 			gui, "pattern_background", "marker_fill_label",
849 			G_CALLBACK (cb_marker_fill_color_changed));
850 	else {
851 		state->marker.fill = w = create_go_combo_color (state,
852 			GO_COLOR_BLUE, GO_COLOR_BLUE,
853 			gui, "pattern_background", "marker_fill_label",
854 			G_CALLBACK (cb_marker_fill_color_changed));
855 		gtk_widget_set_sensitive (w, FALSE);
856 	}
857 	gtk_grid_attach (GTK_GRID (grid), w, 1, 2, 1, 1);
858 
859 	if ((style->interesting_fields & GO_STYLE_MARKER_NO_COLOR ) == 0)
860 		state->marker.outline = w = create_go_combo_color (state,
861 			go_marker_get_outline_color (style->marker.mark),
862 			go_marker_get_outline_color (default_style->marker.mark),
863 			gui, "pattern_foreground", "marker_outline_label",
864 			G_CALLBACK (cb_marker_outline_color_changed));
865 	else {
866 		state->marker.outline = w = create_go_combo_color (state,
867 			GO_COLOR_BLUE, GO_COLOR_BLUE,
868 			gui, "pattern_background", "marker_fill_label",
869 			G_CALLBACK (cb_marker_outline_color_changed));
870 		gtk_widget_set_sensitive (w, FALSE);
871 	}
872 	gtk_grid_attach (GTK_GRID (grid), w, 1, 3, 1, 1);
873 
874 	w = go_gtk_builder_get_widget (gui, "marker_size_spin");
875 	gtk_spin_button_set_value (GTK_SPIN_BUTTON (w),
876 		go_marker_get_size (style->marker.mark));
877 	g_signal_connect (G_OBJECT (gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w))),
878 		"value_changed",
879 		G_CALLBACK (cb_marker_size_changed), state);
880 
881 	gtk_widget_show_all (grid);
882 
883 	go_editor_add_page (editor, g_object_ref (grid), _("Markers"));
884 	g_object_unref (gui);
885 	if (state->gui == NULL) {
886 		g_object_set_data (G_OBJECT (w), "state", state);
887 		g_signal_connect_swapped (G_OBJECT (w), "destroy", G_CALLBACK (go_style_pref_state_free), state);
888 	}
889 }
890 
891 /************************************************************************/
892 
893 static void
cb_font_changed(GOFontSel * fs,PangoAttrList * list,StylePrefState * state)894 cb_font_changed (GOFontSel *fs, PangoAttrList *list,
895 		 StylePrefState *state)
896 {
897 	PangoAttrIterator *iter = pango_attr_list_get_iterator (list);
898 	PangoFontDescription *desc = pango_font_description_new ();
899 	GSList *extra_attrs;
900 	const GOFont *font;
901 	pango_attr_iterator_get_font (iter, desc, NULL, &extra_attrs);
902 	font = go_font_new_by_desc (desc);
903 /* FIXME FIXME FIXME we should do something for extra attributes */
904 	g_slist_foreach (extra_attrs, (GFunc)pango_attribute_destroy, NULL);
905 	g_slist_free (extra_attrs);
906 	pango_attr_iterator_destroy (iter);
907 	state->style->font.auto_font = (font == state->default_style->font.font);
908 	go_style_set_font (state->style, font);
909 	set_style (state);
910 }
911 
912 static void
cb_font_color_changed(GOSelector * selector,StylePrefState * state)913 cb_font_color_changed (GOSelector *selector,
914 		       StylePrefState *state)
915 {
916 	GOStyle *style = state->style;
917 
918 	style->font.color = go_color_selector_get_color (selector, NULL);
919 	style->font.auto_color = (style->font.color == state->default_style->font.color);
920 	set_style (state);
921 }
922 
923 static void
font_init(StylePrefState * state,guint32 enable,GOEditor * editor,GOCmdContext * cc)924 font_init (StylePrefState *state, guint32 enable, GOEditor *editor, GOCmdContext *cc)
925 {
926 	GOStyle *style = state->style;
927 	GtkWidget *w, *grid;
928 	GtkBuilder *gui;
929 
930 	if (!enable)
931 		return;
932 
933 	g_return_if_fail (style->font.font != NULL);
934 
935 	gui = go_gtk_builder_load_internal ("res:go:utils/go-style-prefs.ui", GETTEXT_PACKAGE, cc);
936 	if (gui == NULL)
937 		return;
938 
939 	state->font_gui = gui;
940 
941 	w = create_go_combo_color (state, style->font.color, style->font.color,
942 				   gui, "pattern_foreground", "font_color_label",
943 				   G_CALLBACK (cb_font_color_changed));
944 	grid = go_gtk_builder_get_widget (gui, "go-style-font-prefs");
945 	gtk_grid_attach (GTK_GRID (grid), w, 1, 0, 1, 1);
946 	gtk_widget_show (w);
947 
948 	w = GTK_WIDGET (g_object_new (GO_TYPE_FONT_SEL, "show-style", TRUE, NULL));
949 	go_font_sel_set_font (GO_FONT_SEL (w), style->font.font);
950 	g_signal_connect (G_OBJECT (w), "font_changed",
951 			  G_CALLBACK (cb_font_changed), state);
952 	gtk_widget_set_vexpand (w, TRUE);
953 	gtk_widget_show (w);
954 
955  	gtk_grid_attach (GTK_GRID (grid), w, 0, 1, 3, 1);
956 
957 	go_editor_add_page (editor, grid, _("Font"));
958 }
959 
960 /************************************************************************/
961 
962 static void
cb_angle_changed(GORotationSel * grs,int angle,StylePrefState * state)963 cb_angle_changed (GORotationSel *grs, int angle, StylePrefState *state)
964 {
965 	go_style_set_text_angle (state->style, angle);
966 	state->style->text_layout.auto_angle = state->style->text_layout.angle == state->default_style->text_layout.angle;
967 	set_style (state);
968 }
969 
970 static void
text_layout_init(StylePrefState * state,guint32 enable,GOEditor * editor,GOCmdContext * cc)971 text_layout_init (StylePrefState *state, guint32 enable, GOEditor *editor, GOCmdContext *cc)
972 {
973 	GOStyle *style = state->style;
974 	GtkWidget *w, *box;
975 
976 	if (!enable)
977 		return;
978 
979 	w = go_rotation_sel_new_full ();
980 	go_rotation_sel_set_rotation (GO_ROTATION_SEL (w),
981 		style->text_layout.angle);
982 	g_signal_connect (G_OBJECT (w), "rotation-changed",
983 		G_CALLBACK (cb_angle_changed), state);
984 	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
985 	gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0);
986 	gtk_container_set_border_width (GTK_CONTAINER (box), 12);
987 	gtk_widget_show_all (box);
988 	go_editor_add_page (editor, box, _("Text"));
989 }
990 
991 /************************************************************************/
992 
993 static void
cb_parent_is_gone(StylePrefState * state,GObject * where_the_object_was)994 cb_parent_is_gone (StylePrefState *state, GObject *where_the_object_was)
995 {
996 	state->style_changed_handler = 0;
997 	state->object_with_style = NULL;
998 }
999 
1000 static void
go_style_pref_state_free(StylePrefState * state)1001 go_style_pref_state_free (StylePrefState *state)
1002 {
1003 	if (state->style_changed_handler) {
1004 		g_signal_handler_disconnect (state->object_with_style,
1005 			state->style_changed_handler);
1006 		g_object_weak_unref (G_OBJECT (state->object_with_style),
1007 			(GWeakNotify) cb_parent_is_gone, state);
1008 	}
1009 	g_object_unref (state->style);
1010 	g_object_unref (state->default_style);
1011 	if (state->gui)
1012 		g_object_unref (state->gui);
1013 	if (state->doc)
1014 		g_object_unref (state->doc);
1015 	if (state->font_gui != NULL)
1016 		g_object_unref (state->font_gui);
1017 	if (state->fill.image.image != NULL)
1018 		g_object_unref (state->fill.image.image);
1019 	g_free (state);
1020 }
1021 
1022 static void
cb_style_changed(GOStyledObject * obj,GOStyle * style,StylePrefState * state)1023 cb_style_changed (GOStyledObject *obj, GOStyle *style, StylePrefState *state)
1024 {
1025 	if (style->interesting_fields & GO_STYLE_FILL)
1026 		fill_update_selectors (state);
1027 	if (style->interesting_fields & GO_STYLE_LINE) {
1028 		g_signal_handlers_block_by_func (state->line.color, cb_line_color_changed,
1029 						 (gpointer) state);
1030 		go_color_selector_set_color (GO_SELECTOR (state->line.color),
1031 					     style->line.color);
1032 		g_signal_handlers_unblock_by_func (state->line.color, cb_line_color_changed,
1033 						 (gpointer) state);
1034 	}
1035 	if (style->interesting_fields & GO_STYLE_OUTLINE) {
1036 		g_signal_handlers_block_by_func (state->line.color, cb_outline_color_changed,
1037 						 (gpointer) state);
1038 		go_color_selector_set_color (GO_SELECTOR (state->line.color),
1039 					     style->line.color);
1040 		g_signal_handlers_unblock_by_func (state->line.color, cb_outline_color_changed,
1041 						 (gpointer) state);
1042 	}
1043 	if (style->interesting_fields & GO_STYLE_MARKER) {
1044 		g_signal_handlers_block_by_func (state->marker.selector, cb_marker_shape_changed,
1045 						 (gpointer) state);
1046 		go_marker_selector_set_shape (GO_SELECTOR (state->marker.selector),
1047 					       go_marker_get_shape (style->marker.mark));
1048 		if ((style->interesting_fields & GO_STYLE_MARKER_NO_COLOR )== 0)
1049 			go_marker_selector_set_colors (GO_SELECTOR (state->marker.selector),
1050 						       go_marker_get_outline_color (style->marker.mark),
1051 						       go_marker_get_fill_color (style->marker.mark));
1052 		else
1053 			go_marker_selector_set_colors (GO_SELECTOR (state->marker.selector),
1054 						       GO_COLOR_BLUE, GO_COLOR_BLUE);
1055 		g_signal_handlers_unblock_by_func (state->marker.selector, cb_marker_shape_changed,
1056 						 (gpointer) state);
1057 		g_signal_handlers_block_by_func (state->marker.fill, cb_marker_fill_color_changed,
1058 						 (gpointer) state);
1059 		go_color_selector_set_color (GO_SELECTOR (state->marker.fill),
1060 					     go_marker_get_fill_color (style->marker.mark));
1061 		g_signal_handlers_unblock_by_func (state->marker.fill, cb_marker_fill_color_changed,
1062 						 (gpointer) state);
1063 		g_signal_handlers_block_by_func (state->marker.outline, cb_marker_outline_color_changed,
1064 						 (gpointer) state);
1065 		go_color_selector_set_color (GO_SELECTOR (state->marker.outline),
1066 					     go_marker_get_outline_color (style->marker.mark));
1067 		g_signal_handlers_unblock_by_func (state->marker.outline, cb_marker_outline_color_changed,
1068 						 (gpointer) state);
1069 	}
1070 
1071 }
1072 
1073 void
go_style_populate_editor(GOStyle * style,GOEditor * editor,GOStyle * default_style,GOCmdContext * cc,GObject * object_with_style,gboolean watch_for_external_change)1074 go_style_populate_editor (GOStyle *style,
1075 			   GOEditor *editor,
1076 			   GOStyle *default_style,
1077 			   GOCmdContext *cc,
1078 			   GObject	*object_with_style,
1079 			   gboolean   watch_for_external_change)
1080 {
1081 	GOStyleFlag enable;
1082 	GtkWidget *w;
1083 	GtkBuilder *gui;
1084 	StylePrefState *state;
1085 
1086 	g_return_if_fail (style != NULL);
1087 	g_return_if_fail (default_style != NULL);
1088 
1089 	enable = style->interesting_fields;
1090 
1091 	g_object_ref (style);
1092 	g_object_ref (default_style);
1093 
1094 	state = g_new0 (StylePrefState, 1);
1095 	state->gui = NULL;
1096 	state->font_gui = NULL;
1097 	state->style = style;
1098 	state->default_style = default_style;
1099 	state->object_with_style = object_with_style;
1100 	state->enable_edit = FALSE;
1101 
1102 	if (GO_IS_STYLED_OBJECT (object_with_style))
1103 		state->doc = go_styled_object_get_document (GO_STYLED_OBJECT (object_with_style));
1104 	else {
1105 		GObjectClass *klass = G_OBJECT_GET_CLASS (object_with_style);
1106 		if (g_object_class_find_property (klass, "document") != NULL)
1107 			g_object_get (object_with_style, "document", &(state->doc), NULL);
1108 	}
1109 	if (state->doc && !GO_IS_DOC (state->doc))
1110 		state->doc = NULL;
1111 
1112 	if ((enable & (GO_STYLE_OUTLINE | GO_STYLE_LINE | GO_STYLE_FILL)) != 0) {
1113 		gui = go_gtk_builder_load_internal ("res:go:utils/go-style-prefs.ui", GETTEXT_PACKAGE, cc);
1114 		if (gui == NULL) {
1115 			g_free (state);
1116 			return;
1117 		}
1118 		state->gui = gui;
1119 		w = go_gtk_builder_get_widget (gui, "go-style-prefs");
1120 		g_object_set_data (G_OBJECT (w), "state", state);
1121 		g_signal_connect_swapped (G_OBJECT (w), "destroy", G_CALLBACK (go_style_pref_state_free), state);
1122 		go_editor_add_page (editor, w, _("Style"));
1123 
1124 		outline_init 	 (state, enable & GO_STYLE_OUTLINE, editor);
1125 		line_init    	 (state, enable & GO_STYLE_LINE, editor);
1126 		fill_init    	 (state, enable & GO_STYLE_FILL, editor);
1127 	}
1128 	marker_init  	 (state, enable & GO_STYLE_MARKER, editor, cc);
1129 	font_init    	 (state, enable & GO_STYLE_FONT, editor, cc);
1130 	text_layout_init (state, enable & GO_STYLE_TEXT_LAYOUT, editor, cc);
1131 
1132 	state->enable_edit = TRUE;
1133 
1134 	if (object_with_style != NULL && watch_for_external_change) {
1135 		state->style_changed_handler = g_signal_connect (G_OBJECT (object_with_style),
1136 			"style-changed",
1137 			G_CALLBACK (cb_style_changed), state);
1138 		g_object_weak_ref (G_OBJECT (object_with_style),
1139 			(GWeakNotify) cb_parent_is_gone, state);
1140 	}
1141 }
1142 
1143 /**
1144  * go_style_get_editor:
1145  * @style: #GOStyle
1146  * @default_style: the style used as default
1147  * @cc: #GOCmdContext
1148  * @object_with_style: the object owning the style
1149  *
1150  * Builds the widget used to edit the style.
1151  * Returns: (transfer full): the style editor
1152  **/
1153 gpointer
go_style_get_editor(GOStyle * style,GOStyle * default_style,GOCmdContext * cc,GObject * object_with_style)1154 go_style_get_editor (GOStyle *style,
1155 		     GOStyle *default_style,
1156 		     GOCmdContext *cc,
1157 		     GObject *object_with_style)
1158 {
1159 	GtkWidget *notebook;
1160 	GOEditor *editor = go_editor_new ();
1161 
1162 	go_style_populate_editor (style, editor, default_style, cc,
1163 				   object_with_style, FALSE);
1164 
1165 	notebook = go_editor_get_notebook (editor);
1166 	go_editor_free (editor);
1167 	gtk_widget_show (notebook);
1168 	return notebook;
1169 }
1170 #endif
1171 
1172 /*****************************************************************************/
1173 
1174 GOStyle *
go_style_new(void)1175 go_style_new (void)
1176 {
1177 	return g_object_new (GO_TYPE_STYLE, NULL);
1178 }
1179 
1180 /**
1181  * go_style_dup:
1182  * @style: a source #GOStyle
1183  *
1184  * Duplicates @style.
1185  *
1186  * Returns: (transfer full): a new #GOStyle
1187  **/
1188 GOStyle *
go_style_dup(GOStyle const * src)1189 go_style_dup (GOStyle const *src)
1190 {
1191 	GOStyle *dst;
1192 
1193 	g_return_val_if_fail (GO_IS_STYLE (src), NULL);
1194 
1195 	/* the source style might be a derived object, so use its type for duplication */
1196 	dst = GO_STYLE (g_object_new (G_OBJECT_TYPE (src), NULL));
1197 	go_style_assign (dst, src);
1198 	return dst;
1199 }
1200 
1201 void
go_style_assign(GOStyle * dst,GOStyle const * src)1202 go_style_assign (GOStyle *dst, GOStyle const *src)
1203 {
1204 	if (src == dst)
1205 		return;
1206 
1207 	g_return_if_fail (GO_IS_STYLE (src));
1208 	g_return_if_fail (GO_IS_STYLE (dst));
1209 	if (GO_STYLE_FILL_IMAGE == dst->fill.type) {
1210 		if (dst->fill.image.image != NULL)
1211 			g_object_unref (dst->fill.image.image);
1212 	}
1213 
1214 	if (src->font.font != NULL)
1215 		go_font_ref (src->font.font);
1216 	if (dst->font.font != NULL)
1217 		go_font_unref (dst->font.font);
1218 
1219 	dst->fill    = src->fill;
1220 	dst->line    = src->line;
1221 	if (dst->marker.mark)
1222 		g_object_unref (dst->marker.mark);
1223 	dst->marker = src->marker;
1224 	dst->marker.mark = go_marker_dup (src->marker.mark);
1225 	dst->font    = src->font;
1226 
1227 	if (GO_STYLE_FILL_IMAGE == dst->fill.type && src->fill.image.image)
1228 		dst->fill.image.image = g_object_ref (src->fill.image.image);
1229 
1230 	dst->text_layout = src->text_layout;
1231 
1232 	dst->interesting_fields = src->interesting_fields;
1233 	dst->disable_theming = src->disable_theming;
1234 }
1235 
1236 /**
1237  * go_style_apply_theme:
1238  * @dst: #GOStyle
1239  * @src:  #GOStyle
1240  * @fields: the fields to which the copy should be limited
1241  *
1242  * Merge the attributes from @src onto the elements of @dst that were not user
1243  * assigned (is_auto)
1244  **/
1245 void
go_style_apply_theme(GOStyle * dst,GOStyle const * src,GOStyleFlag fields)1246 go_style_apply_theme (GOStyle *dst, GOStyle const *src, GOStyleFlag fields)
1247 {
1248 	if (src == dst)
1249 		return;
1250 
1251 	g_return_if_fail (GO_IS_STYLE (src));
1252 	g_return_if_fail (GO_IS_STYLE (dst));
1253 
1254 	if (fields & GO_STYLE_FILL) {
1255 		if (dst->fill.auto_type)
1256 			dst->fill.type = src->fill.type;
1257 		if (dst->fill.auto_pattern)
1258 			dst->fill.pattern.pattern = src->fill.pattern.pattern;
1259 		if (dst->fill.auto_fore)
1260 			dst->fill.pattern.fore = src->fill.pattern.fore;
1261 		if (dst->fill.auto_back)
1262 			dst->fill.pattern.back = src->fill.pattern.back;
1263 		if (dst->fill.gradient.auto_dir)
1264 			dst->fill.gradient.dir = src->fill.gradient.dir;
1265 		if (dst->fill.gradient.auto_brightness)
1266 			dst->fill.gradient.brightness = src->fill.gradient.brightness;
1267 	}
1268 
1269 	if (fields & (GO_STYLE_LINE | GO_STYLE_OUTLINE)) {
1270 		if (dst->line.auto_dash)
1271 			dst->line.dash_type = src->line.dash_type;
1272 		if (dst->line.auto_color)
1273 			dst->line.color = src->line.color;
1274 		if (dst->line.auto_width)
1275 			dst->line.width = src->line.width;
1276 	}
1277 	if (fields & GO_STYLE_MARKER) {
1278 		if (dst->marker.auto_shape)
1279 			go_marker_set_shape (dst->marker.mark,
1280 				go_marker_get_shape (src->marker.mark));
1281 		if (dst->marker.auto_outline_color)
1282 			go_marker_set_outline_color (dst->marker.mark,
1283 				go_marker_get_outline_color (src->marker.mark));
1284 		if (dst->marker.auto_fill_color)
1285 			go_marker_set_fill_color (dst->marker.mark,
1286 			                          go_marker_get_fill_color (src->marker.mark));
1287 	}
1288 
1289 	if ((fields & GO_STYLE_TEXT_LAYOUT) && dst->text_layout.auto_angle)
1290 		dst->text_layout.angle = src->text_layout.angle;
1291 
1292 	if (fields & GO_STYLE_FONT) {
1293 		if (dst->font.auto_color)
1294 			dst->font.color = src->font.color;
1295 
1296 		if (dst->font.auto_font) {
1297 			if (src->font.font != NULL)
1298 				go_font_ref (src->font.font);
1299 			if (dst->font.font != NULL)
1300 				go_font_unref (dst->font.font);
1301 			dst->font.font = src->font.font;
1302 		}
1303 	}
1304 }
1305 
1306 static void
go_style_finalize(GObject * obj)1307 go_style_finalize (GObject *obj)
1308 {
1309 	GOStyle *style = GO_STYLE (obj);
1310 
1311 	if (GO_STYLE_FILL_IMAGE == style->fill.type &&
1312 	    style->fill.image.image != NULL)
1313 		g_object_unref (style->fill.image.image);
1314 
1315 	if (style->font.font != NULL) {
1316 		go_font_unref (style->font.font);
1317 		style->font.font = NULL;
1318 	}
1319 
1320 	if (style->marker.mark != NULL) {
1321 		g_object_unref (style->marker.mark);
1322 		style->marker.mark = NULL;
1323 	}
1324 
1325 	(parent_klass->finalize) (obj);
1326 }
1327 
1328 static void
go_style_class_init(GOStyleClass * klass)1329 go_style_class_init (GOStyleClass *klass)
1330 {
1331 	GObjectClass *gobject_klass = (GObjectClass *)klass;
1332 	parent_klass = g_type_class_peek_parent (klass);
1333 	gobject_klass->finalize	= go_style_finalize;
1334 }
1335 
1336 static void
go_style_init(GOStyle * style)1337 go_style_init (GOStyle *style)
1338 {
1339 	style->interesting_fields = GO_STYLE_ALL;
1340 	style->disable_theming = 0;
1341 	go_style_force_auto (style);
1342 	style->line.dash_type = GO_LINE_SOLID;
1343 	style->line.width = 0;
1344 	style->line.cap = CAIRO_LINE_CAP_BUTT;
1345 	style->line.join = CAIRO_LINE_JOIN_MITER;
1346 	style->line.miter_limit = 2.;
1347 	style->fill.type = GO_STYLE_FILL_NONE;
1348 	style->fill.gradient.brightness = -1.;
1349 	go_pattern_set_solid (&style->fill.pattern, GO_COLOR_BLACK);
1350 	style->font.font = go_font_new_by_index (0);
1351 	style->font.color = GO_COLOR_BLACK;
1352 	style->text_layout.angle = 0.0;
1353 }
1354 
1355 static const struct {
1356 	GOStyleFill fstyle;
1357 	char const *name;
1358 } fill_names[] = {
1359 	{ GO_STYLE_FILL_NONE,     "none" },
1360 	{ GO_STYLE_FILL_PATTERN,  "pattern" },
1361 	{ GO_STYLE_FILL_GRADIENT, "gradient" },
1362 	{ GO_STYLE_FILL_IMAGE,    "image" }
1363 };
1364 
1365 static const struct {
1366 	GOImageType fstyle;
1367 	char const *name;
1368 } image_tiling_names[] = {
1369 	{ GO_IMAGE_CENTERED,     "centered" },
1370 	{ GO_IMAGE_STRETCHED,    "stretched" },
1371 	{ GO_IMAGE_WALLPAPER,    "wallpaper" },
1372 };
1373 
1374 static gboolean
bool_sax_prop(char const * name,xmlChar const * id,xmlChar const * val,gboolean * res)1375 bool_sax_prop (char const *name, xmlChar const *id, xmlChar const *val, gboolean *res)
1376 {
1377 	if (attr_eq (id, name)) {
1378 		*res = g_ascii_tolower (*val) == 't' ||
1379 			g_ascii_tolower (*val) == 'y' ||
1380 			strtol (val, NULL, 0);
1381 		return TRUE;
1382 	}
1383 	return FALSE;
1384 }
1385 
1386 
1387 static GOStyleFill
str_as_fill_style(char const * name)1388 str_as_fill_style (char const *name)
1389 {
1390 	unsigned i;
1391 	for (i = 0; i < G_N_ELEMENTS (fill_names); i++)
1392 		if (strcmp (fill_names[i].name, name) == 0)
1393 			return fill_names[i].fstyle;
1394 	return GO_STYLE_FILL_PATTERN;
1395 }
1396 
1397 static char const *
fill_style_as_str(GOStyleFill fstyle)1398 fill_style_as_str (GOStyleFill fstyle)
1399 {
1400 	unsigned i;
1401 	for (i = 0; i < G_N_ELEMENTS (fill_names); i++)
1402 		if (fill_names[i].fstyle == fstyle)
1403 			return fill_names[i].name;
1404 	return "pattern";
1405 }
1406 
1407 static GOImageType
str_as_image_tiling(char const * name)1408 str_as_image_tiling (char const *name)
1409 {
1410 	unsigned i;
1411 	for (i = 0; i < G_N_ELEMENTS (image_tiling_names); i++)
1412 		if (strcmp (image_tiling_names[i].name, name) == 0)
1413 			return image_tiling_names[i].fstyle;
1414 	return GO_IMAGE_STRETCHED;
1415 }
1416 
1417 static char const *
image_tiling_as_str(GOImageType fstyle)1418 image_tiling_as_str (GOImageType fstyle)
1419 {
1420 	unsigned i;
1421 	for (i = 0; i < G_N_ELEMENTS (image_tiling_names); i++)
1422 		if (image_tiling_names[i].fstyle == fstyle)
1423 			return image_tiling_names[i].name;
1424 	return "stretched";
1425 }
1426 
1427 static void
go_style_line_sax_save(GsfXMLOut * output,char const * name,GOStyleLine const * line)1428 go_style_line_sax_save (GsfXMLOut *output, char const *name,
1429 			 GOStyleLine const *line)
1430 {
1431 	gsf_xml_out_start_element (output, name);
1432 
1433 	gsf_xml_out_add_bool (output, "auto-dash", line->auto_dash);
1434 	if (!line->auto_dash)
1435 		gsf_xml_out_add_cstr_unchecked (output, "dash",
1436 						go_line_dash_as_str (line->dash_type));
1437 
1438 	gsf_xml_out_add_bool (output, "auto-width", line->auto_width);
1439 	if (!line->auto_width)
1440 		gsf_xml_out_add_float (output, "width", line->width, -1);
1441 
1442 	gsf_xml_out_add_bool (output, "auto-color", line->auto_color);
1443 	if (!line->auto_color)
1444 		go_xml_out_add_color (output, "color", line->color);
1445 
1446 	gsf_xml_out_end_element (output);
1447 }
1448 
1449 static void
go_style_gradient_sax_save(GsfXMLOut * output,GOStyle const * style)1450 go_style_gradient_sax_save (GsfXMLOut *output, GOStyle const *style)
1451 {
1452 	gsf_xml_out_start_element (output, "gradient");
1453 	gsf_xml_out_add_cstr_unchecked (output, "direction",
1454 		go_gradient_dir_as_str (style->fill.gradient.dir));
1455 	go_xml_out_add_color (output, "start-color",
1456 		style->fill.pattern.back);
1457 	if (style->fill.gradient.brightness >= 0.)
1458 		gsf_xml_out_add_float (output, "brightness",
1459 			style->fill.gradient.brightness, -1);
1460 	else
1461 		go_xml_out_add_color (output, "end-color",
1462 			style->fill.pattern.fore);
1463 	gsf_xml_out_add_bool (output, "auto-direction", style->fill.gradient.auto_dir);
1464 	gsf_xml_out_add_bool (output, "auto-brightness", style->fill.gradient.auto_brightness);
1465 	gsf_xml_out_end_element (output);
1466 }
1467 
1468 static void
go_style_fill_sax_save(GsfXMLOut * output,GOStyle const * style)1469 go_style_fill_sax_save (GsfXMLOut *output, GOStyle const *style)
1470 {
1471 	gsf_xml_out_start_element (output, "fill");
1472 	gsf_xml_out_add_cstr_unchecked (output, "type",
1473 		    fill_style_as_str (
1474 			(style->fill.type != GO_STYLE_FILL_IMAGE || style->fill.image.image != NULL)?
1475 		                       style->fill.type: GO_STYLE_FILL_NONE));
1476 	gsf_xml_out_add_bool (output, "auto-type", style->fill.auto_type);
1477 	gsf_xml_out_add_bool (output, "is-auto", style->fill.auto_back);
1478 	gsf_xml_out_add_bool (output, "auto-fore", style->fill.auto_fore);
1479 
1480 	switch (style->fill.type) {
1481 	case GO_STYLE_FILL_NONE: break;
1482 	case GO_STYLE_FILL_PATTERN:
1483 		gsf_xml_out_start_element (output, "pattern");
1484 		gsf_xml_out_add_cstr_unchecked (output, "type",
1485 			go_pattern_as_str (style->fill.pattern.pattern));
1486 		go_xml_out_add_color (output, "fore",
1487 			style->fill.pattern.fore);
1488 		go_xml_out_add_color (output, "back",
1489 			style->fill.pattern.back);
1490 		gsf_xml_out_add_bool (output, "auto-pattern", style->fill.auto_pattern);
1491 		gsf_xml_out_end_element (output);
1492 		break;
1493 
1494 	case GO_STYLE_FILL_GRADIENT:
1495 		go_style_gradient_sax_save (output, style);
1496 		break;
1497 	case GO_STYLE_FILL_IMAGE:
1498 		if (NULL == style->fill.image.image) {
1499 			g_warning ("dropping fill with missing image");
1500 			break;
1501 		}
1502 
1503 		gsf_xml_out_start_element (output, "image");
1504 		/* FIXME : type is not a good characterization */
1505 		gsf_xml_out_add_cstr_unchecked (output, "type",
1506 			image_tiling_as_str (style->fill.image.type));
1507 		gsf_xml_out_add_cstr (output, "name", go_image_get_name (style->fill.image.image));
1508 		gsf_xml_out_add_cstr (output, "type-name", G_OBJECT_TYPE_NAME (style->fill.image.image));
1509 		go_doc_save_image ((GODoc *) g_object_get_data (G_OBJECT (gsf_xml_out_get_output (output)), "document"), go_image_get_name (style->fill.image.image));
1510 		gsf_xml_out_end_element (output);
1511 		break;
1512 	default:
1513 		break;
1514 	}
1515 	gsf_xml_out_end_element (output);
1516 }
1517 
1518 static void
go_style_marker_sax_save(GsfXMLOut * output,GOStyle const * style)1519 go_style_marker_sax_save (GsfXMLOut *output, GOStyle const *style)
1520 {
1521 	gsf_xml_out_start_element (output, "marker");
1522 
1523 	gsf_xml_out_add_bool (output, "auto-shape", style->marker.auto_shape);
1524 	if (!style->marker.auto_shape)
1525 		gsf_xml_out_add_cstr (output, "shape",
1526 				      go_marker_shape_as_str (go_marker_get_shape (style->marker.mark)));
1527 
1528 	gsf_xml_out_add_bool (output, "auto-outline",
1529 			      style->marker.auto_outline_color);
1530 	if (!style->marker.auto_outline_color)
1531 		go_xml_out_add_color (output, "outline-color",
1532 				      go_marker_get_outline_color (style->marker.mark));
1533 
1534 	gsf_xml_out_add_bool (output, "auto-fill", style->marker.auto_fill_color);
1535 	if (!style->marker.auto_fill_color)
1536 		go_xml_out_add_color (output, "fill-color",
1537 				      go_marker_get_fill_color (style->marker.mark));
1538 
1539 	gsf_xml_out_add_int (output, "size",
1540 			     go_marker_get_size (style->marker.mark));
1541 
1542 	gsf_xml_out_end_element (output);
1543 }
1544 
1545 static void
go_style_font_sax_save(GsfXMLOut * output,GOStyle const * style)1546 go_style_font_sax_save (GsfXMLOut *output, GOStyle const *style)
1547 {
1548 	gsf_xml_out_start_element (output, "font");
1549 
1550 	gsf_xml_out_add_bool (output, "auto-color", style->font.auto_color);
1551 	if (!style->font.auto_color)
1552 		go_xml_out_add_color (output, "color", style->font.color);
1553 
1554 	gsf_xml_out_add_bool (output, "auto-font", style->font.auto_font);
1555 	if (!style->font.auto_font) {
1556 		char *str = go_font_as_str (style->font.font);
1557 		gsf_xml_out_add_cstr (output, "font", str);
1558 		g_free (str);
1559 	}
1560 
1561 	gsf_xml_out_add_bool (output, "auto-scale", style->font.auto_scale);
1562 
1563 	gsf_xml_out_end_element (output);
1564 }
1565 
1566 static void
go_style_text_layout_sax_save(GsfXMLOut * output,GOStyle const * style)1567 go_style_text_layout_sax_save (GsfXMLOut *output, GOStyle const *style)
1568 {
1569 	gsf_xml_out_start_element (output, "text_layout");
1570 	if (!style->text_layout.auto_angle)
1571 		gsf_xml_out_add_float (output, "angle", style->text_layout.angle, -1);
1572 	gsf_xml_out_end_element (output);
1573 }
1574 
1575 static void
go_style_sax_load_line(GsfXMLIn * xin,xmlChar const ** attrs)1576 go_style_sax_load_line (GsfXMLIn *xin, xmlChar const **attrs)
1577 {
1578 	GOStyle *style = GO_STYLE (xin->user_state);
1579 	GOStyleLine *line = &style->line;
1580 	gboolean seen_auto_width = FALSE, seen_width = FALSE;
1581 
1582 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
1583 		if (attr_eq (attrs[0], "dash"))
1584 			line->dash_type = go_line_dash_from_str (attrs[1]);
1585 		else if (bool_sax_prop ("auto-dash", attrs[0], attrs[1], &line->auto_dash))
1586 			;
1587 		else if (attr_eq (attrs[0], "width")) {
1588 			seen_width = TRUE;
1589 			line->width = g_strtod (attrs[1], NULL);
1590 			/* For compatibility with older graphs, when dash_type
1591 			 * didn't exist */
1592 			if (line->width < 0.) {
1593 				line->width = 0.;
1594 				line->dash_type = GO_LINE_NONE;
1595 			}
1596 		} else if (bool_sax_prop ("auto-width", attrs[0], attrs[1], &line->auto_width)) {
1597 			seen_auto_width = TRUE;
1598 		} else if (attr_eq (attrs[0], "color"))
1599 			go_color_from_str (attrs[1], &line->color);
1600 		else if (bool_sax_prop ("auto-color", attrs[0], attrs[1], &line->auto_color))
1601 			;
1602 	}
1603 
1604 	if (seen_width && !seen_auto_width) {
1605 		/*
1606 		 * Pre-0.10.16 lacked the auto-width attribute.  Let's just
1607 		 * assume the width was explicitly set iff it is non-zero.
1608 		 */
1609 		line->auto_width = (line->width == 0);
1610 	}
1611 }
1612 
1613 static void
go_style_sax_load_fill_pattern(GsfXMLIn * xin,xmlChar const ** attrs)1614 go_style_sax_load_fill_pattern (GsfXMLIn *xin, xmlChar const **attrs)
1615 {
1616 	GOStyle *style = GO_STYLE (xin->user_state);
1617 	g_return_if_fail (style->fill.type == GO_STYLE_FILL_PATTERN);
1618 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
1619 		if (attr_eq (attrs[0], "type"))
1620 			style->fill.pattern.pattern = go_pattern_from_str (attrs[1]);
1621 		else if (attr_eq (attrs[0], "fore"))
1622 			go_color_from_str (attrs[1], &style->fill.pattern.fore);
1623 		else if (attr_eq (attrs[0], "back"))
1624 			go_color_from_str (attrs[1], &style->fill.pattern.back);
1625 		else if (bool_sax_prop ("auto-pattern", attrs[0], attrs[1], &style->fill.auto_pattern))
1626 			;
1627 }
1628 
1629 static void
go_style_sax_load_fill_gradient(GsfXMLIn * xin,xmlChar const ** attrs)1630 go_style_sax_load_fill_gradient (GsfXMLIn *xin, xmlChar const **attrs)
1631 {
1632 	GOStyle *style = GO_STYLE (xin->user_state);
1633 	g_return_if_fail (style->fill.type == GO_STYLE_FILL_GRADIENT);
1634 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
1635 		if (attr_eq (attrs[0], "direction"))
1636 			style->fill.gradient.dir = go_gradient_dir_from_str (attrs[1]);
1637 		else if (attr_eq (attrs[0], "start-color"))
1638 			go_color_from_str (attrs[1], &style->fill.pattern.back);
1639 		else if (attr_eq (attrs[0], "end-color"))
1640 			go_color_from_str (attrs[1], &style->fill.pattern.fore);
1641 		else if (attr_eq (attrs[0], "brightness"))
1642 			go_style_set_fill_brightness (style, g_strtod (attrs[1], NULL));
1643 		else if (bool_sax_prop ("auto-direction", attrs[0], attrs[1], &style->fill.gradient.auto_dir))
1644 			;
1645 		else if (bool_sax_prop ("auto-brightness", attrs[0], attrs[1], &style->fill.gradient.auto_brightness))
1646 			;
1647 }
1648 
1649 static void
go_style_sax_load_fill_image(GsfXMLIn * xin,xmlChar const ** attrs)1650 go_style_sax_load_fill_image (GsfXMLIn *xin, xmlChar const **attrs)
1651 {
1652 	GOStyle *style = GO_STYLE (xin->user_state);
1653 	GODoc *doc = (GODoc *) g_object_get_data (G_OBJECT (gsf_xml_in_get_input (xin)), "document");
1654 	xmlChar const *name = NULL, *type_name = NULL;
1655 	GType type;
1656 	g_return_if_fail (style->fill.type == GO_STYLE_FILL_NONE);
1657 	g_return_if_fail (GO_IS_DOC (doc));
1658 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
1659 		if (attr_eq (attrs[0], "type"))
1660 			style->fill.image.type = str_as_image_tiling (attrs[1]);
1661 		else if (attr_eq (attrs[0], "name"))
1662 			name = attrs[1];
1663 		else if (attr_eq (attrs[0], "type-name"))
1664 			type_name = attrs[1];
1665 	type = type_name? g_type_from_name (type_name): GO_TYPE_PIXBUF;
1666 	if (name && type)
1667 		style->fill.image.image = g_object_ref (go_doc_image_fetch (doc, name, type));
1668 	if (style->fill.image.image != NULL)
1669 		style->fill.type = GO_STYLE_FILL_IMAGE;
1670 }
1671 
1672 static void
go_style_sax_load_fill(GsfXMLIn * xin,xmlChar const ** attrs)1673 go_style_sax_load_fill (GsfXMLIn *xin, xmlChar const **attrs)
1674 {
1675 	GOStyle *style = GO_STYLE (xin->user_state);
1676 	style->fill.auto_type = FALSE;
1677 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
1678 		if (attr_eq (attrs[0], "type")) {
1679 			style->fill.type = str_as_fill_style (attrs[1]);
1680 			/* image fill can't be accepted until we have an image */
1681 			if (style->fill.type == GO_STYLE_FILL_IMAGE)
1682 				style->fill.type = GO_STYLE_FILL_NONE;
1683 		} else if (bool_sax_prop ("auto-type", attrs[0], attrs[1], &style->fill.auto_type))
1684 			;
1685 		else if (bool_sax_prop ("is-auto", attrs[0], attrs[1], &style->fill.auto_back))
1686 			;
1687 		else if (bool_sax_prop ("auto-fore", attrs[0], attrs[1], &style->fill.auto_fore))
1688 			;
1689 }
1690 static void
go_style_sax_load_marker(GsfXMLIn * xin,xmlChar const ** attrs)1691 go_style_sax_load_marker (GsfXMLIn *xin, xmlChar const **attrs)
1692 {
1693 	GOStyle *style = GO_STYLE (xin->user_state);
1694 	GOMarker *marker = go_marker_dup (style->marker.mark);
1695 	GOColor c;
1696 
1697 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2)
1698 		if (bool_sax_prop ("auto-shape", attrs[0], attrs[1], &style->marker.auto_shape))
1699 			;
1700 		else if (attr_eq (attrs[0], "shape"))
1701 			go_marker_set_shape (marker, go_marker_shape_from_str (attrs[1]));
1702 		else if (bool_sax_prop ("auto-outline", attrs[0], attrs[1], &style->marker.auto_outline_color))
1703 			;
1704 		else if (attr_eq (attrs[0], "outline-color")) {
1705 			if (go_color_from_str (attrs[1], &c))
1706 				go_marker_set_outline_color (marker, c);
1707 		} else if (bool_sax_prop ("auto-fill", attrs[0], attrs[1], &style->marker.auto_fill_color))
1708 			;
1709 		else if (attr_eq (attrs[0], "fill-color")) {
1710 			if (go_color_from_str (attrs[1], &c))
1711 				go_marker_set_fill_color (marker, c);
1712 		} else if (attr_eq (attrs[0], "size"))
1713 			go_marker_set_size (marker, g_strtod (attrs[1], NULL));
1714 
1715 	go_style_set_marker (style, marker);
1716 }
1717 
1718 static void
go_style_sax_load_font(GsfXMLIn * xin,xmlChar const ** attrs)1719 go_style_sax_load_font (GsfXMLIn *xin, xmlChar const **attrs)
1720 {
1721 	GOStyle *style = GO_STYLE (xin->user_state);
1722 	gboolean seen_auto_color = FALSE, seen_color = FALSE;
1723 	gboolean seen_auto_font = FALSE, seen_font = FALSE;
1724 
1725 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
1726 		if (attr_eq (attrs[0], "color")) {
1727 			seen_color = TRUE;
1728 			go_color_from_str (attrs[1], &style->font.color);
1729 		} else if (bool_sax_prop ("auto-color", attrs[0], attrs[1], &style->font.auto_color)) {
1730 			seen_auto_color = TRUE;
1731 		} else if (bool_sax_prop ("auto-font", attrs[0], attrs[1], &style->font.auto_font)) {
1732 			seen_auto_font = TRUE;
1733 		} else if (attr_eq (attrs[0], "font")) {
1734 			PangoFontDescription *desc = pango_font_description_from_string (attrs[1]);
1735 			if (desc != NULL) {
1736 				if (pango_font_description_get_family (desc) == NULL)
1737 					pango_font_description_set_family_static (desc, "Sans");
1738 				go_style_set_font_desc (style, desc);
1739 			}
1740 			seen_font = TRUE;
1741 		} else if (bool_sax_prop ("auto-scale", attrs[0], attrs[1], &style->font.auto_scale))
1742 			;
1743 	}
1744 
1745 	if (seen_color && !seen_auto_color) {
1746 		/*
1747 		 * Pre-0.10.16 lacked the auto-color attribute.  Let's just
1748 		 * assume it was explicitly set iff it is not black.
1749 		 */
1750 		style->font.auto_color = (style->font.color == GO_COLOR_BLACK);
1751 	}
1752 
1753 	if (seen_font && seen_auto_font && style->font.auto_font) {
1754 		GOFont const *def = go_font_new_by_index (0);
1755 		/*
1756 		 * Pre-0.10.21 lacked the font styling.  We were always saving a font and
1757 		 * always saving auto-font=TRUE.  Since we weren't actually applying any
1758 		 * font theming, the default is font 0 (aka "Sans 8").
1759 		 */
1760 		style->font.auto_font = (style->font.font == def);
1761 		go_font_unref (def);
1762 	}
1763 }
1764 
1765 static void
go_style_sax_load_text_layout(GsfXMLIn * xin,xmlChar const ** attrs)1766 go_style_sax_load_text_layout (GsfXMLIn *xin, xmlChar const **attrs)
1767 {
1768 	GOStyle *style = GO_STYLE (xin->user_state);
1769 	for (; attrs != NULL && attrs[0] && attrs[1] ; attrs += 2) {
1770 		if (attr_eq (attrs[0], "angle")) {
1771 			/* Note, that this sets auto-angle to FALSE.  */
1772 			go_style_set_text_angle (style, g_strtod (attrs[1], NULL));
1773 		}
1774 	}
1775 }
1776 
1777 static void
go_style_persist_sax_save(GOPersist const * gp,GsfXMLOut * output)1778 go_style_persist_sax_save (GOPersist const *gp, GsfXMLOut *output)
1779 {
1780 	GOStyle const *style = GO_STYLE (gp);
1781 
1782 	gsf_xml_out_add_cstr_unchecked (output, "type",
1783 		G_OBJECT_TYPE_NAME (style));
1784 
1785 	if (style->interesting_fields & GO_STYLE_LINE)
1786 		go_style_line_sax_save (output, "line", &style->line);
1787 	else if (style->interesting_fields & GO_STYLE_OUTLINE)
1788 		/* no need to save both line and outline */
1789 		go_style_line_sax_save (output, "outline", &style->line);
1790 	if (style->interesting_fields & GO_STYLE_FILL)
1791 		go_style_fill_sax_save (output, style);
1792 	if (style->interesting_fields & GO_STYLE_MARKER)
1793 		go_style_marker_sax_save (output, style);
1794 	if (style->interesting_fields & GO_STYLE_FONT)
1795 		go_style_font_sax_save (output, style);
1796 	if (style->interesting_fields & GO_STYLE_TEXT_LAYOUT)
1797 		go_style_text_layout_sax_save (output, style);
1798 }
1799 
1800 static void
go_style_persist_prep_sax(GOPersist * gp,GsfXMLIn * xin,xmlChar const ** attrs)1801 go_style_persist_prep_sax (GOPersist *gp, GsfXMLIn *xin, xmlChar const **attrs)
1802 {
1803 	static GsfXMLInNode const dtd[] = {
1804 		GSF_XML_IN_NODE 	(STYLE, STYLE,
1805 					 -1, "Style",
1806 					 FALSE, NULL, NULL),
1807 		GSF_XML_IN_NODE_FULL 	(STYLE, STYLE_LINE,
1808 					 -1, "line",
1809 					 GSF_XML_NO_CONTENT, FALSE, FALSE,
1810 					 &go_style_sax_load_line, NULL, 0),
1811 		GSF_XML_IN_NODE_FULL 	(STYLE, STYLE_OUTLINE,
1812 					 -1, "outline",
1813 					 GSF_XML_NO_CONTENT, FALSE, FALSE,
1814 					 &go_style_sax_load_line, NULL, 1),
1815 		GSF_XML_IN_NODE 	(STYLE, STYLE_FILL,
1816 					 -1, "fill",
1817 					 GSF_XML_NO_CONTENT,
1818 					 &go_style_sax_load_fill, NULL),
1819 		GSF_XML_IN_NODE 	(STYLE_FILL, FILL_PATTERN,
1820 					 -1, "pattern",
1821 					 GSF_XML_NO_CONTENT,
1822 					 &go_style_sax_load_fill_pattern, NULL),
1823 		GSF_XML_IN_NODE 	(STYLE_FILL, FILL_GRADIENT,
1824 					 -1, "gradient",
1825 					 GSF_XML_NO_CONTENT,
1826 					 &go_style_sax_load_fill_gradient, NULL),
1827 		GSF_XML_IN_NODE 	(STYLE_FILL, FILL_IMAGE,
1828 					 -1, "image",
1829 					 GSF_XML_NO_CONTENT,
1830 					 &go_style_sax_load_fill_image, NULL),
1831 		GSF_XML_IN_NODE 	(STYLE, STYLE_MARKER,
1832 					 -1, "marker",
1833 					 GSF_XML_NO_CONTENT,
1834 					 &go_style_sax_load_marker, NULL),
1835 		GSF_XML_IN_NODE 	(STYLE, STYLE_FONT,
1836 			 		 -1, "font",
1837 					 GSF_XML_NO_CONTENT,
1838 					 &go_style_sax_load_font, NULL),
1839 		GSF_XML_IN_NODE 	(STYLE, STYLE_ALIGNMENT,
1840 					 -1, "text_layout",
1841 					 GSF_XML_NO_CONTENT,
1842 					 &go_style_sax_load_text_layout, NULL),
1843 		GSF_XML_IN_NODE_END
1844 	};
1845 	static GsfXMLInDoc *doc = NULL;
1846 
1847 	if (NULL == doc) {
1848 		doc = gsf_xml_in_doc_new (dtd, NULL);
1849 		go_xml_in_doc_dispose_on_exit (&doc);
1850 	}
1851 	gsf_xml_in_push_state (xin, doc, gp, NULL, attrs);
1852 }
1853 
1854 static void
go_style_persist_init(GOPersistClass * iface)1855 go_style_persist_init (GOPersistClass *iface)
1856 {
1857 	iface->prep_sax = go_style_persist_prep_sax;
1858 	iface->sax_save = go_style_persist_sax_save;
1859 }
1860 
1861 GSF_CLASS_FULL (GOStyle, go_style,
1862 		NULL, NULL, go_style_class_init, NULL,
1863 		go_style_init, G_TYPE_OBJECT, 0,
1864 		GSF_INTERFACE (go_style_persist_init, GO_TYPE_PERSIST))
1865 
1866 gboolean
go_style_is_different_size(GOStyle const * a,GOStyle const * b)1867 go_style_is_different_size (GOStyle const *a, GOStyle const *b)
1868 {
1869 	if (a == NULL || b == NULL)
1870 		return TRUE;
1871 	return	a->line.dash_type != b->line.dash_type ||
1872 		a->line.width != b->line.width ||
1873 		a->fill.type != b->fill.type ||
1874 		a->text_layout.angle != b->text_layout.angle ||
1875 		!go_font_eq (a->font.font, b->font.font);
1876 }
1877 
1878 gboolean
go_style_is_marker_visible(GOStyle const * style)1879 go_style_is_marker_visible (GOStyle const *style)
1880 {
1881 	g_return_val_if_fail (GO_IS_STYLE (style), FALSE);
1882 
1883 	/* FIXME FIXME FIXME TODO : make this smarter */
1884 	return (style->interesting_fields & GO_STYLE_MARKER) &&
1885 		go_marker_get_shape (style->marker.mark) != GO_MARKER_NONE;
1886 }
1887 
1888 gboolean
go_style_is_outline_visible(GOStyle const * style)1889 go_style_is_outline_visible (GOStyle const *style)
1890 {
1891 	g_return_val_if_fail (GO_IS_STYLE (style), FALSE);
1892 
1893 	/* FIXME FIXME FIXME make this smarter */
1894 	return GO_COLOR_UINT_A (style->line.color) > 0 &&
1895 		style->line.dash_type != GO_LINE_NONE;
1896 }
1897 
1898 gboolean
go_style_is_line_visible(GOStyle const * style)1899 go_style_is_line_visible (GOStyle const *style)
1900 {
1901 	g_return_val_if_fail (GO_IS_STYLE (style), FALSE);
1902 
1903 	/* FIXME FIXME FIXME TODO : make this smarter */
1904 	return GO_COLOR_UINT_A (style->line.color) > 0 &&
1905 		style->line.dash_type != GO_LINE_NONE;
1906 }
1907 
1908 gboolean
go_style_is_fill_visible(const GOStyle * style)1909 go_style_is_fill_visible (const GOStyle *style)
1910 {
1911 	g_return_val_if_fail (GO_IS_STYLE (style), FALSE);
1912 
1913 	return (style->fill.type != GO_STYLE_FILL_NONE);
1914 }
1915 
1916 /**
1917  * go_style_force_auto:
1918  * @style: a #GOStyle
1919  *
1920  * Sets all auto fields in @style to %TRUE.
1921  **/
1922 void
go_style_force_auto(GOStyle * style)1923 go_style_force_auto (GOStyle *style)
1924 {
1925 	g_return_if_fail (GO_IS_STYLE (style));
1926 
1927 	if (style->marker.mark != NULL)
1928 		g_object_unref (style->marker.mark);
1929 	style->marker.mark = go_marker_new ();
1930 	style->marker.auto_shape =
1931 	style->marker.auto_outline_color =
1932 	style->marker.auto_fill_color =
1933 	style->line.auto_dash =
1934 	style->line.auto_color =
1935 	style->line.auto_fore =
1936 	style->line.auto_width =
1937 	style->fill.auto_type =
1938 	style->fill.auto_fore =
1939 	style->fill.auto_back =
1940 	style->fill.auto_pattern =
1941 	style->fill.gradient.auto_dir =
1942 	style->fill.gradient.auto_brightness =
1943 	style->font.auto_scale =
1944 	style->font.auto_font =
1945 	style->font.auto_color =
1946 	style->text_layout.auto_angle = TRUE;
1947 }
1948 
1949 /**
1950  * go_style_clear_auto:
1951  * @style: a #GOStyle
1952  *
1953  * Sets all auto fields in @style to %FALSE.
1954  **/
1955 void
go_style_clear_auto(GOStyle * style)1956 go_style_clear_auto (GOStyle *style)
1957 {
1958 	g_return_if_fail (GO_IS_STYLE (style));
1959 
1960 	if (style->marker.mark != NULL)
1961 		g_object_unref (style->marker.mark);
1962 	style->marker.mark = go_marker_new ();
1963 	style->marker.auto_shape =
1964 	style->marker.auto_outline_color =
1965 	style->marker.auto_fill_color =
1966 	style->line.auto_dash =
1967 	style->line.auto_color =
1968 	style->line.auto_fore =
1969 	style->line.auto_width =
1970 	style->fill.auto_type =
1971 	style->fill.auto_fore =
1972 	style->fill.auto_back =
1973 	style->fill.auto_pattern =
1974 	style->fill.gradient.auto_dir =
1975 	style->fill.gradient.auto_brightness =
1976 	style->font.auto_scale =
1977 	style->font.auto_font =
1978 	style->font.auto_color =
1979 	style->text_layout.auto_angle = FALSE;
1980 }
1981 
1982 gboolean
go_style_is_auto(GOStyle * style)1983 go_style_is_auto (GOStyle *style)
1984 {
1985 	return (style->marker.auto_shape && style->marker.auto_outline_color &&
1986 		style->marker.auto_fill_color && style->line.auto_dash &&
1987 		style->line.auto_color && style->fill.auto_type &&
1988 		style->fill.auto_fore && style->fill.auto_back &&
1989 	    (style->fill.type == GO_STYLE_FILL_NONE ||
1990 	     (style->fill.type == GO_STYLE_FILL_PATTERN && style->fill.auto_pattern) ||
1991 	     (style->fill.type == GO_STYLE_FILL_GRADIENT && style->fill.gradient.auto_dir
1992 	      && style->fill.gradient.auto_brightness)) &&
1993 		style->font.auto_scale && style->font.auto_color && style->font.auto_font &&
1994 		style->text_layout.auto_angle);
1995 }
1996 
1997 /**
1998  * go_style_set_marker:
1999  * @style: #GOStyle
2000  * @marker: (transfer full): #GOMarker
2001  *
2002  * Absorb a reference to @marker and assign it to @style.
2003  **/
2004 void
go_style_set_marker(GOStyle * style,GOMarker * marker)2005 go_style_set_marker (GOStyle *style, GOMarker *marker)
2006 {
2007 	g_return_if_fail (GO_IS_STYLE (style));
2008 	g_return_if_fail (GO_IS_MARKER (marker));
2009 
2010 	if (style->marker.mark != marker) {
2011 		if (style->marker.mark != NULL)
2012 			g_object_unref (style->marker.mark);
2013 		style->marker.mark = marker;
2014 	}
2015 }
2016 
2017 /**
2018  * go_style_get_marker:
2019  * @style: #GOStyle
2020  *
2021  * Accessor for @style::marker, without referencing it.
2022  *
2023  * Returns: (transfer none): the style #GOMarker.
2024  **/
2025 GOMarker const *
go_style_get_marker(GOStyle const * style)2026 go_style_get_marker (GOStyle const *style)
2027 {
2028 	g_return_val_if_fail (GO_IS_STYLE (style), NULL);
2029 
2030 	return style->marker.mark;
2031 }
2032 
2033 /**
2034  * go_style_set_font_desc:
2035  * @style: #GOStyle
2036  * @desc: (transfer full): new font description to use
2037  *
2038  * Set text font used by the style.
2039  **/
2040 void
go_style_set_font_desc(GOStyle * style,PangoFontDescription * desc)2041 go_style_set_font_desc (GOStyle *style, PangoFontDescription *desc)
2042 {
2043 	g_return_if_fail (GO_IS_STYLE (style));
2044 
2045 	go_style_set_font (style, go_font_new_by_desc (desc));
2046 }
2047 
2048 /**
2049  * go_style_set_font:
2050  * @style: #GOStyle
2051  * @font: (transfer full): new font
2052  *
2053  * Set text font used by the style.
2054  **/
2055 void
go_style_set_font(GOStyle * style,GOFont const * font)2056 go_style_set_font (GOStyle *style, GOFont const *font)
2057 {
2058 	g_return_if_fail (GO_IS_STYLE (style));
2059 
2060 	if (font != NULL) {
2061 		go_font_unref (style->font.font);
2062 		style->font.font = font;
2063 	}
2064 }
2065 
2066 void
go_style_set_fill_brightness(GOStyle * style,double brightness)2067 go_style_set_fill_brightness (GOStyle *style, double brightness)
2068 {
2069 	double limit;
2070 	g_return_if_fail (GO_IS_STYLE (style));
2071 	g_return_if_fail (style->fill.type == GO_STYLE_FILL_GRADIENT);
2072 
2073 	brightness = CLAMP (brightness, 0, 100.0);
2074 	limit = (GO_COLOR_UINT_R (style->fill.pattern.back) + GO_COLOR_UINT_G (style->fill.pattern.back) + GO_COLOR_UINT_B (style->fill.pattern.back)) / 7.65;
2075 
2076 	style->fill.gradient.brightness = brightness;
2077 	style->fill.pattern.fore = (brightness <= limit && limit > 0.)
2078 		? GO_COLOR_INTERPOLATE(style->fill.pattern.back, GO_COLOR_BLACK, 1. - brightness / limit)
2079 		: GO_COLOR_INTERPOLATE(style->fill.pattern.back, GO_COLOR_WHITE, (brightness - limit) / (100. - limit));
2080 }
2081 
2082 /**
2083  * go_style_set_text_angle:
2084  * @style: #GOStyle
2085  * @angle: text rotation in degrees
2086  *
2087  * Set text rotation angle in degrees. Valid values are in the range
2088  * [-180.0° , 180.0°].
2089  **/
2090 void
go_style_set_text_angle(GOStyle * style,double angle)2091 go_style_set_text_angle (GOStyle *style, double angle)
2092 {
2093 	g_return_if_fail (GO_IS_STYLE (style));
2094 
2095 	style->text_layout.angle = CLAMP (angle, -180.0, 180.0);
2096 	style->text_layout.auto_angle = FALSE;
2097 }
2098 
2099 void
go_style_fill(GOStyle const * style,cairo_t * cr,gboolean preserve)2100 go_style_fill (GOStyle const *style, cairo_t *cr, gboolean preserve)
2101 {
2102 	cairo_pattern_t *cr_pattern = NULL;
2103 	double x[3], y[3];
2104 	int w, h;
2105 
2106 	static struct { unsigned x0i, y0i, x1i, y1i; } const grad_i[GO_GRADIENT_MAX] = {
2107 		{0, 0, 0, 1},
2108 		{0, 1, 0, 0},
2109 		{0, 0, 0, 2},
2110 		{0, 2, 0, 1},
2111 		{0, 0, 1, 0},
2112 		{1, 0, 0, 0},
2113 		{0, 0, 2, 0},
2114 		{2, 0, 1, 0},
2115 		{0, 0, 1, 1},
2116 		{1, 1, 0, 0},
2117 		{0, 0, 2, 2},
2118 		{2, 2, 1, 1},
2119 		{1, 0, 0, 1},
2120 		{0, 1, 1, 0},
2121 		{1, 0, 2, 2},
2122 		{2, 2, 0, 1}
2123 	};
2124 
2125 	cairo_fill_extents (cr, &x[0], &y[0], &x[1], &y[1]);
2126 	if (!GO_IS_STYLE (style) ||
2127 	    go_sub_epsilon (fabs (x[0] - x[1])) <=0.0 ||
2128 	    go_sub_epsilon (fabs (y[0] - y[1])) <=0.0) {
2129 		if (!preserve)
2130 			cairo_new_path (cr);
2131 		return;
2132 	    }
2133 
2134 	switch (style->fill.type) {
2135 		case GO_STYLE_FILL_PATTERN:
2136 			cr_pattern = go_pattern_create_cairo_pattern (&style->fill.pattern, cr);
2137 			break;
2138 
2139 		case GO_STYLE_FILL_GRADIENT:
2140 			x[2] = (x[1] - x[0]) / 2.0 + x[0];
2141 			y[2] = (y[1] - y[0]) / 2.0 + y[0];
2142 			cr_pattern = cairo_pattern_create_linear (
2143 				x[grad_i[style->fill.gradient.dir].x0i],
2144 				y[grad_i[style->fill.gradient.dir].y0i],
2145 				x[grad_i[style->fill.gradient.dir].x1i],
2146 				y[grad_i[style->fill.gradient.dir].y1i]);
2147 			cairo_pattern_set_extend (cr_pattern, CAIRO_EXTEND_REFLECT);
2148 			cairo_pattern_add_color_stop_rgba (cr_pattern, 0,
2149 				GO_COLOR_TO_CAIRO (style->fill.pattern.back));
2150 			cairo_pattern_add_color_stop_rgba (cr_pattern, 1,
2151 				GO_COLOR_TO_CAIRO (style->fill.pattern.fore));
2152 			break;
2153 
2154 		case GO_STYLE_FILL_IMAGE:
2155 			if (!GO_IS_IMAGE (style->fill.image.image))
2156 				cr_pattern = cairo_pattern_create_rgba (1, 1, 1, 1);
2157 			else {
2158 				cairo_save (cr);
2159 				if (preserve)
2160 					cairo_clip_preserve (cr);
2161 				else
2162 					cairo_clip (cr);
2163 				g_object_get (style->fill.image.image, "width", &w, "height", &h, NULL);
2164 				switch (style->fill.image.type) {
2165 					case GO_IMAGE_CENTERED:
2166 						cairo_translate (cr,
2167 						                 (x[1] - x[0] - w) / 2 + x[0],
2168 						                 (y[1] - y[0] - h) / 2 + y[0]);
2169 						go_image_draw (style->fill.image.image, cr);
2170 						break;
2171 					case GO_IMAGE_STRETCHED:
2172 						cairo_translate (cr, x[0], y[0]);
2173 						cairo_scale (cr, (x[1] - x[0]) / w, (y[1] - y[0]) / h);
2174 						go_image_draw (style->fill.image.image, cr);
2175 						break;
2176 					case GO_IMAGE_CENTERED_WALLPAPER: {
2177 						int n = go_fake_floor ((x[1] - x[0]) / w);
2178 						x[0] -= w - (x[1] - x[0] - n * w) / 2.;
2179 						n = go_fake_floor ((y[1] - y[0]) / h);
2180 						y[0] -= h - (y[1] - y[0] - n * h) / 2.;
2181 					}
2182 					case GO_IMAGE_WALLPAPER: {
2183 						double cx = x[0], cy;
2184 						while (cx < x[1]) {
2185 							cy = y[0];
2186 							while (cy < y[1]) {
2187 								cairo_save (cr);
2188 								cairo_translate (cr, cx, cy);
2189 								go_image_draw (style->fill.image.image, cr);
2190 								cairo_restore (cr);
2191 								cy += h;
2192 							}
2193 							cx += w;
2194 						}
2195 						break;
2196 					}
2197 				}
2198 				cairo_restore (cr);
2199 				return;
2200 			}
2201 
2202 		case GO_STYLE_FILL_NONE:
2203 			if (!preserve)
2204 				cairo_new_path (cr);
2205 			break;
2206 	}
2207 
2208 	if (cr_pattern) {
2209 		cairo_set_source (cr, cr_pattern);
2210 		cairo_pattern_destroy (cr_pattern);
2211 
2212 		if (preserve)
2213 			cairo_fill_preserve (cr);
2214 		else
2215 			cairo_fill (cr);
2216 	}
2217 }
2218 
2219 gboolean
go_style_set_cairo_line(GOStyle const * style,cairo_t * cr)2220 go_style_set_cairo_line (GOStyle const *style, cairo_t *cr)
2221 {
2222 	double width;
2223 	GOLineDashSequence *line_dash;
2224 
2225 	g_return_val_if_fail (GO_IS_STYLE (style) && cr != NULL, FALSE);
2226 	if (style->line.dash_type == GO_LINE_NONE)
2227 		return FALSE;
2228 	if (style->line.width > 0.)
2229 		width = style->line.width;
2230 	else {
2231 		cairo_matrix_t m;
2232 		cairo_get_matrix (cr, &m);
2233 		width = m.xx * m.yy - m.xy * m.yx;
2234 		width = (width > 0.)? 1. / sqrt (m.xx * m.yy - m.xy * m.yx): 1.;
2235 	}
2236 	cairo_set_line_width (cr, width);
2237 	cairo_set_line_cap (cr, style->line.cap);
2238 	cairo_set_line_join (cr, style->line.join);
2239 	cairo_set_miter_limit (cr, style->line.miter_limit);
2240 	line_dash = go_line_dash_get_sequence (style->line.dash_type, width);
2241 	if (style->line.cap == CAIRO_LINE_CAP_BUTT &&
2242 	    style->line.dash_type != GO_LINE_SOLID) {
2243 		unsigned i;
2244 		for (i = 0; i < line_dash->n_dash; i++)
2245 			if (line_dash->dash[i] == 0.)
2246 				line_dash->dash[i] = width;
2247 	}
2248 	if (line_dash != NULL) {
2249 		cairo_set_dash (cr,
2250 				line_dash->dash,
2251 				line_dash->n_dash,
2252 				line_dash->offset);
2253 		go_line_dash_sequence_free (line_dash);
2254 	} else
2255 		cairo_set_dash (cr, NULL, 0, 0);
2256 	switch (style->line.pattern) {
2257 	case GO_PATTERN_SOLID:
2258 		cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (style->line.color));
2259 		break;
2260 	case GO_PATTERN_FOREGROUND_SOLID:
2261 		cairo_set_source_rgba (cr, GO_COLOR_TO_CAIRO (style->line.fore));
2262 		break;
2263 	default: {
2264 		GOPattern pat;
2265 		double scalex = 1., scaley = 1.;
2266 		cairo_pattern_t *cp;
2267 		cairo_matrix_t mat;
2268 		pat.fore = style->line.fore;
2269 		pat.back = style->line.color;
2270 		pat.pattern = style->line.pattern;
2271 		cp = go_pattern_create_cairo_pattern (&pat, cr);
2272 		g_return_val_if_fail (cp != NULL, FALSE);
2273 		cairo_user_to_device_distance (cr, &scalex, &scaley);
2274 		cairo_matrix_init_scale (&mat, scalex, scaley);
2275 		cairo_pattern_set_matrix (cp, &mat);
2276 		cairo_set_source (cr, cp);
2277 		cairo_pattern_destroy (cp);
2278 		break;
2279 	}
2280 	}
2281 	return TRUE;
2282 }
2283