Code

Revert recent refactoring changes by johnce because they break the build, which canno...
[inkscape.git] / src / widgets / fill-style.cpp
1 /** @file
2  * @brief  Fill style widget
3  */
4 /* Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   Frank Felfe <innerspace@iname.com>
7  *   bulia byak <buliabyak@users.sf.net>
8  *
9  * Copyright (C) 1999-2005 authors
10  * Copyright (C) 2001-2002 Ximian, Inc.
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #define noSP_FS_VERBOSE
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
21 #include <glibmm/i18n.h>
23 #include "desktop-handles.h"
24 #include "desktop-style.h"
25 #include "display/sp-canvas.h"
26 #include "document-private.h"
27 #include "gradient-chemistry.h"
28 #include "inkscape.h"
29 #include "selection.h"
30 #include "sp-linear-gradient.h"
31 #include "sp-pattern.h"
32 #include "sp-radial-gradient.h"
33 #include "style.h"
34 #include "widgets/paint-selector.h"
35 #include "widgets/sp-widget.h"
36 #include "xml/repr.h"
38 #include "widgets/fill-style.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 #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) {
131         sp_fill_style_widget_update (spw);
132     }
134 } // end of sp_fill_style_widget_construct()
136 static void
137 sp_fill_style_widget_modify_selection( SPWidget *spw,
138                                        Inkscape::Selection */*selection*/,
139                                        guint flags,
140                                        SPPaintSelector */*psel*/ )
142     if (flags & ( SP_OBJECT_MODIFIED_FLAG |
143                   SP_OBJECT_PARENT_MODIFIED_FLAG |
144                   SP_OBJECT_STYLE_MODIFIED_FLAG) )
145     {
146         sp_fill_style_widget_update (spw);
147     }
150 static void
151 sp_fill_style_widget_change_subselection( Inkscape::Application */*inkscape*/,
152                                           SPDesktop */*desktop*/,
153                                           SPWidget *spw )
155     sp_fill_style_widget_update (spw);
158 static void
159 sp_fill_style_widget_change_selection( SPWidget *spw,
160                                        Inkscape::Selection */*selection*/,
161                                        SPPaintSelector */*psel*/ )
163     sp_fill_style_widget_update (spw);
166 /**
167 * \param sel Selection to use, or NULL.
168 */
169 static void
170 sp_fill_style_widget_update (SPWidget *spw)
172     if (g_object_get_data (G_OBJECT (spw), "update"))
173         return;
175     if (g_object_get_data (G_OBJECT (spw), "local")) {
176         g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (FALSE)); // local change; do nothing, but reset the flag
177         return;
178     }
180     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
182     SPPaintSelector *psel = SP_PAINT_SELECTOR (g_object_get_data (G_OBJECT (spw), "paint-selector"));
184     // create temporary style
185     SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
186     // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
187     int result = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FILL); 
189     switch (result) {
190         case QUERY_STYLE_NOTHING:
191         {
192             /* No paint at all */
193             sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_EMPTY);
194             break;
195         }
197         case QUERY_STYLE_SINGLE:
198         case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector
199         case QUERY_STYLE_MULTIPLE_SAME: 
200         {
201             SPPaintSelectorMode pselmode = sp_style_determine_paint_selector_mode (query, true);
202             sp_paint_selector_set_mode (psel, pselmode);
204             sp_paint_selector_set_fillrule (psel, query->fill_rule.computed == ART_WIND_RULE_NONZERO? 
205                                      SP_PAINT_SELECTOR_FILLRULE_NONZERO : SP_PAINT_SELECTOR_FILLRULE_EVENODD);
207             if (query->fill.set && query->fill.isColor()) {
208                 sp_paint_selector_set_color_alpha (psel, &query->fill.value.color, SP_SCALE24_TO_FLOAT (query->fill_opacity.value));
209             } else if (query->fill.set && query->fill.isPaintserver()) {
211                 SPPaintServer *server = SP_STYLE_FILL_SERVER (query);
213                 if (SP_IS_LINEARGRADIENT (server)) {
214                     SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
215                     sp_paint_selector_set_gradient_linear (psel, vector);
217                     SPLinearGradient *lg = SP_LINEARGRADIENT (server);
218                     sp_paint_selector_set_gradient_properties (psel,
219                                                        SP_GRADIENT_UNITS (lg),
220                                                        SP_GRADIENT_SPREAD (lg));
221                 } else if (SP_IS_RADIALGRADIENT (server)) {
222                     SPGradient *vector = sp_gradient_get_vector (SP_GRADIENT (server), FALSE);
223                     sp_paint_selector_set_gradient_radial (psel, vector);
225                     SPRadialGradient *rg = SP_RADIALGRADIENT (server);
226                     sp_paint_selector_set_gradient_properties (psel,
227                                                        SP_GRADIENT_UNITS (rg),
228                                                        SP_GRADIENT_SPREAD (rg));
229                 } else if (SP_IS_PATTERN (server)) {
230                     SPPattern *pat = pattern_getroot (SP_PATTERN (server));
231                     sp_update_pattern_list (psel, pat);
232                 }
233             }
234             break;
235         }
237         case QUERY_STYLE_MULTIPLE_DIFFERENT:
238         {
239             sp_paint_selector_set_mode (psel, SP_PAINT_SELECTOR_MODE_MULTIPLE);
240             break;
241         }
242     }
244     sp_style_unref(query);
246     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
251 static void
252 sp_fill_style_widget_paint_mode_changed ( SPPaintSelector *psel,
253                                           SPPaintSelectorMode /*mode*/,
254                                           SPWidget *spw )
256     if (g_object_get_data (G_OBJECT (spw), "update"))
257         return;
259     /* TODO: Does this work? */
260     /* TODO: Not really, here we have to get old color back from object */
261     /* Instead of relying on paint widget having meaningful colors set */
262     sp_fill_style_widget_paint_changed (psel, spw);
265 static void
266 sp_fill_style_widget_fillrule_changed ( SPPaintSelector */*psel*/,
267                                           SPPaintSelectorFillRule mode,
268                                           SPWidget *spw )
270     if (g_object_get_data (G_OBJECT (spw), "update"))
271         return;
273     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
275     SPCSSAttr *css = sp_repr_css_attr_new ();
276     sp_repr_css_set_property (css, "fill-rule", mode == SP_PAINT_SELECTOR_FILLRULE_EVENODD? "evenodd":"nonzero");
278     sp_desktop_set_style (desktop, css);
280     sp_repr_css_attr_unref (css);
282     sp_document_done (SP_ACTIVE_DOCUMENT, SP_VERB_DIALOG_FILL_STROKE, 
283                       _("Change fill rule"));
286 static gchar const *undo_label_1 = "fill:flatcolor:1";
287 static gchar const *undo_label_2 = "fill:flatcolor:2";
288 static gchar const *undo_label = undo_label_1;
290 /**
291 This is called repeatedly while you are dragging a color slider, only for flat color
292 modes. Previously it set the color in style but did not update the repr for efficiency, however
293 this was flakey and didn't buy us almost anything. So now it does the same as _changed, except
294 lumps all its changes for undo.
295  */
296 static void
297 sp_fill_style_widget_paint_dragged (SPPaintSelector *psel, SPWidget *spw)
299     if (!spw->inkscape) {
300         return;
301     }
303     if (g_object_get_data (G_OBJECT (spw), "update")) {
304         return;
305     }
307     if (g_object_get_data (G_OBJECT (spw), "local")) {
308         // previous local flag not cleared yet; 
309         // this means dragged events come too fast, so we better skip this one to speed up display 
310         // (it's safe to do this in any case)
311         return;
312     }
314     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
316     switch (psel->mode) {
318         case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
319         case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
320         {
321             sp_paint_selector_set_flat_color (psel, SP_ACTIVE_DESKTOP, "fill", "fill-opacity");
322             sp_document_maybe_done (sp_desktop_document(SP_ACTIVE_DESKTOP), undo_label, SP_VERB_DIALOG_FILL_STROKE, 
323                                     _("Set fill color"));
324             g_object_set_data (G_OBJECT (spw), "local", GINT_TO_POINTER (TRUE)); // local change, do not update from selection
325             break;
326         }
328         default:
329             g_warning ( "file %s: line %d: Paint %d should not emit 'dragged'",
330                         __FILE__, __LINE__, psel->mode );
331             break;
333     }
334     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
338 /**
339 This is called (at least) when:
340 1  paint selector mode is switched (e.g. flat color -> gradient)
341 2  you finished dragging a gradient node and released mouse
342 3  you changed a gradient selector parameter (e.g. spread)
343 Must update repr.
344  */
345 static void
346 sp_fill_style_widget_paint_changed ( SPPaintSelector *psel,
347                                      SPWidget *spw )
349     if (g_object_get_data (G_OBJECT (spw), "update")) {
350         return;
351     }
352     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (TRUE));
354     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
355     if (!desktop) {
356         return;
357     }
358     SPDocument *document = sp_desktop_document (desktop);
359     Inkscape::Selection *selection = sp_desktop_selection (desktop);
361     GSList const *items = selection->itemList();
363     switch (psel->mode) {
365         case SP_PAINT_SELECTOR_MODE_EMPTY:
366             // This should not happen.
367             g_warning ( "file %s: line %d: Paint %d should not emit 'changed'",
368                         __FILE__, __LINE__, psel->mode);
369             break;
370         case SP_PAINT_SELECTOR_MODE_MULTIPLE:
371             // This happens when you switch multiple objects with different gradients to flat color;
372             // nothing to do here.
373             break;
375         case SP_PAINT_SELECTOR_MODE_NONE:
376         {
377             SPCSSAttr *css = sp_repr_css_attr_new ();
378             sp_repr_css_set_property (css, "fill", "none");
380             sp_desktop_set_style (desktop, css);
382             sp_repr_css_attr_unref (css);
384             sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, 
385                               _("Remove fill"));
386             break;
387         }
389         case SP_PAINT_SELECTOR_MODE_COLOR_RGB:
390         case SP_PAINT_SELECTOR_MODE_COLOR_CMYK:
391         {
392             // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed; here it results in losing release events
393             sp_canvas_force_full_redraw_after_interruptions(sp_desktop_canvas(desktop), 0);
395             sp_paint_selector_set_flat_color (psel, desktop, "fill", "fill-opacity");
396             sp_document_maybe_done (sp_desktop_document(desktop), undo_label, SP_VERB_DIALOG_FILL_STROKE,
397                                     _("Set fill color"));
398             // resume interruptibility
399             sp_canvas_end_forced_full_redraws(sp_desktop_canvas(desktop));
401             // on release, toggle undo_label so that the next drag will not be lumped with this one
402             if (undo_label == undo_label_1)
403                 undo_label = undo_label_2;
404             else
405                 undo_label = undo_label_1;
407             break;
408         }
410         case SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR:
411         case SP_PAINT_SELECTOR_MODE_GRADIENT_RADIAL:
412             if (items) {
413                 SPGradientType const gradient_type = ( psel->mode == SP_PAINT_SELECTOR_MODE_GRADIENT_LINEAR
414                                                        ? SP_GRADIENT_TYPE_LINEAR
415                                                        : SP_GRADIENT_TYPE_RADIAL );
417                 // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
418                 SPCSSAttr *css = sp_repr_css_attr_new();
419                 sp_repr_css_set_property(css, "fill-opacity", "1.0");
421                 SPGradient *vector = sp_paint_selector_get_gradient_vector(psel);
422                 if (!vector) {
423                     /* No vector in paint selector should mean that we just changed mode */
425                     SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
426                     int result = objects_query_fillstroke ((GSList *) items, query, true);
427                     guint32 common_rgb = 0;
428                     if (result == QUERY_STYLE_MULTIPLE_SAME) {
429                         if (!query->fill.isColor()) {
430                             common_rgb = sp_desktop_get_color(desktop, true);
431                         } else {
432                             common_rgb = query->fill.value.color.toRGBA32( 0xff );
433                         }
434                         vector = sp_document_default_gradient_vector(document, common_rgb);
435                     }
436                     sp_style_unref(query);
438                     for (GSList const *i = items; i != NULL; i = i->next) {
439                         //FIXME: see above
440                         sp_repr_css_change_recursive(SP_OBJECT_REPR(i->data), css, "style");
442                         if (!vector) {
443                             sp_item_set_gradient(SP_ITEM(i->data),
444                                                  sp_gradient_vector_for_object(document, desktop, SP_OBJECT(i->data), true),
445                                                  gradient_type, true);
446                         } else {
447                             sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
448                         }
449                     }
450                 } else {
451                     /* We have changed from another gradient type, or modified spread/units within
452                      * this gradient type. */
453                     vector = sp_gradient_ensure_vector_normalized (vector);
454                     for (GSList const *i = items; i != NULL; i = i->next) {
455                         //FIXME: see above
456                         sp_repr_css_change_recursive (SP_OBJECT_REPR (i->data), css, "style");
458                         SPGradient *gr = sp_item_set_gradient(SP_ITEM(i->data), vector, gradient_type, true);
459                         sp_gradient_selector_attrs_to_gradient (gr, psel);
460                     }
461                 }
463                 sp_repr_css_attr_unref (css);
465                 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, 
466                                   _("Set gradient on fill"));
467             }
468             break;
470         case SP_PAINT_SELECTOR_MODE_PATTERN:
472             if (items) {
474                 SPPattern *pattern = sp_paint_selector_get_pattern (psel);
475                 if (!pattern) {
477                     /* No Pattern in paint selector should mean that we just
478                      * changed mode - dont do jack.
479                      */
481                 } else {
482                     Inkscape::XML::Node *patrepr = SP_OBJECT_REPR(pattern);
483                     SPCSSAttr *css = sp_repr_css_attr_new ();
484                     gchar *urltext = g_strdup_printf ("url(#%s)", patrepr->attribute("id"));
485                     sp_repr_css_set_property (css, "fill", urltext);
487                     // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
488                     sp_repr_css_set_property(css, "fill-opacity", "1.0");
490                     // cannot just call sp_desktop_set_style, because we don't want to touch those
491                     // objects who already have the same root pattern but through a different href
492                     // chain. FIXME: move this to a sp_item_set_pattern
493                     for (GSList const *i = items; i != NULL; i = i->next) {
494                          SPObject *selobj = SP_OBJECT (i->data);
496                          SPStyle *style = SP_OBJECT_STYLE (selobj);
497                          if (style && style->fill.isPaintserver()) {
498                              SPObject *server = SP_OBJECT_STYLE_FILL_SERVER (selobj);
499                              if (SP_IS_PATTERN (server) && pattern_getroot (SP_PATTERN(server)) == pattern)
500                                 // only if this object's pattern is not rooted in our selected pattern, apply
501                                  continue;
502                          }
504                          sp_desktop_apply_css_recursive (selobj, css, true);
505                      }
507                     sp_repr_css_attr_unref (css);
508                     g_free (urltext);
510                 } // end if
512                 sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, 
513                                   _("Set pattern on fill"));
515             } // end if
517             break;
519         case SP_PAINT_SELECTOR_MODE_UNSET:
520             if (items) {
521                     SPCSSAttr *css = sp_repr_css_attr_new ();
522                     sp_repr_css_unset_property (css, "fill");
524                     sp_desktop_set_style (desktop, css);
525                     sp_repr_css_attr_unref (css);
527                     sp_document_done (document, SP_VERB_DIALOG_FILL_STROKE, 
528                                       _("Unset fill"));
529             }
530             break;
532         default:
533             g_warning ( "file %s: line %d: Paint selector should not be in "
534                         "mode %d",
535                         __FILE__, __LINE__, psel->mode );
536             break;
537     }
539     g_object_set_data (G_OBJECT (spw), "update", GINT_TO_POINTER (FALSE));
543 /*
544   Local Variables:
545   mode:c++
546   c-file-style:"stroustrup"
547   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
548   indent-tabs-mode:nil
549   fill-column:99
550   End:
551 */
552 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :