Code

Initial support for icc color selection including CMYK
[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>
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 )
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 )
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     }
154 static void
155 sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape,
156                                         SPDesktop *desktop,
157                                         SPWidget *spw )
159     (void)inkscape;
160     sp_fill_style_widget_update (spw);
163 static void
164 sp_fill_style_widget_change_selection ( SPWidget *spw,
165                                         Inkscape::Selection *selection,
166                                         SPPaintSelector *psel )
168     (void)selection;
169     sp_fill_style_widget_update (spw);
172 /**
173 * \param sel Selection to use, or NULL.
174 */
175 static void
176 sp_fill_style_widget_update (SPWidget *spw)
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                 sp_paint_selector_set_color_alpha (psel, &query->fill.value.color, SP_SCALE24_TO_FLOAT (query->fill_opacity.value));
215             } else if (query->fill.set && query->fill.isPaintserver()) {
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     sp_style_unref(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     (void)mode;
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     (void)psel;
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"));
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)
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));
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 )
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 (SP_ACTIVE_DOCUMENT);
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.isColor()) {
438                             common_rgb = sp_desktop_get_color(desktop, true);
439                         } else {
440                             common_rgb = query->fill.value.color.toRGBA32( 0xff );
441                         }
442                         vector = sp_document_default_gradient_vector(document, common_rgb);
443                     }
444                     sp_style_unref(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.isPaintserver()) {
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));
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 :