1 #define __SP_FILL_STYLE_C__
3 /**
4 * \brief Fill style widget
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * Frank Felfe <innerspace@iname.com>
9 * bulia byak <buliabyak@users.sf.net>
10 *
11 * Copyright (C) 1999-2005 authors
12 * Copyright (C) 2001-2002 Ximian, Inc.
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #define noSP_FS_VERBOSE
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
24 #include <widgets/sp-widget.h>
25 #include <sp-linear-gradient.h>
26 #include <sp-pattern.h>
27 #include <sp-radial-gradient.h>
28 #include <widgets/paint-selector.h>
29 #include <style.h>
30 #include <gradient-chemistry.h>
31 #include <desktop-style.h>
32 #include <desktop-handles.h>
33 #include <selection.h>
34 #include <inkscape.h>
35 #include <document-private.h>
36 #include <xml/repr.h>
39 // These can be deleted once we sort out the libart dependence.
41 #define ART_WIND_RULE_NONZERO 0
43 static void sp_fill_style_widget_construct ( SPWidget *spw,
44 SPPaintSelector *psel );
46 static void sp_fill_style_widget_modify_selection ( SPWidget *spw,
47 Inkscape::Selection *selection,
48 guint flags,
49 SPPaintSelector *psel );
51 static void sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape, SPDesktop *desktop, SPWidget *spw );
53 static void sp_fill_style_widget_change_selection ( SPWidget *spw,
54 Inkscape::Selection *selection,
55 SPPaintSelector *psel );
57 static void sp_fill_style_widget_update (SPWidget *spw);
59 static void sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
60 SPPaintSelectorMode mode,
61 SPWidget *spw );
62 static void sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
63 SPPaintSelectorFillRule mode,
64 SPWidget *spw );
66 static void sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw );
67 static void sp_fill_style_widget_paint_changed (SPPaintSelector *psel, SPWidget *spw );
69 GtkWidget *
70 sp_fill_style_widget_new (void)
71 {
72 GtkWidget *spw = sp_widget_new_global (INKSCAPE);
74 GtkWidget *vb = gtk_vbox_new (FALSE, 0);
75 gtk_widget_show (vb);
76 gtk_container_add (GTK_CONTAINER (spw), vb);
78 GtkWidget *psel = sp_paint_selector_new (true); // with fillrule selector
79 gtk_widget_show (psel);
80 gtk_box_pack_start (GTK_BOX (vb), psel, TRUE, TRUE, 0);
81 g_object_set_data (G_OBJECT (spw), "paint-selector", psel);
83 g_signal_connect ( G_OBJECT (psel), "mode_changed",
84 G_CALLBACK (sp_fill_style_widget_paint_mode_changed),
85 spw );
87 g_signal_connect ( G_OBJECT (psel), "dragged",
88 G_CALLBACK (sp_fill_style_widget_paint_dragged),
89 spw );
91 g_signal_connect ( G_OBJECT (psel), "changed",
92 G_CALLBACK (sp_fill_style_widget_paint_changed),
93 spw );
95 g_signal_connect ( G_OBJECT (psel), "fillrule_changed",
96 G_CALLBACK (sp_fill_style_widget_fillrule_changed),
97 spw );
100 g_signal_connect ( G_OBJECT (spw), "construct",
101 G_CALLBACK (sp_fill_style_widget_construct), psel);
103 //FIXME: switch these from spw signals to global inkscape object signals; spw just retranslates
104 //those anyway; then eliminate spw
105 g_signal_connect ( G_OBJECT (spw), "modify_selection",
106 G_CALLBACK (sp_fill_style_widget_modify_selection), psel);
108 g_signal_connect ( G_OBJECT (spw), "change_selection",
109 G_CALLBACK (sp_fill_style_widget_change_selection), psel);
111 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_fill_style_widget_change_subselection), spw);
113 sp_fill_style_widget_update (SP_WIDGET (spw));
115 return spw;
117 } // end of sp_fill_style_widget_new()
121 static void
122 sp_fill_style_widget_construct ( SPWidget *spw, SPPaintSelector *psel )
123 {
125 #ifdef SP_FS_VERBOSE
126 g_print ( "Fill style widget constructed: inkscape %p repr %p\n",
127 spw->inkscape, spw->repr );
128 #endif
129 if (spw->inkscape) {
131 sp_fill_style_widget_update (spw);
133 }
135 } // end of sp_fill_style_widget_construct()
137 static void
138 sp_fill_style_widget_modify_selection ( SPWidget *spw,
139 Inkscape::Selection *selection,
140 guint flags,
141 SPPaintSelector *psel )
142 {
143 if (flags & ( SP_OBJECT_MODIFIED_FLAG |
144 SP_OBJECT_PARENT_MODIFIED_FLAG |
145 SP_OBJECT_STYLE_MODIFIED_FLAG) )
146 {
147 sp_fill_style_widget_update (spw);
148 }
149 }
151 static void
152 sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape,
153 SPDesktop *desktop,
154 SPWidget *spw )
155 {
156 sp_fill_style_widget_update (spw);
157 }
159 static void
160 sp_fill_style_widget_change_selection ( SPWidget *spw,
161 Inkscape::Selection *selection,
162 SPPaintSelector *psel )
163 {
164 sp_fill_style_widget_update (spw);
165 }
167 /**
168 * \param sel Selection to use, or NULL.
169 */
170 static void
171 sp_fill_style_widget_update (SPWidget *spw)
172 {
173 if (g_object_get_data (G_OBJECT (spw), "update"))
174 return;
176 if (g_object_get_data (G_OBJECT (spw), "local")) {
177 g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (FALSE)); // local change; do nothing, but reset the flag
178 return;
179 }
181 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
183 SPPaintSelector *psel = SP_PAINT_SELECTOR (g_object_get_data (G_OBJECT (spw), "paint-selector"));
185 // create temporary style
186 SPStyle *query = sp_style_new ();
187 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
188 int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FILL);
190 switch (result) {
191 case QUERY_STYLE_NOTHING:
192 {
193 /* No paint at all */
194 sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY);
195 break;
196 }
198 case QUERY_STYLE_SINGLE:
199 case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector
200 case QUERY_STYLE_MULTIPLE_SAME:
201 {
202 SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, true);
203 sp_paint_selector_set_mode (psel, pselmode);
205 sp_paint_selector_set_fillrule (psel, query->fill_rule.computed == ART_WIND_RULE_NONZERO?
206 SP_PAINT_SELECTOR_FILLRULE_NONZERO : SP_PAINT_SELECTOR_FILLRULE_EVENODD);
208 if (query->fill.set && query->fill.type == SP_PAINT_TYPE_COLOR) {
209 gfloat d[3];
210 sp_color_get_rgb_floatv (&query->fill.value.color, d);
211 SPColor color;
212 sp_color_set_rgb_float (&color, d[0], d[1], d[2]);
213 sp_paint_selector_set_color_alpha (psel, &color, SP_SCALE24_TO_FLOAT (query->fill_opacity.value));
215 } else if (query->fill.set && query->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
217 SPPaintServer *server = SP_STYLE_FILL_SERVER (query);
219 if (SP_IS_LINEARGRADIENT (server)) {
220 SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
221 sp_paint_selector_set_gradient_linear (psel, vector);
223 SPLinearGradient *lg = SP_LINEARGRADIENT (server);
224 sp_paint_selector_set_gradient_properties (psel,
225 SP_GRADIENT_UNITS (lg),
226 SP_GRADIENT_SPREAD (lg));
227 } else if (SP_IS_RADIALGRADIENT (server)) {
228 SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
229 sp_paint_selector_set_gradient_radial (psel, vector);
231 SPRadialGradient *rg = SP_RADIALGRADIENT (server);
232 sp_paint_selector_set_gradient_properties (psel,
233 SP_GRADIENT_UNITS (rg),
234 SP_GRADIENT_SPREAD (rg));
235 } else if (SP_IS_PATTERN (server)) {
236 SPPattern *pat = pattern_getroot (SP_PATTERN (server));
237 sp_update_pattern_list (psel, pat);
238 }
239 }
240 break;
241 }
243 case QUERY_STYLE_MULTIPLE_DIFFERENT:
244 {
245 sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE);
246 break;
247 }
248 }
250 g_free (query);
252 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
254 }
257 static void
258 sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
259 SPPaintSelectorMode mode,
260 SPWidget *spw )
261 {
262 if (g_object_get_data (G_OBJECT (spw), "update"))
263 return;
265 /* TODO: Does this work? */
266 /* TODO: Not really, here we have to get old color back from object */
267 /* Instead of relying on paint widget having meaningful colors set */
268 sp_fill_style_widget_paint_changed (psel, spw);
269 }
271 static void
272 sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
273 SPPaintSelectorFillRule mode,
274 SPWidget *spw )
275 {
276 if (g_object_get_data (G_OBJECT (spw), "update"))
277 return;
279 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
281 SPCSSAttr *css = sp_repr_css_attr_new ();
282 sp_repr_css_set_property (css, "fill-rule", mode == SP_PAINT_SELECTOR_FILLRULE_EVENODD? "evenodd":"nonzero");
284 sp_desktop_set_style (desktop, css);
286 sp_repr_css_attr_unref (css);
288 sp_document_done (SP_ACTIVE_DOCUMENT);
289 }
291 static gchar *undo_label_1 = "fill:flatcolor:1";
292 static gchar *undo_label_2 = "fill:flatcolor:2";
293 static gchar *undo_label = undo_label_1;
295 /**
296 This is called repeatedly while you are dragging a color slider, only for flat color
297 modes. Previously it set the color in style but did not update the repr for efficiency, however
298 this was flakey and didn't buy us almost anything. So now it does the same as _changed, except
299 lumps all its changes for undo.
300 */
301 static void
302 sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw)
303 {
304 if (!spw->inkscape) {
305 return;
306 }
308 if (g_object_get_data (G_OBJECT (spw), "update")) {
309 return;
310 }
312 if (g_object_get_data (G_OBJECT (spw), "local")) {
313 // previous local flag not cleared yet;
314 // this means dragged events come too fast, so we better skip this one to speed up display
315 // (it's safe to do this in any case)
316 return;
317 }
319 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
321 switch (psel->mode) {
323 case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
324 case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
325 {
326 sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "fill", "fill-opacity");
327 sp_document_maybe_done (SP_DT_DOCUMENT(SP_ACTIVE_DESKTOP), undo_label);
328 g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (TRUE)); // local change, do not update from selection
329 break;
330 }
332 default:
333 g_warning ( "file %s: line %d: Paint %d should not emit 'dragged'",
334 __FILE__, __LINE__, psel->mode );
335 break;
337 }
338 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
339 }
342 /**
343 This is called (at least) when:
344 1 paint selector mode is switched (e.g. flat color -> gradient)
345 2 you finished dragging a gradient node and released mouse
346 3 you changed a gradient selector parameter (e.g. spread)
347 Must update repr.
348 */
349 static void
350 sp_fill_style_widget_paint_changed ( SPPaintSelector *psel,
351 SPWidget *spw )
352 {
353 if (g_object_get_data (G_OBJECT (spw), "update")) {
354 return;
355 }
356 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
358 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
359 if (!desktop) {
360 return;
361 }
362 SPDocument *document = SP_DT_DOCUMENT (desktop);
363 Inkscape::Selection *selection = SP_DT_SELECTION (desktop);
365 GSList const *items = selection->itemList();
367 switch (psel->mode) {
369 case SP_PAINT_SELECTOR_MODE_EMPTY:
370 // This should not happen.
371 g_warning ( "file %s: line %d: Paint %d should not emit 'changed'",
372 __FILE__, __LINE__, psel->mode);
373 break;
374 case SP_PAINT_SELECTOR_MODE_MULTIPLE:
375 // This happens when you switch multiple objects with different gradients to flat color;
376 // nothing to do here.
377 break;
379 case SP_PAINT_SELECTOR_MODE_NONE:
380 {
381 SPCSSAttr *css = sp_repr_css_attr_new ();
382 sp_repr_css_set_property (css, "fill", "none");
384 sp_desktop_set_style (desktop, css);
386 sp_repr_css_attr_unref (css);
388 sp_document_done (document);
389 break;
390 }
392 case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
393 case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
394 {
395 sp_paint_selector_set_flat_color (psel, desktop, "fill", "fill-opacity");
396 sp_document_maybe_done (SP_DT_DOCUMENT(desktop), undo_label);
398 // on release, toggle undo_label so that the next drag will not be lumped with this one
399 if (undo_label == undo_label_1)
400 undo_label = undo_label_2;
401 else
402 undo_label = undo_label_1;
404 break;
405 }
407 case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR:
408 case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL:
409 if (items) {
410 SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR
411 ? SP_GRADIENT_TYPE_LINEAR
412 : SP_GRADIENT_TYPE_RADIAL );
414 // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
415 SPCSSAttr *css = sp_repr_css_attr_new();
416 sp_repr_css_set_property(css, "fill-opacity", "1.0");
418 SPGradient *vector = sp_paint_selector_get_gradient_vector(psel);
419 if (!vector) {
420 /* No vector in paint selector should mean that we just changed mode */
422 SPStyle *query = sp_style_new ();
423 int result = objects_query_fillstroke ((GSList *) items, query, true);
424 guint32 common_rgb = 0;
425 if (result == QUERY_STYLE_MULTIPLE_SAME) {
426 if (query->fill.type != SP_PAINT_TYPE_COLOR) {
427 common_rgb = sp_desktop_get_color(desktop, true);
428 } else {
429 common_rgb = sp_color_get_rgba32_ualpha(&query->fill.value.color, 0xff);
430 }
431 vector = sp_document_default_gradient_vector(document, common_rgb);
432 }
433 g_free (query);
435 for (GSList const *i = items; i != NULL; i = i->next) {
436 //FIXME: see above
437 sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style");
439 if (!vector) {
440 sp_item_set_gradient(SP_ITEM(i->data),
441 sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), true),
442 gradient_type, true);
443 } else {
444 sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
445 }
446 }
447 } else {
448 /* We have changed from another gradient type, or modified spread/units within
449 * this gradient type. */
450 vector = sp_gradient_ensure_vector_normalized (vector);
451 for (GSList const *i = items; i != NULL; i = i->next) {
452 //FIXME: see above
453 sp_repr_css_change_recursive (SP_OBJECT_REPR (i->data), css, "style");
455 SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
456 sp_gradient_selector_attrs_to_gradient (gr, psel);
457 }
458 }
460 sp_repr_css_attr_unref (css);
462 sp_document_done (document);
463 }
464 break;
466 case SP_PAINT_SELECTOR_MODE_PATTERN:
468 if (items) {
470 SPPattern *pattern = sp_paint_selector_get_pattern (psel);
471 if (!pattern) {
473 /* No Pattern in paint selector should mean that we just
474 * changed mode - dont do jack.
475 */
477 } else {
478 Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern);
479 SPCSSAttr *css = sp_repr_css_attr_new ();
480 gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id"));
481 sp_repr_css_set_property (css, "fill", urltext);
483 // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
484 sp_repr_css_set_property(css, "fill-opacity", "1.0");
486 // cannot just call sp_desktop_set_style, because we don't want to touch those
487 // objects who already have the same root pattern but through a different href
488 // chain. FIXME: move this to a sp_item_set_pattern
489 for (GSList const *i = items; i != NULL; i = i->next) {
490 SPObject *selobj = SP_OBJECT (i->data);
492 SPStyle *style = SP_OBJECT_STYLE (selobj);
493 if (style && style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
494 SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (selobj);
495 if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern)
496 // only if this object's pattern is not rooted in our selected pattern, apply
497 continue;
498 }
500 sp_desktop_apply_css_recursive (selobj, css, true);
501 }
503 sp_repr_css_attr_unref (css);
504 g_free (urltext);
506 } // end if
508 sp_document_done (document);
510 } // end if
512 break;
514 case SP_PAINT_SELECTOR_MODE_UNSET:
515 if (items) {
516 SPCSSAttr *css = sp_repr_css_attr_new ();
517 sp_repr_css_unset_property (css, "fill");
519 sp_desktop_set_style (desktop, css);
520 sp_repr_css_attr_unref (css);
522 sp_document_done (document);
523 }
524 break;
526 default:
527 g_warning ( "file %s: line %d: Paint selector should not be in "
528 "mode %d",
529 __FILE__, __LINE__, psel->mode );
530 break;
531 }
533 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
534 }
537 /*
538 Local Variables:
539 mode:c++
540 c-file-style:"stroustrup"
541 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
542 indent-tabs-mode:nil
543 fill-column:99
544 End:
545 */
546 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :