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