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>
40 // These can be deleted once we sort out the libart dependence.
42 #define ART_WIND_RULE_NONZERO 0
44 static void sp_fill_style_widget_construct ( SPWidget *spw,
45 SPPaintSelector *psel );
47 static void sp_fill_style_widget_modify_selection ( SPWidget *spw,
48 Inkscape::Selection *selection,
49 guint flags,
50 SPPaintSelector *psel );
52 static void sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape, SPDesktop *desktop, SPWidget *spw );
54 static void sp_fill_style_widget_change_selection ( SPWidget *spw,
55 Inkscape::Selection *selection,
56 SPPaintSelector *psel );
58 static void sp_fill_style_widget_update (SPWidget *spw);
60 static void sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
61 SPPaintSelectorMode mode,
62 SPWidget *spw );
63 static void sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
64 SPPaintSelectorFillRule mode,
65 SPWidget *spw );
67 static void sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw );
68 static void sp_fill_style_widget_paint_changed (SPPaintSelector *psel, SPWidget *spw );
70 GtkWidget *
71 sp_fill_style_widget_new (void)
72 {
73 GtkWidget *spw = sp_widget_new_global (INKSCAPE);
75 GtkWidget *vb = gtk_vbox_new (FALSE, 0);
76 gtk_widget_show (vb);
77 gtk_container_add (GTK_CONTAINER (spw), vb);
79 GtkWidget *psel = sp_paint_selector_new (true); // with fillrule selector
80 gtk_widget_show (psel);
81 gtk_box_pack_start (GTK_BOX (vb), psel, TRUE, TRUE, 0);
82 g_object_set_data (G_OBJECT (spw), "paint-selector", psel);
84 g_signal_connect ( G_OBJECT (psel), "mode_changed",
85 G_CALLBACK (sp_fill_style_widget_paint_mode_changed),
86 spw );
88 g_signal_connect ( G_OBJECT (psel), "dragged",
89 G_CALLBACK (sp_fill_style_widget_paint_dragged),
90 spw );
92 g_signal_connect ( G_OBJECT (psel), "changed",
93 G_CALLBACK (sp_fill_style_widget_paint_changed),
94 spw );
96 g_signal_connect ( G_OBJECT (psel), "fillrule_changed",
97 G_CALLBACK (sp_fill_style_widget_fillrule_changed),
98 spw );
101 g_signal_connect ( G_OBJECT (spw), "construct",
102 G_CALLBACK (sp_fill_style_widget_construct), psel);
104 //FIXME: switch these from spw signals to global inkscape object signals; spw just retranslates
105 //those anyway; then eliminate spw
106 g_signal_connect ( G_OBJECT (spw), "modify_selection",
107 G_CALLBACK (sp_fill_style_widget_modify_selection), psel);
109 g_signal_connect ( G_OBJECT (spw), "change_selection",
110 G_CALLBACK (sp_fill_style_widget_change_selection), psel);
112 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_fill_style_widget_change_subselection), spw);
114 sp_fill_style_widget_update (SP_WIDGET (spw));
116 return spw;
118 } // end of sp_fill_style_widget_new()
122 static void
123 sp_fill_style_widget_construct ( SPWidget *spw, SPPaintSelector *psel )
124 {
126 #ifdef SP_FS_VERBOSE
127 g_print ( "Fill style widget constructed: inkscape %p repr %p\n",
128 spw->inkscape, spw->repr );
129 #endif
130 if (spw->inkscape) {
132 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 if (flags & ( SP_OBJECT_MODIFIED_FLAG |
145 SP_OBJECT_PARENT_MODIFIED_FLAG |
146 SP_OBJECT_STYLE_MODIFIED_FLAG) )
147 {
148 sp_fill_style_widget_update (spw);
149 }
150 }
152 static void
153 sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape,
154 SPDesktop *desktop,
155 SPWidget *spw )
156 {
157 sp_fill_style_widget_update (spw);
158 }
160 static void
161 sp_fill_style_widget_change_selection ( SPWidget *spw,
162 Inkscape::Selection *selection,
163 SPPaintSelector *psel )
164 {
165 sp_fill_style_widget_update (spw);
166 }
168 /**
169 * \param sel Selection to use, or NULL.
170 */
171 static void
172 sp_fill_style_widget_update (SPWidget *spw)
173 {
174 if (g_object_get_data (G_OBJECT (spw), "update"))
175 return;
177 if (g_object_get_data (G_OBJECT (spw), "local")) {
178 g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (FALSE)); // local change; do nothing, but reset the flag
179 return;
180 }
182 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
184 SPPaintSelector *psel = SP_PAINT_SELECTOR (g_object_get_data (G_OBJECT (spw), "paint-selector"));
186 // create temporary style
187 SPStyle *query = sp_style_new ();
188 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
189 int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FILL);
191 switch (result) {
192 case QUERY_STYLE_NOTHING:
193 {
194 /* No paint at all */
195 sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY);
196 break;
197 }
199 case QUERY_STYLE_SINGLE:
200 case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector
201 case QUERY_STYLE_MULTIPLE_SAME:
202 {
203 SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, true);
204 sp_paint_selector_set_mode (psel, pselmode);
206 sp_paint_selector_set_fillrule (psel, query->fill_rule.computed == ART_WIND_RULE_NONZERO?
207 SP_PAINT_SELECTOR_FILLRULE_NONZERO : SP_PAINT_SELECTOR_FILLRULE_EVENODD);
209 if (query->fill.set && query->fill.type == SP_PAINT_TYPE_COLOR) {
210 gfloat d[3];
211 sp_color_get_rgb_floatv (&query->fill.value.color, d);
212 SPColor color;
213 sp_color_set_rgb_float (&color, d[0], d[1], d[2]);
214 sp_paint_selector_set_color_alpha (psel, &color, SP_SCALE24_TO_FLOAT (query->fill_opacity.value));
216 } else if (query->fill.set && query->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
218 SPPaintServer *server = SP_STYLE_FILL_SERVER (query);
220 if (SP_IS_LINEARGRADIENT (server)) {
221 SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
222 sp_paint_selector_set_gradient_linear (psel, vector);
224 SPLinearGradient *lg = SP_LINEARGRADIENT (server);
225 sp_paint_selector_set_gradient_properties (psel,
226 SP_GRADIENT_UNITS (lg),
227 SP_GRADIENT_SPREAD (lg));
228 } else if (SP_IS_RADIALGRADIENT (server)) {
229 SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
230 sp_paint_selector_set_gradient_radial (psel, vector);
232 SPRadialGradient *rg = SP_RADIALGRADIENT (server);
233 sp_paint_selector_set_gradient_properties (psel,
234 SP_GRADIENT_UNITS (rg),
235 SP_GRADIENT_SPREAD (rg));
236 } else if (SP_IS_PATTERN (server)) {
237 SPPattern *pat = pattern_getroot (SP_PATTERN (server));
238 sp_update_pattern_list (psel, pat);
239 }
240 }
241 break;
242 }
244 case QUERY_STYLE_MULTIPLE_DIFFERENT:
245 {
246 sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE);
247 break;
248 }
249 }
251 g_free (query);
253 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
255 }
258 static void
259 sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
260 SPPaintSelectorMode mode,
261 SPWidget *spw )
262 {
263 if (g_object_get_data (G_OBJECT (spw), "update"))
264 return;
266 /* TODO: Does this work? */
267 /* TODO: Not really, here we have to get old color back from object */
268 /* Instead of relying on paint widget having meaningful colors set */
269 sp_fill_style_widget_paint_changed (psel, spw);
270 }
272 static void
273 sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
274 SPPaintSelectorFillRule mode,
275 SPWidget *spw )
276 {
277 if (g_object_get_data (G_OBJECT (spw), "update"))
278 return;
280 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
282 SPCSSAttr *css = sp_repr_css_attr_new ();
283 sp_repr_css_set_property (css, "fill-rule", mode == SP_PAINT_SELECTOR_FILLRULE_EVENODD? "evenodd":"nonzero");
285 sp_desktop_set_style (desktop, css);
287 sp_repr_css_attr_unref (css);
289 sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_FILL_STROKE,
290 _("Change fill rule"));
291 }
293 static gchar *undo_label_1 = "fill:flatcolor:1";
294 static gchar *undo_label_2 = "fill:flatcolor:2";
295 static gchar *undo_label = undo_label_1;
297 /**
298 This is called repeatedly while you are dragging a color slider, only for flat color
299 modes. Previously it set the color in style but did not update the repr for efficiency, however
300 this was flakey and didn't buy us almost anything. So now it does the same as _changed, except
301 lumps all its changes for undo.
302 */
303 static void
304 sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw)
305 {
306 if (!spw->inkscape) {
307 return;
308 }
310 if (g_object_get_data (G_OBJECT (spw), "update")) {
311 return;
312 }
314 if (g_object_get_data (G_OBJECT (spw), "local")) {
315 // previous local flag not cleared yet;
316 // this means dragged events come too fast, so we better skip this one to speed up display
317 // (it's safe to do this in any case)
318 return;
319 }
321 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
323 switch (psel->mode) {
325 case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
326 case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
327 {
328 sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "fill", "fill-opacity");
329 sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE,
330 _("Set fill color"));
331 g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (TRUE)); // local change, do not update from selection
332 break;
333 }
335 default:
336 g_warning ( "file %s: line %d: Paint %d should not emit 'dragged'",
337 __FILE__, __LINE__, psel->mode );
338 break;
340 }
341 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
342 }
345 /**
346 This is called (at least) when:
347 1 paint selector mode is switched (e.g. flat color -> gradient)
348 2 you finished dragging a gradient node and released mouse
349 3 you changed a gradient selector parameter (e.g. spread)
350 Must update repr.
351 */
352 static void
353 sp_fill_style_widget_paint_changed ( SPPaintSelector *psel,
354 SPWidget *spw )
355 {
356 if (g_object_get_data (G_OBJECT (spw), "update")) {
357 return;
358 }
359 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
361 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
362 if (!desktop) {
363 return;
364 }
365 SPDocument *document = sp_desktop_document (desktop);
366 Inkscape::Selection *selection = sp_desktop_selection (desktop);
368 GSList const *items = selection->itemList();
370 switch (psel->mode) {
372 case SP_PAINT_SELECTOR_MODE_EMPTY:
373 // This should not happen.
374 g_warning ( "file %s: line %d: Paint %d should not emit 'changed'",
375 __FILE__, __LINE__, psel->mode);
376 break;
377 case SP_PAINT_SELECTOR_MODE_MULTIPLE:
378 // This happens when you switch multiple objects with different gradients to flat color;
379 // nothing to do here.
380 break;
382 case SP_PAINT_SELECTOR_MODE_NONE:
383 {
384 SPCSSAttr *css = sp_repr_css_attr_new ();
385 sp_repr_css_set_property (css, "fill", "none");
387 sp_desktop_set_style (desktop, css);
389 sp_repr_css_attr_unref (css);
391 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
392 _("Remove fill"));
393 break;
394 }
396 case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
397 case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
398 {
399 sp_paint_selector_set_flat_color (psel, desktop, "fill", "fill-opacity");
400 sp_document_maybe_done (sp_desktop_document(desktop), undo_label, SP_VERB_DIALOG_FILL_STROKE,
401 _("Set fill color"));
403 // on release, toggle undo_label so that the next drag will not be lumped with this one
404 if (undo_label == undo_label_1)
405 undo_label = undo_label_2;
406 else
407 undo_label = undo_label_1;
409 break;
410 }
412 case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR:
413 case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL:
414 if (items) {
415 SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR
416 ? SP_GRADIENT_TYPE_LINEAR
417 : SP_GRADIENT_TYPE_RADIAL );
419 // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
420 SPCSSAttr *css = sp_repr_css_attr_new();
421 sp_repr_css_set_property(css, "fill-opacity", "1.0");
423 SPGradient *vector = sp_paint_selector_get_gradient_vector(psel);
424 if (!vector) {
425 /* No vector in paint selector should mean that we just changed mode */
427 SPStyle *query = sp_style_new ();
428 int result = objects_query_fillstroke ((GSList *) items, query, true);
429 guint32 common_rgb = 0;
430 if (result == QUERY_STYLE_MULTIPLE_SAME) {
431 if (query->fill.type != SP_PAINT_TYPE_COLOR) {
432 common_rgb = sp_desktop_get_color(desktop, true);
433 } else {
434 common_rgb = sp_color_get_rgba32_ualpha(&query->fill.value.color, 0xff);
435 }
436 vector = sp_document_default_gradient_vector(document, common_rgb);
437 }
438 g_free (query);
440 for (GSList const *i = items; i != NULL; i = i->next) {
441 //FIXME: see above
442 sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style");
444 if (!vector) {
445 sp_item_set_gradient(SP_ITEM(i->data),
446 sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), true),
447 gradient_type, true);
448 } else {
449 sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
450 }
451 }
452 } else {
453 /* We have changed from another gradient type, or modified spread/units within
454 * this gradient type. */
455 vector = sp_gradient_ensure_vector_normalized (vector);
456 for (GSList const *i = items; i != NULL; i = i->next) {
457 //FIXME: see above
458 sp_repr_css_change_recursive (SP_OBJECT_REPR (i->data), css, "style");
460 SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
461 sp_gradient_selector_attrs_to_gradient (gr, psel);
462 }
463 }
465 sp_repr_css_attr_unref (css);
467 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
468 _("Set gradient on fill"));
469 }
470 break;
472 case SP_PAINT_SELECTOR_MODE_PATTERN:
474 if (items) {
476 SPPattern *pattern = sp_paint_selector_get_pattern (psel);
477 if (!pattern) {
479 /* No Pattern in paint selector should mean that we just
480 * changed mode - dont do jack.
481 */
483 } else {
484 Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern);
485 SPCSSAttr *css = sp_repr_css_attr_new ();
486 gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id"));
487 sp_repr_css_set_property (css, "fill", urltext);
489 // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
490 sp_repr_css_set_property(css, "fill-opacity", "1.0");
492 // cannot just call sp_desktop_set_style, because we don't want to touch those
493 // objects who already have the same root pattern but through a different href
494 // chain. FIXME: move this to a sp_item_set_pattern
495 for (GSList const *i = items; i != NULL; i = i->next) {
496 SPObject *selobj = SP_OBJECT (i->data);
498 SPStyle *style = SP_OBJECT_STYLE (selobj);
499 if (style && style->fill.type == SP_PAINT_TYPE_PAINTSERVER) {
500 SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (selobj);
501 if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern)
502 // only if this object's pattern is not rooted in our selected pattern, apply
503 continue;
504 }
506 sp_desktop_apply_css_recursive (selobj, css, true);
507 }
509 sp_repr_css_attr_unref (css);
510 g_free (urltext);
512 } // end if
514 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
515 _("Set pattern on fill"));
517 } // end if
519 break;
521 case SP_PAINT_SELECTOR_MODE_UNSET:
522 if (items) {
523 SPCSSAttr *css = sp_repr_css_attr_new ();
524 sp_repr_css_unset_property (css, "fill");
526 sp_desktop_set_style (desktop, css);
527 sp_repr_css_attr_unref (css);
529 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE,
530 _("Unset fill"));
531 }
532 break;
534 default:
535 g_warning ( "file %s: line %d: Paint selector should not be in "
536 "mode %d",
537 __FILE__, __LINE__, psel->mode );
538 break;
539 }
541 g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
542 }
545 /*
546 Local Variables:
547 mode:c++
548 c-file-style:"stroustrup"
549 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
550 indent-tabs-mode:nil
551 fill-column:99
552 End:
553 */
554 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :