Code

change API: separate functions creating a blur filter, one for a given item, another...
[inkscape.git] / src / dialogs / fill-style.cpp
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 )
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 )
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     }
152 static void
153 sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape,
154                                         SPDesktop *desktop,
155                                         SPWidget *spw )
157     sp_fill_style_widget_update (spw);
160 static void
161 sp_fill_style_widget_change_selection ( SPWidget *spw,
162                                         Inkscape::Selection *selection,
163                                         SPPaintSelector *psel )
165     sp_fill_style_widget_update (spw);
168 /**
169 * \param sel Selection to use, or NULL.
170 */
171 static void
172 sp_fill_style_widget_update (SPWidget *spw)
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));
258 static void
259 sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
260                                           SPPaintSelectorMode mode,
261                                           SPWidget *spw )
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);
272 static void
273 sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
274                                           SPPaintSelectorFillRule mode,
275                                           SPWidget *spw )
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"));
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)
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));
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 )
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));
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 :