9933887f31f6e7f9d0e045e308c2339c6514ded6
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 {
127 #ifdef SP_FS_VERBOSE
128 g_print ( "Fill style widget constructed: inkscape %p repr %p\n",
129 spw->inkscape, spw->repr );
130 #endif
131 if (spw->inkscape) {
133 sp_fill_style_widget_update (spw);
135 }
137 } // end of sp_fill_style_widget_construct()
139 static void
140 sp_fill_style_widget_modify_selection ( SPWidget *spw,
141 Inkscape::Selection *selection,
142 guint flags,
143 SPPaintSelector *psel )
144 {
145 if (flags & ( SP_OBJECT_MODIFIED_FLAG |
146 SP_OBJECT_PARENT_MODIFIED_FLAG |
147 SP_OBJECT_STYLE_MODIFIED_FLAG) )
148 {
149 sp_fill_style_widget_update (spw);
150 }
151 }
153 static void
154 sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape,
155 SPDesktop *desktop,
156 SPWidget *spw )
157 {
158 sp_fill_style_widget_update (spw);
159 }
161 static void
162 sp_fill_style_widget_change_selection ( SPWidget *spw,
163 Inkscape::Selection *selection,
164 SPPaintSelector *psel )
165 {
166 sp_fill_style_widget_update (spw);
167 }
169 /**
170 * \param sel Selection to use, or NULL.
171 */
172 static void
173 sp_fill_style_widget_update (SPWidget *spw)
174 {
175 if (g_object_get_data (G_OBJECT (spw), "update"))
176 return;
178 if (g_object_get_data (G_OBJECT (spw), "local")) {
179 g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (FALSE)); // local change; do nothing, but reset the flag
180 return;
181 }
183 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
185 SPPaintSelector *psel = SP_PAINT_SELECTOR (g_object_get_data (G_OBJECT (spw), "paint-selector"));
187 // create temporary style
188 SPStyle *query = sp_style_new ();
189 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
190 int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FILL);
192 switch (result) {
193 case QUERY_STYLE_NOTHING:
194 {
195 /* No paint at all */
196 sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY);
197 break;
198 }
200 case QUERY_STYLE_SINGLE:
201 case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector
202 case QUERY_STYLE_MULTIPLE_SAME:
203 {
204 SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, true);
205 sp_paint_selector_set_mode (psel, pselmode);
207 sp_paint_selector_set_fillrule (psel, query->fill_rule.computed == ART_WIND_RULE_NONZERO?
208 SP_PAINT_SELECTOR_FILLRULE_NONZERO : SP_PAINT_SELECTOR_FILLRULE_EVENODD);
210 if (query->fill.set && query->fill.type == SP_PAINT_TYPE_COLOR) {
211 gfloat d[3];
212 sp_color_get_rgb_floatv (&query->fill.value.color, d);
213 SPColor color;
214 sp_color_set_rgb_float (&color, d[0], d[1], d[2]);
215 sp_paint_selector_set_color_alpha (psel, &color, SP_SCALE24_TO_FLOAT (query->fill_opacity.value));
217 } else if (query->fill.set && query->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
219 SPPaintServer *server = SP_STYLE_FILL_SERVER (query);
221 if (SP_IS_LINEARGRADIENT (server)) {
222 SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
223 sp_paint_selector_set_gradient_linear (psel, vector);
225 SPLinearGradient *lg = SP_LINEARGRADIENT (server);
226 sp_paint_selector_set_gradient_properties (psel,
227 SP_GRADIENT_UNITS (lg),
228 SP_GRADIENT_SPREAD (lg));
229 } else if (SP_IS_RADIALGRADIENT (server)) {
230 SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
231 sp_paint_selector_set_gradient_radial (psel, vector);
233 SPRadialGradient *rg = SP_RADIALGRADIENT (server);
234 sp_paint_selector_set_gradient_properties (psel,
235 SP_GRADIENT_UNITS (rg),
236 SP_GRADIENT_SPREAD (rg));
237 } else if (SP_IS_PATTERN (server)) {
238 SPPattern *pat = pattern_getroot (SP_PATTERN (server));
239 sp_update_pattern_list (psel, pat);
240 }
241 }
242 break;
243 }
245 case QUERY_STYLE_MULTIPLE_DIFFERENT:
246 {
247 sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE);
248 break;
249 }
250 }
252 g_free (query);
254 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
256 }
259 static void
260 sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
261 SPPaintSelectorMode mode,
262 SPWidget *spw )
263 {
264 if (g_object_get_data (G_OBJECT (spw), "update"))
265 return;
267 /* TODO: Does this work? */
268 /* TODO: Not really, here we have to get old color back from object */
269 /* Instead of relying on paint widget having meaningful colors set */
270 sp_fill_style_widget_paint_changed (psel, spw);
271 }
273 static void
274 sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
275 SPPaintSelectorFillRule mode,
276 SPWidget *spw )
277 {
278 if (g_object_get_data (G_OBJECT (spw), "update"))
279 return;
281 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
283 SPCSSAttr *css = sp_repr_css_attr_new ();
284 sp_repr_css_set_property (css, "fill-rule", mode == SP_PAINT_SELECTOR_FILLRULE_EVENODD? "evenodd":"nonzero");
286 sp_desktop_set_style (desktop, css);
288 sp_repr_css_attr_unref (css);
290 sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_FILL_STROKE,
291 _("Change fill rule"));
292 }
294 static gchar *undo_label_1 = "fill:flatcolor:1";
295 static gchar *undo_label_2 = "fill:flatcolor:2";
296 static gchar *undo_label = undo_label_1;
298 /**
299 This is called repeatedly while you are dragging a color slider, only for flat color
300 modes. Previously it set the color in style but did not update the repr for efficiency, however
301 this was flakey and didn't buy us almost anything. So now it does the same as _changed, except
302 lumps all its changes for undo.
303 */
304 static void
305 sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw)
306 {
307 if (!spw->inkscape) {
308 return;
309 }
311 if (g_object_get_data (G_OBJECT (spw), "update")) {
312 return;
313 }
315 if (g_object_get_data (G_OBJECT (spw), "local")) {
316 // previous local flag not cleared yet;
317 // this means dragged events come too fast, so we better skip this one to speed up display
318 // (it's safe to do this in any case)
319 return;
320 }
322 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
324 switch (psel->mode) {
326 case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
327 case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
328 {
329 sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "fill", "fill-opacity");
330 sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE,
331 _("Set fill color"));
332 g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (TRUE)); // local change, do not update from selection
333 break;
334 }
336 default:
337 g_warning ( "file %s: line %d: Paint %d should not emit 'dragged'",
338 __FILE__, __LINE__, psel->mode );
339 break;
341 }
342 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
343 }
346 /**
347 This is called (at least) when:
348 1 paint selector mode is switched (e.g. flat color -> gradient)
349 2 you finished dragging a gradient node and released mouse
350 3 you changed a gradient selector parameter (e.g. spread)
351 Must update repr.
352 */
353 static void
354 sp_fill_style_widget_paint_changed ( SPPaintSelector *psel,
355 SPWidget *spw )
356 {
357 if (g_object_get_data (G_OBJECT (spw), "update")) {
358 return;
359 }
360 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
362 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
363 if (!desktop) {
364 return;
365 }
366 SPDocument *document = sp_desktop_document (desktop);
367 Inkscape::Selection *selection = sp_desktop_selection (desktop);
369 GSList const *items = selection->itemList();
371 switch (psel->mode) {
373 case SP_PAINT_SELECTOR_MODE_EMPTY:
374 // This should not happen.
375 g_warning ( "file %s: line %d: Paint %d should not emit 'changed'",
376 __FILE__, __LINE__, psel->mode);
377 break;
378 case SP_PAINT_SELECTOR_MODE_MULTIPLE:
379 // This happens when you switch multiple objects with different gradients to flat color;
380 // nothing to do here.
381 break;
383 case SP_PAINT_SELECTOR_MODE_NONE:
384 {
385 SPCSSAttr *css = sp_repr_css_attr_new ();
386 sp_repr_css_set_property (css, "fill", "none");
388 sp_desktop_set_style (desktop, css);
390 sp_repr_css_attr_unref (css);
392 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
393 _("Remove fill"));
394 break;
395 }
397 case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
398 case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
399 {
400 // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed; here it results in losing release events
401 sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(desktop), 0);
403 sp_paint_selector_set_flat_color (psel, desktop, "fill", "fill-opacity");
404 sp_document_maybe_done (sp_desktop_document(desktop), undo_label, SP_VERB_DIALOG_FILL_STROKE,
405 _("Set fill color"));
406 // resume interruptibility
407 sp_canvas_end_forced_full_redraws(sp_desktop_canvas(desktop));
409 // on release, toggle undo_label so that the next drag will not be lumped with this one
410 if (undo_label == undo_label_1)
411 undo_label = undo_label_2;
412 else
413 undo_label = undo_label_1;
415 break;
416 }
418 case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR:
419 case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL:
420 if (items) {
421 SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR
422 ? SP_GRADIENT_TYPE_LINEAR
423 : SP_GRADIENT_TYPE_RADIAL );
425 // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
426 SPCSSAttr *css = sp_repr_css_attr_new();
427 sp_repr_css_set_property(css, "fill-opacity", "1.0");
429 SPGradient *vector = sp_paint_selector_get_gradient_vector(psel);
430 if (!vector) {
431 /* No vector in paint selector should mean that we just changed mode */
433 SPStyle *query = sp_style_new ();
434 int result = objects_query_fillstroke ((GSList *) items, query, true);
435 guint32 common_rgb = 0;
436 if (result == QUERY_STYLE_MULTIPLE_SAME) {
437 if (query->fill.type != SP_PAINT_TYPE_COLOR) {
438 common_rgb = sp_desktop_get_color(desktop, true);
439 } else {
440 common_rgb = sp_color_get_rgba32_ualpha(&query->fill.value.color, 0xff);
441 }
442 vector = sp_document_default_gradient_vector(document, common_rgb);
443 }
444 g_free (query);
446 for (GSList const *i = items; i != NULL; i = i->next) {
447 //FIXME: see above
448 sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style");
450 if (!vector) {
451 sp_item_set_gradient(SP_ITEM(i->data),
452 sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), true),
453 gradient_type, true);
454 } else {
455 sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
456 }
457 }
458 } else {
459 /* We have changed from another gradient type, or modified spread/units within
460 * this gradient type. */
461 vector = sp_gradient_ensure_vector_normalized (vector);
462 for (GSList const *i = items; i != NULL; i = i->next) {
463 //FIXME: see above
464 sp_repr_css_change_recursive (SP_OBJECT_REPR (i->data), css, "style");
466 SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
467 sp_gradient_selector_attrs_to_gradient (gr, psel);
468 }
469 }
471 sp_repr_css_attr_unref (css);
473 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
474 _("Set gradient on fill"));
475 }
476 break;
478 case SP_PAINT_SELECTOR_MODE_PATTERN:
480 if (items) {
482 SPPattern *pattern = sp_paint_selector_get_pattern (psel);
483 if (!pattern) {
485 /* No Pattern in paint selector should mean that we just
486 * changed mode - dont do jack.
487 */
489 } else {
490 Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern);
491 SPCSSAttr *css = sp_repr_css_attr_new ();
492 gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id"));
493 sp_repr_css_set_property (css, "fill", urltext);
495 // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
496 sp_repr_css_set_property(css, "fill-opacity", "1.0");
498 // cannot just call sp_desktop_set_style, because we don't want to touch those
499 // objects who already have the same root pattern but through a different href
500 // chain. FIXME: move this to a sp_item_set_pattern
501 for (GSList const *i = items; i != NULL; i = i->next) {
502 SPObject *selobj = SP_OBJECT (i->data);
504 SPStyle *style = SP_OBJECT_STYLE (selobj);
505 if (style && style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
506 SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (selobj);
507 if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern)
508 // only if this object's pattern is not rooted in our selected pattern, apply
509 continue;
510 }
512 sp_desktop_apply_css_recursive (selobj, css, true);
513 }
515 sp_repr_css_attr_unref (css);
516 g_free (urltext);
518 } // end if
520 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
521 _("Set pattern on fill"));
523 } // end if
525 break;
527 case SP_PAINT_SELECTOR_MODE_UNSET:
528 if (items) {
529 SPCSSAttr *css = sp_repr_css_attr_new ();
530 sp_repr_css_unset_property (css, "fill");
532 sp_desktop_set_style (desktop, css);
533 sp_repr_css_attr_unref (css);
535 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
536 _("Unset fill"));
537 }
538 break;
540 default:
541 g_warning ( "file %s: line %d: Paint selector should not be in "
542 "mode %d",
543 __FILE__, __LINE__, psel->mode );
544 break;
545 }
547 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
548 }
551 /*
552 Local Variables:
553 mode:c++
554 c-file-style:"stroustrup"
555 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
556 indent-tabs-mode:nil
557 fill-column:99
558 End:
559 */
560 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :