Code

A simple layout document as to what, why and how is cppification.
[inkscape.git] / src / desktop-style.cpp
1 #define __SP_DESKTOP_STYLE_C__
3 /** \file
4  * Desktop style management
5  *
6  * Authors:
7  *   bulia byak
8  *   verbalshadow
9  *
10  * Copyright (C) 2004, 2006 authors
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #include <string>
16 #include <cstring>
18 #include "desktop.h"
19 #include "color-rgba.h"
20 #include "svg/css-ostringstream.h"
21 #include "svg/svg.h"
22 #include "svg/svg-color.h"
23 #include "selection.h"
24 #include "inkscape.h"
25 #include "style.h"
26 #include "preferences.h"
27 #include "sp-use.h"
28 #include "filters/blend.h"
29 #include "sp-filter.h"
30 #include "sp-filter-reference.h"
31 #include "sp-gaussian-blur.h"
32 #include "sp-flowtext.h"
33 #include "sp-flowregion.h"
34 #include "sp-flowdiv.h"
35 #include "sp-linear-gradient.h"
36 #include "sp-pattern.h"
37 #include "sp-radial-gradient.h"
38 #include "sp-textpath.h"
39 #include "sp-tref.h"
40 #include "sp-tspan.h"
41 #include "xml/repr.h"
42 #include "libnrtype/font-style-to-pos.h"
43 #include "sp-path.h"
45 #include "desktop-style.h"
46 #include "svg/svg-icc-color.h"
47 #include "svg/svg-device-color.h"
48 #include "box3d-side.h"
50 /**
51  * Set color on selection on desktop.
52  */
53 void
54 sp_desktop_set_color(SPDesktop *desktop, ColorRGBA const &color, bool is_relative, bool fill)
55 {
56     /// \todo relative color setting
57     if (is_relative) {
58         g_warning("FIXME: relative color setting not yet implemented");
59         return;
60     }
62     guint32 rgba = SP_RGBA32_F_COMPOSE(color[0], color[1], color[2], color[3]);
63     gchar b[64];
64     sp_svg_write_color(b, sizeof(b), rgba);
65     SPCSSAttr *css = sp_repr_css_attr_new();
66     if (fill) {
67         sp_repr_css_set_property(css, "fill", b);
68         Inkscape::CSSOStringStream osalpha;
69         osalpha << color[3];
70         sp_repr_css_set_property(css, "fill-opacity", osalpha.str().c_str());
71     } else {
72         sp_repr_css_set_property(css, "stroke", b);
73         Inkscape::CSSOStringStream osalpha;
74         osalpha << color[3];
75         sp_repr_css_set_property(css, "stroke-opacity", osalpha.str().c_str());
76     }
78     sp_desktop_set_style(desktop, css);
80     sp_repr_css_attr_unref(css);
81 }
83 /**
84  * Apply style on object and children, recursively.
85  */
86 void
87 sp_desktop_apply_css_recursive(SPObject *o, SPCSSAttr *css, bool skip_lines)
88 {
89     // non-items should not have style
90     if (!SP_IS_ITEM(o))
91         return;
93     // 1. tspans with role=line are not regular objects in that they are not supposed to have style of their own,
94     // but must always inherit from the parent text. Same for textPath.
95     // However, if the line tspan or textPath contains some style (old file?), we reluctantly set our style to it too.
97     // 2. Generally we allow setting style on clones, but when it's inside flowRegion, do not touch
98     // it, be it clone or not; it's just styleless shape (because that's how Inkscape does
99     // flowtext).
101     if (!(skip_lines
102           && ((SP_IS_TSPAN(o) && SP_TSPAN(o)->role == SP_TSPAN_ROLE_LINE)
103               || SP_IS_FLOWDIV(o)
104               || SP_IS_FLOWPARA(o)
105               || SP_IS_TEXTPATH(o))
106           && /*!SP_OBJECT_REPR(o)->attribute("style")*/ !o->getAttribute("style"))
107         &&
108         !(SP_IS_FLOWREGION(o) ||
109           SP_IS_FLOWREGIONEXCLUDE(o) ||
110           (SP_IS_USE(o) &&
111            SP_OBJECT_PARENT(o) &&
112            (SP_IS_FLOWREGION(SP_OBJECT_PARENT(o)) ||
113             SP_IS_FLOWREGIONEXCLUDE(SP_OBJECT_PARENT(o))
114            )
115           )
116          )
117         ) {
119         SPCSSAttr *css_set = sp_repr_css_attr_new();
120         sp_repr_css_merge(css_set, css);
122         // Scale the style by the inverse of the accumulated parent transform in the paste context.
123         {
124             Geom::Matrix const local(SP_ITEM(o)->i2doc_affine());
125             double const ex(local.descrim());
126             if ( ( ex != 0. )
127                  && ( ex != 1. ) ) {
128                 sp_css_attr_scale(css_set, 1/ex);
129             }
130         }
132         //sp_repr_css_change(SP_OBJECT_REPR(o), css_set, "style");
133                 o->changeCSS(css_set,"style");
135         sp_repr_css_attr_unref(css_set);
136     }
138     // setting style on child of clone spills into the clone original (via shared repr), don't do it!
139     if (SP_IS_USE(o))
140         return;
142     for (SPObject *child = SP_OBJECT(o)->first_child() ; child != NULL ; child = SP_OBJECT_NEXT(child) ) {
143         if (sp_repr_css_property(css, "opacity", NULL) != NULL) {
144             // Unset properties which are accumulating and thus should not be set recursively.
145             // For example, setting opacity 0.5 on a group recursively would result in the visible opacity of 0.25 for an item in the group.
146             SPCSSAttr *css_recurse = sp_repr_css_attr_new();
147             sp_repr_css_merge(css_recurse, css);
148             sp_repr_css_set_property(css_recurse, "opacity", NULL);
149             sp_desktop_apply_css_recursive(child, css_recurse, skip_lines);
150             sp_repr_css_attr_unref(css_recurse);
151         } else {
152             sp_desktop_apply_css_recursive(child, css, skip_lines);
153         }
154     }
157 /**
158  * Apply style on selection on desktop.
159  */
160 void
161 sp_desktop_set_style(SPDesktop *desktop, SPCSSAttr *css, bool change, bool write_current)
163     if (write_current) {
164         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
165         // 1. Set internal value
166         sp_repr_css_merge(desktop->current, css);
168         // 1a. Write to prefs; make a copy and unset any URIs first
169         SPCSSAttr *css_write = sp_repr_css_attr_new();
170         sp_repr_css_merge(css_write, css);
171         sp_css_attr_unset_uris(css_write);
172         prefs->mergeStyle("/desktop/style", css_write);
174         for (const GSList *i = desktop->selection->itemList(); i != NULL; i = i->next) {
175             /* last used styles for 3D box faces are stored separately */
176             if (SP_IS_BOX3D_SIDE (i->data)) {
177                 const char * descr  = box3d_side_axes_string(SP_BOX3D_SIDE(i->data));
178                 if (descr != NULL) {
179                     prefs->mergeStyle(Glib::ustring("/desktop/") + descr + "/style", css_write);
180                 }
181             }
182         }
183         sp_repr_css_attr_unref(css_write);
184     }
186     if (!change)
187         return;
189 // 2. Emit signal
190     bool intercepted = desktop->_set_style_signal.emit(css);
192 /** \todo
193  * FIXME: in set_style, compensate pattern and gradient fills, stroke width,
194  * rect corners, font size for the object's own transform so that pasting
195  * fills does not depend on preserve/optimize.
196  */
198 // 3. If nobody has intercepted the signal, apply the style to the selection
199     if (!intercepted) {
201         // Remove text attributes if not text...
202         // Do this once in case a zillion objects are selected.
203         SPCSSAttr *css_no_text = sp_repr_css_attr_new();
204         sp_repr_css_merge(css_no_text, css);
205         css_no_text = sp_css_attr_unset_text(css_no_text);
207         for (GSList const *i = desktop->selection->itemList(); i != NULL; i = i->next) {
209             // If not text, don't apply text attributes (can a group have text attributes?)
210             if ( SP_IS_TEXT(i->data) || SP_IS_FLOWTEXT(i->data)
211                 || SP_IS_TSPAN(i->data) || SP_IS_TREF(i->data) || SP_IS_TEXTPATH(i->data)
212                 || SP_IS_FLOWDIV(i->data) || SP_IS_FLOWPARA(i->data) || SP_IS_FLOWTSPAN(i->data)) {
214                 sp_desktop_apply_css_recursive(SP_OBJECT(i->data), css, true);
216             } else {
218                 sp_desktop_apply_css_recursive(SP_OBJECT(i->data), css_no_text, true);
220             }
221         }
222         sp_repr_css_attr_unref(css_no_text);
223     }
226 /**
227  * Return the desktop's current style.
228  */
229 SPCSSAttr *
230 sp_desktop_get_style(SPDesktop *desktop, bool with_text)
232     SPCSSAttr *css = sp_repr_css_attr_new();
233     sp_repr_css_merge(css, desktop->current);
234     if (!css->attributeList()) {
235         sp_repr_css_attr_unref(css);
236         return NULL;
237     } else {
238         if (!with_text) {
239             css = sp_css_attr_unset_text(css);
240         }
241         return css;
242     }
245 /**
246  * Return the desktop's current color.
247  */
248 guint32
249 sp_desktop_get_color(SPDesktop *desktop, bool is_fill)
251     guint32 r = 0; // if there's no color, return black
252     gchar const *property = sp_repr_css_property(desktop->current,
253                                                  is_fill ? "fill" : "stroke",
254                                                  "#000");
256     if (desktop->current && property) { // if there is style and the property in it,
257         if (strncmp(property, "url", 3)) { // and if it's not url,
258             // read it
259             r = sp_svg_read_color(property, r);
260         }
261     }
263     return r;
266 double
267 sp_desktop_get_master_opacity_tool(SPDesktop *desktop, Glib::ustring const &tool, bool *has_opacity)
269     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
270     SPCSSAttr *css = NULL;
271     gfloat value = 1.0; // default if nothing else found
272     if (has_opacity)
273         *has_opacity = false;
274     if (prefs->getBool(tool + "/usecurrent")) {
275         css = sp_desktop_get_style(desktop, true);
276     } else {
277         css = prefs->getStyle(tool + "/style");
278     }
280     if (css) {
281         gchar const *property = css ? sp_repr_css_property(css, "opacity", "1.000") : 0;
283         if (desktop->current && property) { // if there is style and the property in it,
284             if ( !sp_svg_number_read_f(property, &value) ) {
285                 value = 1.0; // things failed. set back to the default
286             } else {
287                 if (has_opacity)
288                    *has_opacity = false;
289             }
290         }
292         sp_repr_css_attr_unref(css);
293     }
295     return value;
297 double
298 sp_desktop_get_opacity_tool(SPDesktop *desktop, Glib::ustring const &tool, bool is_fill)
300     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
301     SPCSSAttr *css = NULL;
302     gfloat value = 1.0; // default if nothing else found
303     if (prefs->getBool(tool + "/usecurrent")) {
304         css = sp_desktop_get_style(desktop, true);
305     } else {
306         css = prefs->getStyle(tool + "/style");
307     }
309     if (css) {
310         gchar const *property = css ? sp_repr_css_property(css, is_fill ? "fill-opacity": "stroke-opacity", "1.000") : 0;
312         if (desktop->current && property) { // if there is style and the property in it,
313             if ( !sp_svg_number_read_f(property, &value) ) {
314                 value = 1.0; // things failed. set back to the default
315             }
316         }
318         sp_repr_css_attr_unref(css);
319     }
321     return value;
324 guint32
325 sp_desktop_get_color_tool(SPDesktop *desktop, Glib::ustring const &tool, bool is_fill, bool *has_color)
327     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
328     SPCSSAttr *css = NULL;
329     guint32 r = 0; // if there's no color, return black
330     if (has_color)
331         *has_color = false;
332     if (prefs->getBool(tool + "/usecurrent")) {
333         css = sp_desktop_get_style(desktop, true);
334     } else {
335         css = prefs->getStyle(tool + "/style");
336     }
338     if (css) {
339         gchar const *property = sp_repr_css_property(css, is_fill ? "fill" : "stroke", "#000");
341         if (desktop->current && property) { // if there is style and the property in it,
342             if (strncmp(property, "url", 3) && strncmp(property, "none", 4)) { // and if it's not url or none,
343                 // read it
344                 r = sp_svg_read_color(property, r);
345                 if (has_color)
346                     *has_color = true;
347             }
348         }
350         sp_repr_css_attr_unref(css);
351     }
353     return r | 0xff;
356 /**
357  * Apply the desktop's current style or the tool style to repr.
358  */
359 void
360 sp_desktop_apply_style_tool(SPDesktop *desktop, Inkscape::XML::Node *repr, Glib::ustring const &tool_path, bool with_text)
362     SPCSSAttr *css_current = sp_desktop_get_style(desktop, with_text);
363     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
365     if (prefs->getBool(tool_path + "/usecurrent") && css_current) {
366         sp_repr_css_set(repr, css_current, "style");
367     } else {
368         SPCSSAttr *css = prefs->getInheritedStyle(tool_path + "/style");
369         sp_repr_css_set(repr, css, "style");
370         sp_repr_css_attr_unref(css);
371     }
372     if (css_current) {
373         sp_repr_css_attr_unref(css_current);
374     }
377 /**
378  * Returns the font size (in SVG pixels) of the text tool style (if text
379  * tool uses its own style) or desktop style (otherwise).
380 */
381 double
382 sp_desktop_get_font_size_tool(SPDesktop *desktop)
384     (void)desktop; // TODO cleanup
385     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
386     Glib::ustring desktop_style = prefs->getString("/desktop/style");
387     Glib::ustring style_str;
388     if ((prefs->getBool("/tools/text/usecurrent")) && !desktop_style.empty()) {
389         style_str = desktop_style;
390     } else {
391         style_str = prefs->getString("/tools/text/style");
392     }
394     double ret = 12;
395     if (!style_str.empty()) {
396         SPStyle *style = sp_style_new(SP_ACTIVE_DOCUMENT);
397         sp_style_merge_from_style_string(style, style_str.data());
398         ret = style->font_size.computed;
399         sp_style_unref(style);
400     }
401     return ret;
404 /** Determine average stroke width, simple method */
405 // see TODO in dialogs/stroke-style.cpp on how to get rid of this eventually
406 gdouble
407 stroke_average_width (GSList const *objects)
409     if (g_slist_length ((GSList *) objects) == 0)
410         return NR_HUGE;
412     gdouble avgwidth = 0.0;
413     bool notstroked = true;
414     int n_notstroked = 0;
416     for (GSList const *l = objects; l != NULL; l = l->next) {
417         if (!SP_IS_ITEM (l->data))
418             continue;
420         Geom::Matrix i2d = (SP_ITEM(l->data))->i2d_affine ();
422         SPObject *object = SP_OBJECT(l->data);
424         if ( object->style->stroke.isNone() ) {
425             ++n_notstroked;   // do not count nonstroked objects
426             continue;
427         } else {
428             notstroked = false;
429         }
431         avgwidth += SP_OBJECT_STYLE (object)->stroke_width.computed * i2d.descrim();
432     }
434     if (notstroked)
435         return NR_HUGE;
437     return avgwidth / (g_slist_length ((GSList *) objects) - n_notstroked);
440 /**
441  * Write to style_res the average fill or stroke of list of objects, if applicable.
442  */
443 int
444 objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill)
446     if (g_slist_length(objects) == 0) {
447         /* No objects, set empty */
448         return QUERY_STYLE_NOTHING;
449     }
451     SPIPaint *paint_res = isfill? &style_res->fill : &style_res->stroke;
452     bool paintImpossible = true;
453     paint_res->set = TRUE;
455     SVGICCColor* iccColor = 0;
456     SVGDeviceColor* devColor = 0;
458     bool iccSeen = false;
459     gfloat c[4];
460     c[0] = c[1] = c[2] = c[3] = 0.0;
461     gint num = 0;
463     gfloat prev[3];
464     prev[0] = prev[1] = prev[2] = 0.0;
465     bool same_color = true;
467     for (GSList const *i = objects; i != NULL; i = i->next) {
468         SPObject *obj = SP_OBJECT (i->data);
469         SPStyle *style = SP_OBJECT_STYLE (obj);
470         if (!style) continue;
472         SPIPaint *paint = isfill? &style->fill : &style->stroke;
474         // We consider paint "effectively set" for anything within text hierarchy
475         SPObject *parent = SP_OBJECT_PARENT (obj);
476         bool paint_effectively_set =
477             paint->set || (SP_IS_TEXT(parent) || SP_IS_TEXTPATH(parent) || SP_IS_TSPAN(parent)
478             || SP_IS_FLOWTEXT(parent) || SP_IS_FLOWDIV(parent) || SP_IS_FLOWPARA(parent)
479             || SP_IS_FLOWTSPAN(parent) || SP_IS_FLOWLINE(parent));
481         // 1. Bail out with QUERY_STYLE_MULTIPLE_DIFFERENT if necessary
483         if ((!paintImpossible) && (!paint->isSameType(*paint_res) || (paint_res->set != paint_effectively_set))) {
484             return QUERY_STYLE_MULTIPLE_DIFFERENT;  // different types of paint
485         }
487         if (paint_res->set && paint->set && paint_res->isPaintserver()) {
488             // both previous paint and this paint were a server, see if the servers are compatible
490             SPPaintServer *server_res = isfill? SP_STYLE_FILL_SERVER (style_res) : SP_STYLE_STROKE_SERVER (style_res);
491             SPPaintServer *server = isfill? SP_STYLE_FILL_SERVER (style) : SP_STYLE_STROKE_SERVER (style);
493             if (SP_IS_LINEARGRADIENT (server_res)) {
495                 if (!SP_IS_LINEARGRADIENT(server))
496                    return QUERY_STYLE_MULTIPLE_DIFFERENT;  // different kind of server
498                 SPGradient *vector = SP_GRADIENT(server)->getVector();
499                 SPGradient *vector_res = SP_GRADIENT(server_res)->getVector();
500                 if (vector_res != vector)
501                    return QUERY_STYLE_MULTIPLE_DIFFERENT;  // different gradient vectors
503             } else if (SP_IS_RADIALGRADIENT (server_res)) {
505                 if (!SP_IS_RADIALGRADIENT(server))
506                    return QUERY_STYLE_MULTIPLE_DIFFERENT;  // different kind of server
508                 SPGradient *vector = SP_GRADIENT(server)->getVector();
509                 SPGradient *vector_res = SP_GRADIENT(server_res)->getVector();
510                 if (vector_res != vector)
511                    return QUERY_STYLE_MULTIPLE_DIFFERENT;  // different gradient vectors
513             } else if (SP_IS_PATTERN (server_res)) {
515                 if (!SP_IS_PATTERN(server))
516                    return QUERY_STYLE_MULTIPLE_DIFFERENT;  // different kind of server
518                 SPPattern *pat = pattern_getroot (SP_PATTERN (server));
519                 SPPattern *pat_res = pattern_getroot (SP_PATTERN (server_res));
520                 if (pat_res != pat)
521                    return QUERY_STYLE_MULTIPLE_DIFFERENT;  // different pattern roots
522             }
523         }
525         // 2. Sum color, copy server from paint to paint_res
527         if (paint_res->set && paint_effectively_set && paint->isColor()) {
528             gfloat d[3];
529             sp_color_get_rgb_floatv (&paint->value.color, d);
531             // Check if this color is the same as previous
532             if (paintImpossible) {
533                 prev[0] = d[0];
534                 prev[1] = d[1];
535                 prev[2] = d[2];
536                 paint_res->setColor(d[0], d[1], d[2]);
537                 iccColor = paint->value.color.icc;
538                 iccSeen = true;
539             } else {
540                 if (same_color && (prev[0] != d[0] || prev[1] != d[1] || prev[2] != d[2]))
541                     same_color = false;
542                 if ( iccSeen ) {
543                     if(paint->value.color.icc) {
544                         // TODO fix this
545                         iccColor = 0;
546                     }
547                 }
548             }
550             // average color
551             c[0] += d[0];
552             c[1] += d[1];
553             c[2] += d[2];
554             c[3] += SP_SCALE24_TO_FLOAT (isfill? style->fill_opacity.value : style->stroke_opacity.value);
556             // average device color
557             unsigned int it;
558             if (i==objects /*if this is the first object in the GList*/
559                 && paint->value.color.device){
560                 devColor = new SVGDeviceColor(*paint->value.color.device);
561                 for (it=0; it < paint->value.color.device->colors.size(); it++){
562                     devColor->colors[it] = 0;
563                 }
564             }
566             if (devColor && paint->value.color.device && paint->value.color.device->type == devColor->type){
567                 for (it=0; it < paint->value.color.device->colors.size(); it++){
568                     devColor->colors[it] += paint->value.color.device->colors[it];
569                 }
570             }
572             num ++;
573         }
575        paintImpossible = false;
576        paint_res->colorSet = paint->colorSet;
577        paint_res->currentcolor = paint->currentcolor;
578        if (paint_res->set && paint_effectively_set && paint->isPaintserver()) { // copy the server
579            if (isfill) {
580                sp_style_set_to_uri_string (style_res, true, style->getFillURI());
581            } else {
582                sp_style_set_to_uri_string (style_res, false, style->getStrokeURI());
583            }
584        }
585        paint_res->set = paint_effectively_set;
586        style_res->fill_rule.computed = style->fill_rule.computed; // no averaging on this, just use the last one
587     }
589     // After all objects processed, divide the color if necessary and return
590     if (paint_res->set && paint_res->isColor()) { // set the color
591         g_assert (num >= 1);
593         c[0] /= num;
594         c[1] /= num;
595         c[2] /= num;
596         c[3] /= num;
597         paint_res->setColor(c[0], c[1], c[2]);
598         if (isfill) {
599             style_res->fill_opacity.value = SP_SCALE24_FROM_FLOAT (c[3]);
600         } else {
601             style_res->stroke_opacity.value = SP_SCALE24_FROM_FLOAT (c[3]);
602         }
605         if ( iccSeen && iccColor ) {
606             // TODO check for existing
607             SVGICCColor* tmp = new SVGICCColor(*iccColor);
608             paint_res->value.color.icc = tmp;
609         }
611         // divide and store the device-color
612         if (devColor){
613             for (unsigned int it=0; it < devColor->colors.size(); it++){
614                 devColor->colors[it] /= num;
615             }
616             paint_res->value.color.device = devColor;
617         }
619         if (num > 1) {
620             if (same_color)
621                 return QUERY_STYLE_MULTIPLE_SAME;
622             else
623                 return QUERY_STYLE_MULTIPLE_AVERAGED;
624         } else {
625             return QUERY_STYLE_SINGLE;
626         }
627     }
629     // Not color
630     if (g_slist_length(objects) > 1) {
631         return QUERY_STYLE_MULTIPLE_SAME;
632     } else {
633         return QUERY_STYLE_SINGLE;
634     }
637 /**
638  * Write to style_res the average opacity of a list of objects.
639  */
640 int
641 objects_query_opacity (GSList *objects, SPStyle *style_res)
643     if (g_slist_length(objects) == 0) {
644         /* No objects, set empty */
645         return QUERY_STYLE_NOTHING;
646     }
648     gdouble opacity_sum = 0;
649     gdouble opacity_prev = -1;
650     bool same_opacity = true;
651     guint opacity_items = 0;
654     for (GSList const *i = objects; i != NULL; i = i->next) {
655         SPObject *obj = SP_OBJECT (i->data);
656         SPStyle *style = SP_OBJECT_STYLE (obj);
657         if (!style) continue;
659         double opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
660         opacity_sum += opacity;
661         if (opacity_prev != -1 && opacity != opacity_prev)
662             same_opacity = false;
663         opacity_prev = opacity;
664         opacity_items ++;
665     }
666     if (opacity_items > 1)
667         opacity_sum /= opacity_items;
669     style_res->opacity.value = SP_SCALE24_FROM_FLOAT(opacity_sum);
671     if (opacity_items == 0) {
672         return QUERY_STYLE_NOTHING;
673     } else if (opacity_items == 1) {
674         return QUERY_STYLE_SINGLE;
675     } else {
676         if (same_opacity)
677             return QUERY_STYLE_MULTIPLE_SAME;
678         else
679             return QUERY_STYLE_MULTIPLE_AVERAGED;
680     }
683 /**
684  * Write to style_res the average stroke width of a list of objects.
685  */
686 int
687 objects_query_strokewidth (GSList *objects, SPStyle *style_res)
689     if (g_slist_length(objects) == 0) {
690         /* No objects, set empty */
691         return QUERY_STYLE_NOTHING;
692     }
694     gdouble avgwidth = 0.0;
696     gdouble prev_sw = -1;
697     bool same_sw = true;
698     bool noneSet = true; // is stroke set to none?
700     int n_stroked = 0;
702     for (GSList const *i = objects; i != NULL; i = i->next) {
703         SPObject *obj = SP_OBJECT (i->data);
704         if (!SP_IS_ITEM(obj)) continue;
705         SPStyle *style = SP_OBJECT_STYLE (obj);
706         if (!style) continue;
708         if ( style->stroke.isNone() && !(
709                                 style->marker[SP_MARKER_LOC].set || // stroke width affects markers, so if there's no stroke but only markers then we should
710                                 style->marker[SP_MARKER_LOC_START].set || // still calculate the stroke width
711                                 style->marker[SP_MARKER_LOC_MID].set ||
712                                 style->marker[SP_MARKER_LOC_END].set))
713                 {
714             continue;
715         }
717         n_stroked ++;
719         noneSet &= style->stroke.isNone();
721         Geom::Matrix i2d = SP_ITEM(obj)->i2d_affine ();
722         double sw = style->stroke_width.computed * i2d.descrim();
724         if (prev_sw != -1 && fabs(sw - prev_sw) > 1e-3)
725             same_sw = false;
726         prev_sw = sw;
728         avgwidth += sw;
729     }
731     if (n_stroked > 1)
732         avgwidth /= (n_stroked);
734     style_res->stroke_width.computed = avgwidth;
735     style_res->stroke_width.set = true;
736     style_res->stroke.noneSet = noneSet; // Will only be true if none of the selected objects has it's stroke set.
738     if (n_stroked == 0) {
739         return QUERY_STYLE_NOTHING;
740     } else if (n_stroked == 1) {
741         return QUERY_STYLE_SINGLE;
742     } else {
743         if (same_sw)
744             return QUERY_STYLE_MULTIPLE_SAME;
745         else
746             return QUERY_STYLE_MULTIPLE_AVERAGED;
747     }
750 /**
751  * Write to style_res the average miter limit of a list of objects.
752  */
753 int
754 objects_query_miterlimit (GSList *objects, SPStyle *style_res)
756     if (g_slist_length(objects) == 0) {
757         /* No objects, set empty */
758         return QUERY_STYLE_NOTHING;
759     }
761     gdouble avgml = 0.0;
762     int n_stroked = 0;
764     gdouble prev_ml = -1;
765     bool same_ml = true;
767     for (GSList const *i = objects; i != NULL; i = i->next) {
768         SPObject *obj = SP_OBJECT (i->data);
769         if (!SP_IS_ITEM(obj)) continue;
770         SPStyle *style = SP_OBJECT_STYLE (obj);
771         if (!style) continue;
773         if ( style->stroke.isNone() ) {
774             continue;
775         }
777         n_stroked ++;
779         if (prev_ml != -1 && fabs(style->stroke_miterlimit.value - prev_ml) > 1e-3)
780             same_ml = false;
781         prev_ml = style->stroke_miterlimit.value;
783         avgml += style->stroke_miterlimit.value;
784     }
786     if (n_stroked > 1)
787         avgml /= (n_stroked);
789     style_res->stroke_miterlimit.value = avgml;
790     style_res->stroke_miterlimit.set = true;
792     if (n_stroked == 0) {
793         return QUERY_STYLE_NOTHING;
794     } else if (n_stroked == 1) {
795         return QUERY_STYLE_SINGLE;
796     } else {
797         if (same_ml)
798             return QUERY_STYLE_MULTIPLE_SAME;
799         else
800             return QUERY_STYLE_MULTIPLE_AVERAGED;
801     }
804 /**
805  * Write to style_res the stroke cap of a list of objects.
806  */
807 int
808 objects_query_strokecap (GSList *objects, SPStyle *style_res)
810     if (g_slist_length(objects) == 0) {
811         /* No objects, set empty */
812         return QUERY_STYLE_NOTHING;
813     }
815     int cap = -1;
816     gdouble prev_cap = -1;
817     bool same_cap = true;
818     int n_stroked = 0;
820     for (GSList const *i = objects; i != NULL; i = i->next) {
821         SPObject *obj = SP_OBJECT (i->data);
822         if (!SP_IS_ITEM(obj)) continue;
823         SPStyle *style = SP_OBJECT_STYLE (obj);
824         if (!style) continue;
826         if ( style->stroke.isNone() ) {
827             continue;
828         }
830         n_stroked ++;
832         if (prev_cap != -1 && style->stroke_linecap.value != prev_cap)
833             same_cap = false;
834         prev_cap = style->stroke_linecap.value;
836         cap = style->stroke_linecap.value;
837     }
839     style_res->stroke_linecap.value = cap;
840     style_res->stroke_linecap.set = true;
842     if (n_stroked == 0) {
843         return QUERY_STYLE_NOTHING;
844     } else if (n_stroked == 1) {
845         return QUERY_STYLE_SINGLE;
846     } else {
847         if (same_cap)
848             return QUERY_STYLE_MULTIPLE_SAME;
849         else
850             return QUERY_STYLE_MULTIPLE_DIFFERENT;
851     }
854 /**
855  * Write to style_res the stroke join of a list of objects.
856  */
857 int
858 objects_query_strokejoin (GSList *objects, SPStyle *style_res)
860     if (g_slist_length(objects) == 0) {
861         /* No objects, set empty */
862         return QUERY_STYLE_NOTHING;
863     }
865     int join = -1;
866     gdouble prev_join = -1;
867     bool same_join = true;
868     int n_stroked = 0;
870     for (GSList const *i = objects; i != NULL; i = i->next) {
871         SPObject *obj = SP_OBJECT (i->data);
872         if (!SP_IS_ITEM(obj)) continue;
873         SPStyle *style = SP_OBJECT_STYLE (obj);
874         if (!style) continue;
876         if ( style->stroke.isNone() ) {
877             continue;
878         }
880         n_stroked ++;
882         if (prev_join != -1 && style->stroke_linejoin.value != prev_join)
883             same_join = false;
884         prev_join = style->stroke_linejoin.value;
886         join = style->stroke_linejoin.value;
887     }
889     style_res->stroke_linejoin.value = join;
890     style_res->stroke_linejoin.set = true;
892     if (n_stroked == 0) {
893         return QUERY_STYLE_NOTHING;
894     } else if (n_stroked == 1) {
895         return QUERY_STYLE_SINGLE;
896     } else {
897         if (same_join)
898             return QUERY_STYLE_MULTIPLE_SAME;
899         else
900             return QUERY_STYLE_MULTIPLE_DIFFERENT;
901     }
904 /**
905  * Write to style_res the average font size and spacing of objects.
906  */
907 int
908 objects_query_fontnumbers (GSList *objects, SPStyle *style_res)
910     bool different = false;
912     double size = 0;
913     double letterspacing = 0;
914     double wordspacing = 0;
915     double linespacing = 0;
916     bool letterspacing_normal = false;
917     bool wordspacing_normal = false;
918     bool linespacing_normal = false;
920     double size_prev = 0;
921     double letterspacing_prev = 0;
922     double wordspacing_prev = 0;
923     double linespacing_prev = 0;
925     int texts = 0;
927     for (GSList const *i = objects; i != NULL; i = i->next) {
928         SPObject *obj = SP_OBJECT (i->data);
930         if (!SP_IS_TEXT(obj) && !SP_IS_FLOWTEXT(obj)
931             && !SP_IS_TSPAN(obj) && !SP_IS_TREF(obj) && !SP_IS_TEXTPATH(obj)
932             && !SP_IS_FLOWDIV(obj) && !SP_IS_FLOWPARA(obj) && !SP_IS_FLOWTSPAN(obj))
933             continue;
935         SPStyle *style = SP_OBJECT_STYLE (obj);
936         if (!style) continue;
938         texts ++;
939         size += style->font_size.computed * Geom::Matrix(SP_ITEM(obj)->i2d_affine()).descrim(); /// \todo FIXME: we assume non-% units here
941         if (style->letter_spacing.normal) {
942             if (!different && (letterspacing_prev == 0 || letterspacing_prev == letterspacing))
943                 letterspacing_normal = true;
944         } else {
945             letterspacing += style->letter_spacing.computed; /// \todo FIXME: we assume non-% units here
946             letterspacing_normal = false;
947         }
949         if (style->word_spacing.normal) {
950             if (!different && (wordspacing_prev == 0 || wordspacing_prev == wordspacing))
951                 wordspacing_normal = true;
952         } else {
953             wordspacing += style->word_spacing.computed; /// \todo FIXME: we assume non-% units here
954             wordspacing_normal = false;
955         }
957         double linespacing_current;
958         if (style->line_height.normal) {
959             linespacing_current = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
960             if (!different && (linespacing_prev == 0 || linespacing_prev == linespacing_current))
961                 linespacing_normal = true;
962         } else if (style->line_height.unit == SP_CSS_UNIT_PERCENT || style->font_size.computed == 0) {
963             linespacing_current = style->line_height.value;
964             linespacing_normal = false;
965         } else { // we need % here
966             linespacing_current = style->line_height.computed / style->font_size.computed;
967             linespacing_normal = false;
968         }
969         linespacing += linespacing_current;
971         if ((size_prev != 0 && style->font_size.computed != size_prev) ||
972             (letterspacing_prev != 0 && style->letter_spacing.computed != letterspacing_prev) ||
973             (wordspacing_prev != 0 && style->word_spacing.computed != wordspacing_prev) ||
974             (linespacing_prev != 0 && linespacing_current != linespacing_prev)) {
975             different = true;
976         }
978         size_prev = style->font_size.computed;
979         letterspacing_prev = style->letter_spacing.computed;
980         wordspacing_prev = style->word_spacing.computed;
981         linespacing_prev = linespacing_current;
983         // FIXME: we must detect MULTIPLE_DIFFERENT for these too
984         style_res->text_anchor.computed = style->text_anchor.computed;
985         style_res->writing_mode.computed = style->writing_mode.computed;
986     }
988     if (texts == 0)
989         return QUERY_STYLE_NOTHING;
991     if (texts > 1) {
992         size /= texts;
993         letterspacing /= texts;
994         wordspacing /= texts;
995         linespacing /= texts;
996     }
998     style_res->font_size.computed = size;
999     style_res->font_size.type = SP_FONT_SIZE_LENGTH;
1001     style_res->letter_spacing.normal = letterspacing_normal;
1002     style_res->letter_spacing.computed = letterspacing;
1004     style_res->word_spacing.normal = wordspacing_normal;
1005     style_res->word_spacing.computed = wordspacing;
1007     style_res->line_height.normal = linespacing_normal;
1008     style_res->line_height.computed = linespacing;
1009     style_res->line_height.value = linespacing;
1010     style_res->line_height.unit = SP_CSS_UNIT_PERCENT;
1012     if (texts > 1) {
1013         if (different) {
1014             return QUERY_STYLE_MULTIPLE_AVERAGED;
1015         } else {
1016             return QUERY_STYLE_MULTIPLE_SAME;
1017         }
1018     } else {
1019         return QUERY_STYLE_SINGLE;
1020     }
1023 /**
1024  * Write to style_res the average font style of objects.
1025  */
1026 int
1027 objects_query_fontstyle (GSList *objects, SPStyle *style_res)
1029     bool different = false;
1030     bool set = false;
1032     int texts = 0;
1034     for (GSList const *i = objects; i != NULL; i = i->next) {
1035         SPObject *obj = SP_OBJECT (i->data);
1037         if (!SP_IS_TEXT(obj) && !SP_IS_FLOWTEXT(obj)
1038             && !SP_IS_TSPAN(obj) && !SP_IS_TREF(obj) && !SP_IS_TEXTPATH(obj)
1039             && !SP_IS_FLOWDIV(obj) && !SP_IS_FLOWPARA(obj) && !SP_IS_FLOWTSPAN(obj))
1040             continue;
1042         SPStyle *style = SP_OBJECT_STYLE (obj);
1043         if (!style) continue;
1045         texts ++;
1047         if (set &&
1048             font_style_to_pos(*style_res).signature() != font_style_to_pos(*style).signature() ) {
1049             different = true;  // different styles
1050         }
1052         set = TRUE;
1053         style_res->font_weight.value = style_res->font_weight.computed = style->font_weight.computed;
1054         style_res->font_style.value = style_res->font_style.computed = style->font_style.computed;
1055         style_res->font_stretch.value = style_res->font_stretch.computed = style->font_stretch.computed;
1056         style_res->font_variant.value = style_res->font_variant.computed = style->font_variant.computed;
1057         style_res->text_align.value = style_res->text_align.computed = style->text_align.computed;
1058     }
1060     if (texts == 0 || !set)
1061         return QUERY_STYLE_NOTHING;
1063     if (texts > 1) {
1064         if (different) {
1065             return QUERY_STYLE_MULTIPLE_DIFFERENT;
1066         } else {
1067             return QUERY_STYLE_MULTIPLE_SAME;
1068         }
1069     } else {
1070         return QUERY_STYLE_SINGLE;
1071     }
1074 /**
1075  * Write to style_res the baseline numbers.
1076  */
1077 int
1078 objects_query_baselines (GSList *objects, SPStyle *style_res)
1080     bool different = false;
1082     // Only baseline-shift at the moment
1083     // We will return:
1084     //   If baseline-shift is same for all objects:
1085     //     The full baseline-shift data (used for subscripts and superscripts)
1086     //   If baseline-shift is different:
1087     //     The average baseline-shift (not implemented at the moment as this is complicated June 2010)
1088     SPIBaselineShift old;
1089     old.value = 0.0;
1090     old.computed = 0.0;
1092     // double baselineshift = 0.0;
1093     bool set = false;
1095     int texts = 0;
1097     for (GSList const *i = objects; i != NULL; i = i->next) {
1098         SPObject *obj = SP_OBJECT (i->data);
1100         if (!SP_IS_TEXT(obj) && !SP_IS_FLOWTEXT(obj)
1101             && !SP_IS_TSPAN(obj) && !SP_IS_TREF(obj) && !SP_IS_TEXTPATH(obj)
1102             && !SP_IS_FLOWDIV(obj) && !SP_IS_FLOWPARA(obj) && !SP_IS_FLOWTSPAN(obj))
1103             continue;
1105         SPStyle *style = SP_OBJECT_STYLE (obj);
1106         if (!style) continue;
1108         texts ++;
1110         SPIBaselineShift current;
1111         if(style->baseline_shift.set) {
1113             current.set      = style->baseline_shift.set;
1114             current.inherit  = style->baseline_shift.inherit;
1115             current.type     = style->baseline_shift.type;
1116             current.literal  = style->baseline_shift.literal;
1117             current.value    = style->baseline_shift.value;
1118             current.computed = style->baseline_shift.computed;
1120             if( set ) {
1121                 if( current.set      != old.set ||
1122                     current.inherit  != old.inherit ||
1123                     current.type     != old.type ||
1124                     current.literal  != old.literal ||
1125                     current.value    != old.value ||
1126                     current.computed != old.computed ) {
1127                     // Maybe this needs to be better thought out.
1128                     different = true;
1129                 }
1130             }
1132             set = true;
1134             old.set      = current.set;
1135             old.inherit  = current.inherit;
1136             old.type     = current.type;
1137             old.literal  = current.literal;
1138             old.value    = current.value;
1139             old.computed = current.computed;
1140         }
1141     }
1143     if (different || !set ) {
1144         style_res->baseline_shift.set = false;
1145         style_res->baseline_shift.computed = 0.0;
1146     } else {
1147         style_res->baseline_shift.set      = old.set;
1148         style_res->baseline_shift.inherit  = old.inherit;
1149         style_res->baseline_shift.type     = old.type;
1150         style_res->baseline_shift.literal  = old.literal;
1151         style_res->baseline_shift.value    = old.value;
1152         style_res->baseline_shift.computed = old.computed;
1153     }
1155     if (texts == 0 || !set)
1156         return QUERY_STYLE_NOTHING;
1158     if (texts > 1) {
1159         if (different) {
1160             return QUERY_STYLE_MULTIPLE_DIFFERENT;
1161         } else {
1162             return QUERY_STYLE_MULTIPLE_SAME;
1163         }
1164     } else {
1165         return QUERY_STYLE_SINGLE;
1166     }
1169 /**
1170  * Write to style_res the average font family of objects.
1171  */
1172 int
1173 objects_query_fontfamily (GSList *objects, SPStyle *style_res)
1175     bool different = false;
1176     int texts = 0;
1178     if (style_res->text->font_family.value) {
1179         g_free(style_res->text->font_family.value);
1180         style_res->text->font_family.value = NULL;
1181     }
1182     style_res->text->font_family.set = FALSE;
1184     for (GSList const *i = objects; i != NULL; i = i->next) {
1185         SPObject *obj = SP_OBJECT (i->data);
1187         // std::cout << "  " << SP_OBJECT_ID (i->data) << std::endl;
1188         if (!SP_IS_TEXT(obj) && !SP_IS_FLOWTEXT(obj)
1189             && !SP_IS_TSPAN(obj) && !SP_IS_TREF(obj) && !SP_IS_TEXTPATH(obj)
1190             && !SP_IS_FLOWDIV(obj) && !SP_IS_FLOWPARA(obj) && !SP_IS_FLOWTSPAN(obj))
1191             continue;
1193         SPStyle *style = SP_OBJECT_STYLE (obj);
1194         if (!style) continue;
1196         texts ++;
1198         if (style_res->text->font_family.value && style->text->font_family.value &&
1199             strcmp (style_res->text->font_family.value, style->text->font_family.value)) {
1200             different = true;  // different fonts
1201         }
1203         if (style_res->text->font_family.value) {
1204             g_free(style_res->text->font_family.value);
1205             style_res->text->font_family.value = NULL;
1206         }
1208         style_res->text->font_family.set = TRUE;
1209         style_res->text->font_family.value = g_strdup(style->text->font_family.value);
1210     }
1212     if (texts == 0 || !style_res->text->font_family.set)
1213         return QUERY_STYLE_NOTHING;
1215     if (texts > 1) {
1216         if (different) {
1217             return QUERY_STYLE_MULTIPLE_DIFFERENT;
1218         } else {
1219             return QUERY_STYLE_MULTIPLE_SAME;
1220         }
1221     } else {
1222         return QUERY_STYLE_SINGLE;
1223     }
1226 int
1227 objects_query_fontspecification (GSList *objects, SPStyle *style_res)
1229     bool different = false;
1230     int texts = 0;
1232     if (style_res->text->font_specification.value) {
1233         g_free(style_res->text->font_specification.value);
1234         style_res->text->font_specification.value = NULL;
1235     }
1236     style_res->text->font_specification.set = FALSE;
1238     for (GSList const *i = objects; i != NULL; i = i->next) {
1239         SPObject *obj = SP_OBJECT (i->data);
1241         // std::cout << "  " << SP_OBJECT_ID (i->data) << std::endl;
1242         if (!SP_IS_TEXT(obj) && !SP_IS_FLOWTEXT(obj)
1243             && !SP_IS_TSPAN(obj) && !SP_IS_TREF(obj) && !SP_IS_TEXTPATH(obj)
1244             && !SP_IS_FLOWDIV(obj) && !SP_IS_FLOWPARA(obj) && !SP_IS_FLOWTSPAN(obj))
1245             continue;
1247         SPStyle *style = SP_OBJECT_STYLE (obj);
1248         if (!style) continue;
1250         texts ++;
1252         if (style_res->text->font_specification.value && style_res->text->font_specification.set &&
1253             style->text->font_specification.value && style->text->font_specification.set &&
1254             strcmp (style_res->text->font_specification.value, style->text->font_specification.value)) {
1255             different = true;  // different fonts
1256         }
1258         if (style->text->font_specification.set) {
1260             if (style_res->text->font_specification.value) {
1261                 g_free(style_res->text->font_specification.value);
1262                 style_res->text->font_specification.value = NULL;
1263             }
1265             style_res->text->font_specification.set = TRUE;
1266             style_res->text->font_specification.value = g_strdup(style->text->font_specification.value);
1267         }
1268     }
1270     if (texts == 0)
1271         return QUERY_STYLE_NOTHING;
1273     if (texts > 1) {
1274         if (different) {
1275             return QUERY_STYLE_MULTIPLE_DIFFERENT;
1276         } else {
1277             return QUERY_STYLE_MULTIPLE_SAME;
1278         }
1279     } else {
1280         return QUERY_STYLE_SINGLE;
1281     }
1284 int
1285 objects_query_blend (GSList *objects, SPStyle *style_res)
1287     const int empty_prev = -2;
1288     const int complex_filter = 5;
1289     int blend = 0;
1290     float blend_prev = empty_prev;
1291     bool same_blend = true;
1292     guint items = 0;
1294     for (GSList const *i = objects; i != NULL; i = i->next) {
1295         SPObject *obj = SP_OBJECT (i->data);
1296         SPStyle *style = SP_OBJECT_STYLE (obj);
1297         if(!style || !SP_IS_ITEM(obj)) continue;
1299         items++;
1301         //if object has a filter
1302         if (style->filter.set && style->getFilter()) {
1303             int blurcount = 0;
1304             int blendcount = 0;
1306             // determine whether filter is simple (blend and/or blur) or complex
1307             for(SPObject *primitive_obj = style->getFilter()->children;
1308                 primitive_obj && SP_IS_FILTER_PRIMITIVE(primitive_obj);
1309                 primitive_obj = primitive_obj->next) {
1310                 SPFilterPrimitive *primitive = SP_FILTER_PRIMITIVE(primitive_obj);
1311                 if(SP_IS_FEBLEND(primitive))
1312                     ++blendcount;
1313                 else if(SP_IS_GAUSSIANBLUR(primitive))
1314                     ++blurcount;
1315                 else {
1316                     blurcount = complex_filter;
1317                     break;
1318                 }
1319             }
1321             // simple filter
1322             if(blurcount == 1 || blendcount == 1) {
1323                 for(SPObject *primitive_obj = style->getFilter()->children;
1324                     primitive_obj && SP_IS_FILTER_PRIMITIVE(primitive_obj);
1325                     primitive_obj = primitive_obj->next) {
1326                     if(SP_IS_FEBLEND(primitive_obj)) {
1327                         SPFeBlend *spblend = SP_FEBLEND(primitive_obj);
1328                         blend = spblend->blend_mode;
1329                     }
1330                 }
1331             }
1332             else {
1333                 blend = complex_filter;
1334             }
1335         }
1336         // defaults to blend mode = "normal"
1337         else {
1338             blend = 0;
1339         }
1341         if(blend_prev != empty_prev && blend_prev != blend)
1342             same_blend = false;
1343         blend_prev = blend;
1344     }
1346     if (items > 0) {
1347         style_res->filter_blend_mode.value = blend;
1348     }
1350     if (items == 0) {
1351         return QUERY_STYLE_NOTHING;
1352     } else if (items == 1) {
1353         return QUERY_STYLE_SINGLE;
1354     } else {
1355         if(same_blend)
1356             return QUERY_STYLE_MULTIPLE_SAME;
1357         else
1358             return QUERY_STYLE_MULTIPLE_DIFFERENT;
1359     }
1362 /**
1363  * Write to style_res the average blurring of a list of objects.
1364  */
1365 int
1366 objects_query_blur (GSList *objects, SPStyle *style_res)
1368    if (g_slist_length(objects) == 0) {
1369         /* No objects, set empty */
1370         return QUERY_STYLE_NOTHING;
1371     }
1373     float blur_sum = 0;
1374     float blur_prev = -1;
1375     bool same_blur = true;
1376     guint blur_items = 0;
1377     guint items = 0;
1379     for (GSList const *i = objects; i != NULL; i = i->next) {
1380         SPObject *obj = SP_OBJECT (i->data);
1381         SPStyle *style = SP_OBJECT_STYLE (obj);
1382         if (!style) continue;
1383         if (!SP_IS_ITEM(obj)) continue;
1385         Geom::Matrix i2d = SP_ITEM(obj)->i2d_affine ();
1387         items ++;
1389         //if object has a filter
1390         if (style->filter.set && style->getFilter()) {
1391             //cycle through filter primitives
1392             SPObject *primitive_obj = style->getFilter()->children;
1393             while (primitive_obj) {
1394                 if (SP_IS_FILTER_PRIMITIVE(primitive_obj)) {
1395                     SPFilterPrimitive *primitive = SP_FILTER_PRIMITIVE(primitive_obj);
1397                     //if primitive is gaussianblur
1398                     if(SP_IS_GAUSSIANBLUR(primitive)) {
1399                         SPGaussianBlur * spblur = SP_GAUSSIANBLUR(primitive);
1400                         float num = spblur->stdDeviation.getNumber();
1401                         blur_sum += num * i2d.descrim();
1402                         if (blur_prev != -1 && fabs (num - blur_prev) > 1e-2) // rather low tolerance because difference in blur radii is much harder to notice than e.g. difference in sizes
1403                             same_blur = false;
1404                         blur_prev = num;
1405                         //TODO: deal with opt number, for the moment it's not necessary to the ui.
1406                         blur_items ++;
1407                     }
1408                 }
1409                 primitive_obj = primitive_obj->next;
1410             }
1411         }
1412     }
1414     if (items > 0) {
1415         if (blur_items > 0)
1416             blur_sum /= blur_items;
1417         style_res->filter_gaussianBlur_deviation.value = blur_sum;
1418     }
1420     if (items == 0) {
1421         return QUERY_STYLE_NOTHING;
1422     } else if (items == 1) {
1423         return QUERY_STYLE_SINGLE;
1424     } else {
1425         if (same_blur)
1426             return QUERY_STYLE_MULTIPLE_SAME;
1427         else
1428             return QUERY_STYLE_MULTIPLE_AVERAGED;
1429     }
1432 /**
1433  * Query the given list of objects for the given property, write
1434  * the result to style, return appropriate flag.
1435  */
1436 int
1437 sp_desktop_query_style_from_list (GSList *list, SPStyle *style, int property)
1439     if (property == QUERY_STYLE_PROPERTY_FILL) {
1440         return objects_query_fillstroke (list, style, true);
1441     } else if (property == QUERY_STYLE_PROPERTY_STROKE) {
1442         return objects_query_fillstroke (list, style, false);
1444     } else if (property == QUERY_STYLE_PROPERTY_STROKEWIDTH) {
1445         return objects_query_strokewidth (list, style);
1446     } else if (property == QUERY_STYLE_PROPERTY_STROKEMITERLIMIT) {
1447         return objects_query_miterlimit (list, style);
1448     } else if (property == QUERY_STYLE_PROPERTY_STROKECAP) {
1449         return objects_query_strokecap (list, style);
1450     } else if (property == QUERY_STYLE_PROPERTY_STROKEJOIN) {
1451         return objects_query_strokejoin (list, style);
1453     } else if (property == QUERY_STYLE_PROPERTY_MASTEROPACITY) {
1454         return objects_query_opacity (list, style);
1456     } else if (property == QUERY_STYLE_PROPERTY_FONT_SPECIFICATION) {
1457         return objects_query_fontspecification (list, style);
1458     } else if (property == QUERY_STYLE_PROPERTY_FONTFAMILY) {
1459         return objects_query_fontfamily (list, style);
1460     } else if (property == QUERY_STYLE_PROPERTY_FONTSTYLE) {
1461         return objects_query_fontstyle (list, style);
1462     } else if (property == QUERY_STYLE_PROPERTY_FONTNUMBERS) {
1463         return objects_query_fontnumbers (list, style);
1464     } else if (property == QUERY_STYLE_PROPERTY_BASELINES) {
1465         return objects_query_baselines (list, style);
1467     } else if (property == QUERY_STYLE_PROPERTY_BLEND) {
1468         return objects_query_blend (list, style);
1469     } else if (property == QUERY_STYLE_PROPERTY_BLUR) {
1470         return objects_query_blur (list, style);
1471     }
1472     return QUERY_STYLE_NOTHING;
1476 /**
1477  * Query the subselection (if any) or selection on the given desktop for the given property, write
1478  * the result to style, return appropriate flag.
1479  */
1480 int
1481 sp_desktop_query_style(SPDesktop *desktop, SPStyle *style, int property)
1483     int ret = desktop->_query_style_signal.emit(style, property);
1485     if (ret != QUERY_STYLE_NOTHING)
1486         return ret; // subselection returned a style, pass it on
1488     // otherwise, do querying and averaging over selection
1489     if (desktop->selection != NULL) {
1490         return sp_desktop_query_style_from_list ((GSList *) desktop->selection->itemList(), style, property);
1491     }
1493     return QUERY_STYLE_NOTHING;
1496 /**
1497  * Do the same as sp_desktop_query_style for all (defined) style properties, return true if at
1498  * least one of the properties did not return QUERY_STYLE_NOTHING.
1499  */
1500 bool
1501 sp_desktop_query_style_all (SPDesktop *desktop, SPStyle *query)
1503         int result_family = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
1504         int result_fstyle = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
1505         int result_fnumbers = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
1506         int result_fill = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_FILL);
1507         int result_stroke = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_STROKE);
1508         int result_strokewidth = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_STROKEWIDTH);
1509         int result_strokemiterlimit = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_STROKEMITERLIMIT);
1510         int result_strokecap = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_STROKECAP);
1511         int result_strokejoin = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_STROKEJOIN);
1512         int result_opacity = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_MASTEROPACITY);
1513         int result_blur = sp_desktop_query_style (desktop, query, QUERY_STYLE_PROPERTY_BLUR);
1515         return (result_family != QUERY_STYLE_NOTHING ||
1516                 result_fstyle != QUERY_STYLE_NOTHING ||
1517                 result_fnumbers != QUERY_STYLE_NOTHING ||
1518                 result_fill != QUERY_STYLE_NOTHING ||
1519                 result_stroke != QUERY_STYLE_NOTHING ||
1520                 result_opacity != QUERY_STYLE_NOTHING ||
1521                 result_strokewidth != QUERY_STYLE_NOTHING ||
1522                 result_strokemiterlimit != QUERY_STYLE_NOTHING ||
1523                 result_strokecap != QUERY_STYLE_NOTHING ||
1524                 result_strokejoin != QUERY_STYLE_NOTHING ||
1525                 result_blur != QUERY_STYLE_NOTHING);
1529 /*
1530   Local Variables:
1531   mode:c++
1532   c-file-style:"stroustrup"
1533   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1534   indent-tabs-mode:nil
1535   fill-column:99
1536   End:
1537 */
1538 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :