Code

more unreffing temporary styles properly
[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 )
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 )
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     }
153 static void
154 sp_fill_style_widget_change_subselection ( Inkscape::Application *inkscape,
155                                         SPDesktop *desktop,
156                                         SPWidget *spw )
158     sp_fill_style_widget_update (spw);
161 static void
162 sp_fill_style_widget_change_selection ( SPWidget *spw,
163                                         Inkscape::Selection *selection,
164                                         SPPaintSelector *psel )
166     sp_fill_style_widget_update (spw);
169 /**
170 * \param sel Selection to use, or NULL.
171 */
172 static void
173 sp_fill_style_widget_update (SPWidget *spw)
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     sp_style_unref(query);
254     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
259 static void
260 sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
261                                           SPPaintSelectorMode mode,
262                                           SPWidget *spw )
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);
273 static void
274 sp_fill_style_widget_fillrule_changed ( SPPaintSelector *psel,
275                                           SPPaintSelectorFillRule mode,
276                                           SPWidget *spw )
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 ();
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                     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.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));
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 :