1dfaea5461b132052b085d0d9161babac0bba0d9
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>
37 #include <glibmm/i18n.h>
38 #include <display/sp-canvas.h>
41 // These can be deleted once we sort out the libart dependence.
43 #define ART_WIND_RULE_NONZERO 0
45 static void sp_fill_style_widget_construct ( SPWidget *spw,
46 SPPaintSelector *psel );
48 static void sp_fill_style_widget_modify_selection ( SPWidget *spw,
49 Inkscape::Selection *selection,
50 guint flags,
51 SPPaintSelector *psel );
53 static void sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape, SPDesktop *desktop, SPWidget *spw );
55 static void sp_fill_style_widget_change_selection ( SPWidget *spw,
56 Inkscape::Selection *selection,
57 SPPaintSelector *psel );
59 static void sp_fill_style_widget_update (SPWidget *spw);
61 static void sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
62 SPPaintSelectorMode mode,
63 SPWidget *spw );
64 static void sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
65 SPPaintSelectorFillRule mode,
66 SPWidget *spw );
68 static void sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw );
69 static void sp_fill_style_widget_paint_changed (SPPaintSelector *psel, SPWidget *spw );
71 GtkWidget *
72 sp_fill_style_widget_new (void)
73 {
74 GtkWidget *spw = sp_widget_new_global (INKSCAPE);
76 GtkWidget *vb = gtk_vbox_new (FALSE, 0);
77 gtk_widget_show (vb);
78 gtk_container_add (GTK_CONTAINER (spw), vb);
80 GtkWidget *psel = sp_paint_selector_new (true); // with fillrule selector
81 gtk_widget_show (psel);
82 gtk_box_pack_start (GTK_BOX (vb), psel, TRUE, TRUE, 0);
83 g_object_set_data (G_OBJECT (spw), "paint-selector", psel);
85 g_signal_connect ( G_OBJECT (psel), "mode_changed",
86 G_CALLBACK (sp_fill_style_widget_paint_mode_changed),
87 spw );
89 g_signal_connect ( G_OBJECT (psel), "dragged",
90 G_CALLBACK (sp_fill_style_widget_paint_dragged),
91 spw );
93 g_signal_connect ( G_OBJECT (psel), "changed",
94 G_CALLBACK (sp_fill_style_widget_paint_changed),
95 spw );
97 g_signal_connect ( G_OBJECT (psel), "fillrule_changed",
98 G_CALLBACK (sp_fill_style_widget_fillrule_changed),
99 spw );
102 g_signal_connect ( G_OBJECT (spw), "construct",
103 G_CALLBACK (sp_fill_style_widget_construct), psel);
105 //FIXME: switch these from spw signals to global inkscape object signals; spw just retranslates
106 //those anyway; then eliminate spw
107 g_signal_connect ( G_OBJECT (spw), "modify_selection",
108 G_CALLBACK (sp_fill_style_widget_modify_selection), psel);
110 g_signal_connect ( G_OBJECT (spw), "change_selection",
111 G_CALLBACK (sp_fill_style_widget_change_selection), psel);
113 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_fill_style_widget_change_subselection), spw);
115 sp_fill_style_widget_update (SP_WIDGET (spw));
117 return spw;
119 } // end of sp_fill_style_widget_new()
123 static void
124 sp_fill_style_widget_construct ( SPWidget *spw, SPPaintSelector *psel )
125 {
126 (void)psel;
128 #ifdef SP_FS_VERBOSE
129 g_print ( "Fill style widget constructed: inkscape %p repr %p\n",
130 spw->inkscape, spw->repr );
131 #endif
132 if (spw->inkscape) {
133 sp_fill_style_widget_update (spw);
134 }
136 } // end of sp_fill_style_widget_construct()
138 static void
139 sp_fill_style_widget_modify_selection ( SPWidget *spw,
140 Inkscape::Selection *selection,
141 guint flags,
142 SPPaintSelector *psel )
143 {
144 (void)selection;
145 (void)psel;
146 if (flags & ( SP_OBJECT_MODIFIED_FLAG |
147 SP_OBJECT_PARENT_MODIFIED_FLAG |
148 SP_OBJECT_STYLE_MODIFIED_FLAG) )
149 {
150 sp_fill_style_widget_update (spw);
151 }
152 }
154 static void
155 sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape,
156 SPDesktop *desktop,
157 SPWidget *spw )
158 {
159 (void)inkscape;
160 sp_fill_style_widget_update (spw);
161 }
163 static void
164 sp_fill_style_widget_change_selection ( SPWidget *spw,
165 Inkscape::Selection *selection,
166 SPPaintSelector *psel )
167 {
168 (void)selection;
169 sp_fill_style_widget_update (spw);
170 }
172 /**
173 * \param sel Selection to use, or NULL.
174 */
175 static void
176 sp_fill_style_widget_update (SPWidget *spw)
177 {
178 if (g_object_get_data (G_OBJECT (spw), "update"))
179 return;
181 if (g_object_get_data (G_OBJECT (spw), "local")) {
182 g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (FALSE)); // local change; do nothing, but reset the flag
183 return;
184 }
186 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
188 SPPaintSelector *psel = SP_PAINT_SELECTOR (g_object_get_data (G_OBJECT (spw), "paint-selector"));
190 // create temporary style
191 SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
192 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
193 int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FILL);
195 switch (result) {
196 case QUERY_STYLE_NOTHING:
197 {
198 /* No paint at all */
199 sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY);
200 break;
201 }
203 case QUERY_STYLE_SINGLE:
204 case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector
205 case QUERY_STYLE_MULTIPLE_SAME:
206 {
207 SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, true);
208 sp_paint_selector_set_mode (psel, pselmode);
210 sp_paint_selector_set_fillrule (psel, query->fill_rule.computed == ART_WIND_RULE_NONZERO?
211 SP_PAINT_SELECTOR_FILLRULE_NONZERO : SP_PAINT_SELECTOR_FILLRULE_EVENODD);
213 if (query->fill.set && query->fill.isColor()) {
214 gfloat d[3];
215 sp_color_get_rgb_floatv (&query->fill.value.color, d);
216 SPColor color( d[0], d[1], d[2] );
217 sp_paint_selector_set_color_alpha (psel, &color, SP_SCALE24_TO_FLOAT (query->fill_opacity.value));
219 } else if (query->fill.set && query->fill.isPaintserver()) {
221 SPPaintServer *server = SP_STYLE_FILL_SERVER (query);
223 if (SP_IS_LINEARGRADIENT (server)) {
224 SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
225 sp_paint_selector_set_gradient_linear (psel, vector);
227 SPLinearGradient *lg = SP_LINEARGRADIENT (server);
228 sp_paint_selector_set_gradient_properties (psel,
229 SP_GRADIENT_UNITS (lg),
230 SP_GRADIENT_SPREAD (lg));
231 } else if (SP_IS_RADIALGRADIENT (server)) {
232 SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
233 sp_paint_selector_set_gradient_radial (psel, vector);
235 SPRadialGradient *rg = SP_RADIALGRADIENT (server);
236 sp_paint_selector_set_gradient_properties (psel,
237 SP_GRADIENT_UNITS (rg),
238 SP_GRADIENT_SPREAD (rg));
239 } else if (SP_IS_PATTERN (server)) {
240 SPPattern *pat = pattern_getroot (SP_PATTERN (server));
241 sp_update_pattern_list (psel, pat);
242 }
243 }
244 break;
245 }
247 case QUERY_STYLE_MULTIPLE_DIFFERENT:
248 {
249 sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE);
250 break;
251 }
252 }
254 sp_style_unref(query);
256 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
258 }
261 static void
262 sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
263 SPPaintSelectorMode mode,
264 SPWidget *spw )
265 {
266 (void)mode;
267 if (g_object_get_data (G_OBJECT (spw), "update"))
268 return;
270 /* TODO: Does this work? */
271 /* TODO: Not really, here we have to get old color back from object */
272 /* Instead of relying on paint widget having meaningful colors set */
273 sp_fill_style_widget_paint_changed (psel, spw);
274 }
276 static void
277 sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
278 SPPaintSelectorFillRule mode,
279 SPWidget *spw )
280 {
281 (void)psel;
282 if (g_object_get_data (G_OBJECT (spw), "update"))
283 return;
285 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
287 SPCSSAttr *css = sp_repr_css_attr_new ();
288 sp_repr_css_set_property (css, "fill-rule", mode == SP_PAINT_SELECTOR_FILLRULE_EVENODD? "evenodd":"nonzero");
290 sp_desktop_set_style (desktop, css);
292 sp_repr_css_attr_unref (css);
294 sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_FILL_STROKE,
295 _("Change fill rule"));
296 }
298 static gchar *undo_label_1 = "fill:flatcolor:1";
299 static gchar *undo_label_2 = "fill:flatcolor:2";
300 static gchar *undo_label = undo_label_1;
302 /**
303 This is called repeatedly while you are dragging a color slider, only for flat color
304 modes. Previously it set the color in style but did not update the repr for efficiency, however
305 this was flakey and didn't buy us almost anything. So now it does the same as _changed, except
306 lumps all its changes for undo.
307 */
308 static void
309 sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw)
310 {
311 if (!spw->inkscape) {
312 return;
313 }
315 if (g_object_get_data (G_OBJECT (spw), "update")) {
316 return;
317 }
319 if (g_object_get_data (G_OBJECT (spw), "local")) {
320 // previous local flag not cleared yet;
321 // this means dragged events come too fast, so we better skip this one to speed up display
322 // (it's safe to do this in any case)
323 return;
324 }
326 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
328 switch (psel->mode) {
330 case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
331 case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
332 {
333 sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "fill", "fill-opacity");
334 sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE,
335 _("Set fill color"));
336 g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (TRUE)); // local change, do not update from selection
337 break;
338 }
340 default:
341 g_warning ( "file %s: line %d: Paint %d should not emit 'dragged'",
342 __FILE__, __LINE__, psel->mode );
343 break;
345 }
346 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
347 }
350 /**
351 This is called (at least) when:
352 1 paint selector mode is switched (e.g. flat color -> gradient)
353 2 you finished dragging a gradient node and released mouse
354 3 you changed a gradient selector parameter (e.g. spread)
355 Must update repr.
356 */
357 static void
358 sp_fill_style_widget_paint_changed ( SPPaintSelector *psel,
359 SPWidget *spw )
360 {
361 if (g_object_get_data (G_OBJECT (spw), "update")) {
362 return;
363 }
364 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
366 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
367 if (!desktop) {
368 return;
369 }
370 SPDocument *document = sp_desktop_document (desktop);
371 Inkscape::Selection *selection = sp_desktop_selection (desktop);
373 GSList const *items = selection->itemList();
375 switch (psel->mode) {
377 case SP_PAINT_SELECTOR_MODE_EMPTY:
378 // This should not happen.
379 g_warning ( "file %s: line %d: Paint %d should not emit 'changed'",
380 __FILE__, __LINE__, psel->mode);
381 break;
382 case SP_PAINT_SELECTOR_MODE_MULTIPLE:
383 // This happens when you switch multiple objects with different gradients to flat color;
384 // nothing to do here.
385 break;
387 case SP_PAINT_SELECTOR_MODE_NONE:
388 {
389 SPCSSAttr *css = sp_repr_css_attr_new ();
390 sp_repr_css_set_property (css, "fill", "none");
392 sp_desktop_set_style (desktop, css);
394 sp_repr_css_attr_unref (css);
396 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
397 _("Remove fill"));
398 break;
399 }
401 case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
402 case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
403 {
404 // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed; here it results in losing release events
405 sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(desktop), 0);
407 sp_paint_selector_set_flat_color (psel, desktop, "fill", "fill-opacity");
408 sp_document_maybe_done (sp_desktop_document(desktop), undo_label, SP_VERB_DIALOG_FILL_STROKE,
409 _("Set fill color"));
410 // resume interruptibility
411 sp_canvas_end_forced_full_redraws(sp_desktop_canvas(desktop));
413 // on release, toggle undo_label so that the next drag will not be lumped with this one
414 if (undo_label == undo_label_1)
415 undo_label = undo_label_2;
416 else
417 undo_label = undo_label_1;
419 break;
420 }
422 case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR:
423 case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL:
424 if (items) {
425 SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR
426 ? SP_GRADIENT_TYPE_LINEAR
427 : SP_GRADIENT_TYPE_RADIAL );
429 // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
430 SPCSSAttr *css = sp_repr_css_attr_new();
431 sp_repr_css_set_property(css, "fill-opacity", "1.0");
433 SPGradient *vector = sp_paint_selector_get_gradient_vector(psel);
434 if (!vector) {
435 /* No vector in paint selector should mean that we just changed mode */
437 SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
438 int result = objects_query_fillstroke ((GSList *) items, query, true);
439 guint32 common_rgb = 0;
440 if (result == QUERY_STYLE_MULTIPLE_SAME) {
441 if (!query->fill.isColor()) {
442 common_rgb = sp_desktop_get_color(desktop, true);
443 } else {
444 common_rgb = query->fill.value.color.toRGBA32( 0xff );
445 }
446 vector = sp_document_default_gradient_vector(document, common_rgb);
447 }
448 sp_style_unref(query);
450 for (GSList const *i = items; i != NULL; i = i->next) {
451 //FIXME: see above
452 sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style");
454 if (!vector) {
455 sp_item_set_gradient(SP_ITEM(i->data),
456 sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), true),
457 gradient_type, true);
458 } else {
459 sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
460 }
461 }
462 } else {
463 /* We have changed from another gradient type, or modified spread/units within
464 * this gradient type. */
465 vector = sp_gradient_ensure_vector_normalized (vector);
466 for (GSList const *i = items; i != NULL; i = i->next) {
467 //FIXME: see above
468 sp_repr_css_change_recursive (SP_OBJECT_REPR (i->data), css, "style");
470 SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
471 sp_gradient_selector_attrs_to_gradient (gr, psel);
472 }
473 }
475 sp_repr_css_attr_unref (css);
477 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
478 _("Set gradient on fill"));
479 }
480 break;
482 case SP_PAINT_SELECTOR_MODE_PATTERN:
484 if (items) {
486 SPPattern *pattern = sp_paint_selector_get_pattern (psel);
487 if (!pattern) {
489 /* No Pattern in paint selector should mean that we just
490 * changed mode - dont do jack.
491 */
493 } else {
494 Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern);
495 SPCSSAttr *css = sp_repr_css_attr_new ();
496 gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id"));
497 sp_repr_css_set_property (css, "fill", urltext);
499 // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
500 sp_repr_css_set_property(css, "fill-opacity", "1.0");
502 // cannot just call sp_desktop_set_style, because we don't want to touch those
503 // objects who already have the same root pattern but through a different href
504 // chain. FIXME: move this to a sp_item_set_pattern
505 for (GSList const *i = items; i != NULL; i = i->next) {
506 SPObject *selobj = SP_OBJECT (i->data);
508 SPStyle *style = SP_OBJECT_STYLE (selobj);
509 if (style && style->fill.isPaintserver()) {
510 SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (selobj);
511 if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern)
512 // only if this object's pattern is not rooted in our selected pattern, apply
513 continue;
514 }
516 sp_desktop_apply_css_recursive (selobj, css, true);
517 }
519 sp_repr_css_attr_unref (css);
520 g_free (urltext);
522 } // end if
524 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
525 _("Set pattern on fill"));
527 } // end if
529 break;
531 case SP_PAINT_SELECTOR_MODE_UNSET:
532 if (items) {
533 SPCSSAttr *css = sp_repr_css_attr_new ();
534 sp_repr_css_unset_property (css, "fill");
536 sp_desktop_set_style (desktop, css);
537 sp_repr_css_attr_unref (css);
539 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
540 _("Unset fill"));
541 }
542 break;
544 default:
545 g_warning ( "file %s: line %d: Paint selector should not be in "
546 "mode %d",
547 __FILE__, __LINE__, psel->mode );
548 break;
549 }
551 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
552 }
555 /*
556 Local Variables:
557 mode:c++
558 c-file-style:"stroustrup"
559 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
560 indent-tabs-mode:nil
561 fill-column:99
562 End:
563 */
564 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :