Code

1efae59e1a56da5c1ffc20843f6618d0cac78b76
[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>
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 )
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 )
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     }
151 static void
152 sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape,
153                                         SPDesktop *desktop,
154                                         SPWidget *spw )
156     sp_fill_style_widget_update (spw);
159 static void
160 sp_fill_style_widget_change_selection ( SPWidget *spw,
161                                         Inkscape::Selection *selection,
162                                         SPPaintSelector *psel )
164     sp_fill_style_widget_update (spw);
167 /**
168 * \param sel Selection to use, or NULL.
169 */
170 static void
171 sp_fill_style_widget_update (SPWidget *spw)
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));
257 static void
258 sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
259                                           SPPaintSelectorMode mode,
260                                           SPWidget *spw )
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);
271 static void
272 sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
273                                           SPPaintSelectorFillRule mode,
274                                           SPWidget *spw )
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);
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)
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_desktop_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));
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 )
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_desktop_document (desktop);
363     Inkscape::Selection *selection = sp_desktop_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_desktop_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));
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 :