summaryrefslogtreecommitdiffstats
path: root/src/widgets/fill-style.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/widgets/fill-style.cpp')
-rw-r--r--src/widgets/fill-style.cpp832
1 files changed, 832 insertions, 0 deletions
diff --git a/src/widgets/fill-style.cpp b/src/widgets/fill-style.cpp
new file mode 100644
index 0000000..f35a148
--- /dev/null
+++ b/src/widgets/fill-style.cpp
@@ -0,0 +1,832 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * @file
+ * Fill style widget.
+ */
+/* Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * Frank Felfe <innerspace@iname.com>
+ * bulia byak <buliabyak@users.sf.net>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
+ *
+ * Copyright (C) 1999-2005 authors
+ * Copyright (C) 2001-2002 Ximian, Inc.
+ * Copyright (C) 2010 Jon A. Cruz
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#define noSP_FS_VERBOSE
+
+#include <gtkmm/box.h>
+#include <glibmm/i18n.h>
+
+#include "desktop-style.h"
+#include "desktop.h"
+#include "document-undo.h"
+#include "fill-n-stroke-factory.h"
+#include "fill-style.h"
+#include "gradient-chemistry.h"
+#include "inkscape.h"
+#include "selection.h"
+#include "verbs.h"
+
+#include "object/sp-defs.h"
+#include "object/sp-linear-gradient.h"
+#include "object/sp-mesh-gradient.h"
+#include "object/sp-pattern.h"
+#include "object/sp-radial-gradient.h"
+#include "object/sp-text.h"
+#include "style.h"
+
+#include "display/sp-canvas.h"
+
+#include "widgets/paint-selector.h"
+
+
+// These can be deleted once we sort out the libart dependence.
+
+#define ART_WIND_RULE_NONZERO 0
+
+/* Fill */
+
+
+Gtk::Widget *sp_fill_style_widget_new()
+{
+ return Inkscape::Widgets::createStyleWidget( FILL );
+}
+
+
+namespace Inkscape {
+
+class FillNStroke : public Gtk::VBox
+{
+public:
+ FillNStroke( FillOrStroke k );
+ ~FillNStroke() override;
+
+ void setFillrule( SPPaintSelector::FillRule mode );
+
+ void setDesktop(SPDesktop *desktop);
+
+private:
+ static void paintModeChangeCB(SPPaintSelector *psel, SPPaintSelector::Mode mode, FillNStroke *self);
+ static void paintChangedCB(SPPaintSelector *psel, FillNStroke *self);
+ static void paintDraggedCB(SPPaintSelector *psel, FillNStroke *self);
+ static gboolean dragDelayCB(gpointer data);
+
+ static void fillruleChangedCB( SPPaintSelector *psel, SPPaintSelector::FillRule mode, FillNStroke *self );
+
+ void selectionModifiedCB(guint flags);
+ void eventContextCB(SPDesktop *desktop, Inkscape::UI::Tools::ToolBase *eventcontext);
+
+ void dragFromPaint();
+ void updateFromPaint();
+
+ void performUpdate();
+
+ FillOrStroke kind;
+ SPDesktop *desktop;
+ SPPaintSelector *psel;
+ guint32 lastDrag;
+ guint dragId;
+ bool update;
+ sigc::connection selectChangedConn;
+ sigc::connection subselChangedConn;
+ sigc::connection selectModifiedConn;
+ sigc::connection eventContextConn;
+};
+
+} // namespace Inkscape
+
+void sp_fill_style_widget_set_desktop(Gtk::Widget *widget, SPDesktop *desktop)
+{
+ Inkscape::FillNStroke *fs = dynamic_cast<Inkscape::FillNStroke*>(widget);
+ if (fs) {
+ fs->setDesktop(desktop);
+ }
+}
+
+namespace Inkscape {
+
+/**
+ * Create the fill or stroke style widget, and hook up all the signals.
+ */
+Gtk::Widget *Inkscape::Widgets::createStyleWidget( FillOrStroke kind )
+{
+ FillNStroke *filler = new FillNStroke(kind);
+
+ return filler;
+}
+
+FillNStroke::FillNStroke( FillOrStroke k ) :
+ Gtk::VBox(),
+ kind(k),
+ desktop(nullptr),
+ psel(nullptr),
+ lastDrag(0),
+ dragId(0),
+ update(false),
+ selectChangedConn(),
+ subselChangedConn(),
+ selectModifiedConn(),
+ eventContextConn()
+{
+ // Add and connect up the paint selector widget:
+ psel = sp_paint_selector_new(kind);
+ gtk_widget_show(GTK_WIDGET(psel));
+ gtk_container_add(GTK_CONTAINER(gobj()), GTK_WIDGET(psel));
+ g_signal_connect( G_OBJECT(psel), "mode_changed",
+ G_CALLBACK(paintModeChangeCB),
+ this );
+
+ g_signal_connect( G_OBJECT(psel), "dragged",
+ G_CALLBACK(paintDraggedCB),
+ this );
+
+ g_signal_connect( G_OBJECT(psel), "changed",
+ G_CALLBACK(paintChangedCB),
+ this );
+ if (kind == FILL) {
+ g_signal_connect( G_OBJECT(psel), "fillrule_changed",
+ G_CALLBACK(fillruleChangedCB),
+ this );
+ }
+
+ performUpdate();
+}
+
+FillNStroke::~FillNStroke()
+{
+ if (dragId) {
+ g_source_remove(dragId);
+ dragId = 0;
+ }
+ psel = nullptr;
+ selectModifiedConn.disconnect();
+ subselChangedConn.disconnect();
+ selectChangedConn.disconnect();
+ eventContextConn.disconnect();
+}
+
+/**
+ * On signal modified, invokes an update of the fill or stroke style paint object.
+ */
+void FillNStroke::selectionModifiedCB( guint flags )
+{
+ if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
+#ifdef SP_FS_VERBOSE
+ g_message("selectionModifiedCB(%d) on %p", flags, this);
+#endif
+ performUpdate();
+ }
+}
+
+void FillNStroke::setDesktop(SPDesktop *desktop)
+{
+ if (this->desktop != desktop) {
+ if (dragId) {
+ g_source_remove(dragId);
+ dragId = 0;
+ }
+ if (this->desktop) {
+ selectModifiedConn.disconnect();
+ subselChangedConn.disconnect();
+ selectChangedConn.disconnect();
+ eventContextConn.disconnect();
+ }
+ this->desktop = desktop;
+ if (desktop && desktop->selection) {
+ selectChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(*this, &FillNStroke::performUpdate)));
+ subselChangedConn = desktop->connectToolSubselectionChanged(sigc::hide(sigc::mem_fun(*this, &FillNStroke::performUpdate)));
+ eventContextConn = desktop->connectEventContextChanged(sigc::hide(sigc::bind(sigc::mem_fun(*this, &FillNStroke::eventContextCB), (Inkscape::UI::Tools::ToolBase *)nullptr)));
+
+ // Must check flags, so can't call performUpdate() directly.
+ selectModifiedConn = desktop->selection->connectModified(sigc::hide<0>(sigc::mem_fun(*this, &FillNStroke::selectionModifiedCB)));
+ }
+ performUpdate();
+ }
+}
+
+/**
+ * Listen to this "change in tool" event, in case a subselection tool (such as Gradient or Node) selection
+ * is changed back to a selection tool - especially needed for selected gradient stops.
+ */
+void FillNStroke::eventContextCB(SPDesktop * /*desktop*/, Inkscape::UI::Tools::ToolBase * /*eventcontext*/)
+{
+ performUpdate();
+}
+
+
+/**
+ * Gets the active fill or stroke style property, then sets the appropriate
+ * color, alpha, gradient, pattern, etc. for the paint-selector.
+ *
+ * @param sel Selection to use, or NULL.
+ */
+void FillNStroke::performUpdate()
+{
+ if ( update || !desktop ) {
+ return;
+ }
+
+ if ( dragId ) {
+ // local change; do nothing, but reset the flag
+ g_source_remove(dragId);
+ dragId = 0;
+ return;
+ }
+
+ update = true;
+
+ // create temporary style
+ SPStyle query(desktop->doc());
+
+ // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
+ int result = sp_desktop_query_style(desktop, &query, (kind == FILL) ? QUERY_STYLE_PROPERTY_FILL : QUERY_STYLE_PROPERTY_STROKE);
+
+ SPIPaint &targPaint = *query.getFillOrStroke(kind == FILL);
+ SPIScale24 &targOpacity = *(kind == FILL ? query.fill_opacity.upcast() : query.stroke_opacity.upcast());
+
+ switch (result) {
+ case QUERY_STYLE_NOTHING:
+ {
+ /* No paint at all */
+ psel->setMode(SPPaintSelector::MODE_EMPTY);
+ break;
+ }
+
+ case QUERY_STYLE_SINGLE:
+ case QUERY_STYLE_MULTIPLE_AVERAGED: // TODO: treat this slightly differently, e.g. display "averaged" somewhere in paint selector
+ case QUERY_STYLE_MULTIPLE_SAME:
+ {
+ SPPaintSelector::Mode pselmode = SPPaintSelector::getModeForStyle(query, kind);
+ psel->setMode(pselmode);
+
+ if (kind == FILL) {
+ psel->setFillrule(query.fill_rule.computed == ART_WIND_RULE_NONZERO?
+ SPPaintSelector::FILLRULE_NONZERO : SPPaintSelector::FILLRULE_EVENODD);
+ }
+
+ if (targPaint.set && targPaint.isColor()) {
+ psel->setColorAlpha(targPaint.value.color, SP_SCALE24_TO_FLOAT(targOpacity.value));
+ } else if (targPaint.set && targPaint.isPaintserver()) {
+
+ SPPaintServer *server = (kind == FILL) ? query.getFillPaintServer() : query.getStrokePaintServer();
+
+ if (server) {
+ if (SP_IS_GRADIENT(server) && SP_GRADIENT(server)->getVector()->isSwatch()) {
+ SPGradient *vector = SP_GRADIENT(server)->getVector();
+ psel->setSwatch( vector );
+ } else if (SP_IS_LINEARGRADIENT(server)) {
+ SPGradient *vector = SP_GRADIENT(server)->getVector();
+ psel->setGradientLinear( vector );
+
+ SPLinearGradient *lg = SP_LINEARGRADIENT(server);
+ psel->setGradientProperties( lg->getUnits(),
+ lg->getSpread() );
+ } else if (SP_IS_RADIALGRADIENT(server)) {
+ SPGradient *vector = SP_GRADIENT(server)->getVector();
+ psel->setGradientRadial( vector );
+
+ SPRadialGradient *rg = SP_RADIALGRADIENT(server);
+ psel->setGradientProperties( rg->getUnits(),
+ rg->getSpread() );
+#ifdef WITH_MESH
+ } else if (SP_IS_MESHGRADIENT(server)) {
+ SPGradient *array = SP_GRADIENT(server)->getArray();
+ psel->setGradientMesh( SP_MESHGRADIENT(array) );
+ SPMeshGradient *mg = SP_MESHGRADIENT(server);
+ psel->updateMeshList( SP_MESHGRADIENT( array ));
+#endif
+ } else if (SP_IS_PATTERN(server)) {
+ SPPattern *pat = SP_PATTERN(server)->rootPattern();
+ psel->updatePatternList( pat );
+ }
+ }
+ }
+ break;
+ }
+
+ case QUERY_STYLE_MULTIPLE_DIFFERENT:
+ {
+ psel->setMode(SPPaintSelector::MODE_MULTIPLE);
+ break;
+ }
+ }
+
+ update = false;
+}
+
+/**
+ * When the mode is changed, invoke a regular changed handler.
+ */
+void FillNStroke::paintModeChangeCB( SPPaintSelector * /*psel*/,
+ SPPaintSelector::Mode /*mode*/,
+ FillNStroke *self )
+{
+#ifdef SP_FS_VERBOSE
+ g_message("paintModeChangeCB(psel, mode, self:%p)", self);
+#endif
+ if (self && !self->update) {
+ self->updateFromPaint();
+ }
+}
+
+void FillNStroke::fillruleChangedCB( SPPaintSelector * /*psel*/,
+ SPPaintSelector::FillRule mode,
+ FillNStroke *self )
+{
+ if (self) {
+ self->setFillrule(mode);
+ }
+}
+
+void FillNStroke::setFillrule( SPPaintSelector::FillRule mode )
+{
+ if (!update && desktop) {
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "fill-rule", (mode == SPPaintSelector::FILLRULE_EVENODD) ? "evenodd":"nonzero");
+
+ sp_desktop_set_style(desktop, css);
+
+ sp_repr_css_attr_unref(css);
+ css = nullptr;
+
+ DocumentUndo::done(desktop->doc(), SP_VERB_DIALOG_FILL_STROKE,
+ _("Change fill rule"));
+ }
+}
+
+static gchar const *undo_F_label_1 = "fill:flatcolor:1";
+static gchar const *undo_F_label_2 = "fill:flatcolor:2";
+
+static gchar const *undo_S_label_1 = "stroke:flatcolor:1";
+static gchar const *undo_S_label_2 = "stroke:flatcolor:2";
+
+static gchar const *undo_F_label = undo_F_label_1;
+static gchar const *undo_S_label = undo_S_label_1;
+
+
+void FillNStroke::paintDraggedCB(SPPaintSelector * /*psel*/, FillNStroke *self)
+{
+#ifdef SP_FS_VERBOSE
+ g_message("paintDraggedCB(psel, spw:%p)", self);
+#endif
+ if (self && !self->update) {
+ self->dragFromPaint();
+ }
+}
+
+
+gboolean FillNStroke::dragDelayCB(gpointer data)
+{
+ gboolean keepGoing = TRUE;
+ if (data) {
+ FillNStroke *self = reinterpret_cast<FillNStroke*>(data);
+ if (!self->update) {
+ if (self->dragId) {
+ g_source_remove(self->dragId);
+ self->dragId = 0;
+
+ self->dragFromPaint();
+ self->performUpdate();
+ }
+ keepGoing = FALSE;
+ }
+ } else {
+ keepGoing = FALSE;
+ }
+ return keepGoing;
+}
+
+/**
+ * This is called repeatedly while you are dragging a color slider, only for flat color
+ * modes. Previously it set the color in style but did not update the repr for efficiency, however
+ * this was flakey and didn't buy us almost anything. So now it does the same as _changed, except
+ * lumps all its changes for undo.
+ */
+void FillNStroke::dragFromPaint()
+{
+ if (!desktop || update) {
+ return;
+ }
+
+ guint32 when = gtk_get_current_event_time();
+
+ // Don't attempt too many updates per second.
+ // Assume a base 15.625ms resolution on the timer.
+ if (!dragId && lastDrag && when && ((when - lastDrag) < 32)) {
+ // local change, do not update from selection
+ dragId = g_timeout_add_full(G_PRIORITY_DEFAULT, 33, dragDelayCB, this, nullptr);
+ }
+
+ if (dragId) {
+ // previous local flag not cleared yet;
+ // this means dragged events come too fast, so we better skip this one to speed up display
+ // (it's safe to do this in any case)
+ return;
+ }
+ lastDrag = when;
+
+ update = true;
+
+ switch (psel->mode) {
+ case SPPaintSelector::MODE_SOLID_COLOR:
+ {
+ // local change, do not update from selection
+ dragId = g_timeout_add_full(G_PRIORITY_DEFAULT, 100, dragDelayCB, this, nullptr);
+ psel->setFlatColor( desktop, (kind == FILL) ? "fill" : "stroke", (kind == FILL) ? "fill-opacity" : "stroke-opacity" );
+ DocumentUndo::maybeDone(desktop->doc(), (kind == FILL) ? undo_F_label : undo_S_label, SP_VERB_DIALOG_FILL_STROKE,
+ (kind == FILL) ? _("Set fill color") : _("Set stroke color"));
+ break;
+ }
+
+ default:
+ g_warning( "file %s: line %d: Paint %d should not emit 'dragged'",
+ __FILE__, __LINE__, psel->mode );
+ break;
+ }
+ update = false;
+}
+
+/**
+This is called (at least) when:
+1 paint selector mode is switched (e.g. flat color -> gradient)
+2 you finished dragging a gradient node and released mouse
+3 you changed a gradient selector parameter (e.g. spread)
+Must update repr.
+ */
+void FillNStroke::paintChangedCB( SPPaintSelector * /*psel*/, FillNStroke *self )
+{
+#ifdef SP_FS_VERBOSE
+ g_message("paintChangedCB(psel, spw:%p)", self);
+#endif
+ if (self && !self->update) {
+ self->updateFromPaint();
+ }
+}
+
+void FillNStroke::updateFromPaint()
+{
+ if (!desktop) {
+ return;
+ }
+ update = true;
+
+ SPDocument *document = desktop->getDocument();
+ Inkscape::Selection *selection = desktop->getSelection();
+
+ std::vector<SPItem*> const items(selection->items().begin(), selection->items().end());
+
+ switch (psel->mode) {
+ case SPPaintSelector::MODE_EMPTY:
+ // This should not happen.
+ g_warning( "file %s: line %d: Paint %d should not emit 'changed'",
+ __FILE__, __LINE__, psel->mode);
+ break;
+ case SPPaintSelector::MODE_MULTIPLE:
+ // This happens when you switch multiple objects with different gradients to flat color;
+ // nothing to do here.
+ break;
+
+ case SPPaintSelector::MODE_NONE:
+ {
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, (kind == FILL) ? "fill" : "stroke", "none");
+
+ sp_desktop_set_style(desktop, css);
+
+ sp_repr_css_attr_unref(css);
+ css = nullptr;
+
+ DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE,
+ (kind == FILL) ? _("Remove fill") : _("Remove stroke"));
+ break;
+ }
+
+ case SPPaintSelector::MODE_SOLID_COLOR:
+ {
+ if (kind == FILL) {
+ // FIXME: fix for GTK breakage, see comment in SelectedStyle::on_opacity_changed; here it results in losing release events
+ desktop->getCanvas()->forceFullRedrawAfterInterruptions(0);
+ }
+
+ psel->setFlatColor( desktop,
+ (kind == FILL) ? "fill" : "stroke",
+ (kind == FILL) ? "fill-opacity" : "stroke-opacity" );
+ DocumentUndo::maybeDone(desktop->getDocument(), (kind == FILL) ? undo_F_label : undo_S_label, SP_VERB_DIALOG_FILL_STROKE,
+ (kind == FILL) ? _("Set fill color") : _("Set stroke color"));
+
+ if (kind == FILL) {
+ // resume interruptibility
+ desktop->getCanvas()->endForcedFullRedraws();
+ }
+
+ // on release, toggle undo_label so that the next drag will not be lumped with this one
+ if (undo_F_label == undo_F_label_1) {
+ undo_F_label = undo_F_label_2;
+ undo_S_label = undo_S_label_2;
+ } else {
+ undo_F_label = undo_F_label_1;
+ undo_S_label = undo_S_label_1;
+ }
+
+ break;
+ }
+
+ case SPPaintSelector::MODE_GRADIENT_LINEAR:
+ case SPPaintSelector::MODE_GRADIENT_RADIAL:
+ case SPPaintSelector::MODE_SWATCH:
+ if (!items.empty()) {
+ SPGradientType const gradient_type = ( psel->mode != SPPaintSelector::MODE_GRADIENT_RADIAL
+ ? SP_GRADIENT_TYPE_LINEAR
+ : SP_GRADIENT_TYPE_RADIAL );
+ bool createSwatch = (psel->mode == SPPaintSelector::MODE_SWATCH);
+
+ SPCSSAttr *css = nullptr;
+ if (kind == FILL) {
+ // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
+ css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "fill-opacity", "1.0");
+ }
+
+ SPGradient *vector = psel->getGradientVector();
+ if (!vector) {
+ /* No vector in paint selector should mean that we just changed mode */
+
+ SPStyle query(desktop->doc());
+ int result = objects_query_fillstroke(items, &query, kind == FILL);
+ if (result == QUERY_STYLE_MULTIPLE_SAME) {
+ SPIPaint &targPaint = *query.getFillOrStroke(kind == FILL);
+ SPColor common;
+ if (!targPaint.isColor()) {
+ common = sp_desktop_get_color(desktop, kind == FILL);
+ } else {
+ common = targPaint.value.color;
+ }
+ vector = sp_document_default_gradient_vector( document, common, createSwatch );
+ if ( vector && createSwatch ) {
+ vector->setSwatch();
+ }
+ }
+
+ for(auto item : items){
+ //FIXME: see above
+ if (kind == FILL) {
+ sp_repr_css_change_recursive(item->getRepr(), css, "style");
+ }
+
+ if (!vector) {
+ SPGradient *gr = sp_gradient_vector_for_object( document,
+ desktop,
+ reinterpret_cast<SPObject*>(item),
+ (kind == FILL) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE,
+ createSwatch );
+ if ( gr && createSwatch ) {
+ gr->setSwatch();
+ }
+ sp_item_set_gradient(item,
+ gr,
+ gradient_type, (kind == FILL) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE);
+ } else {
+ sp_item_set_gradient(item, vector, gradient_type, (kind == FILL) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE);
+ }
+ }
+ } else {
+ // We have changed from another gradient type, or modified spread/units within
+ // this gradient type.
+ vector = sp_gradient_ensure_vector_normalized(vector);
+ for(auto item : items){
+ //FIXME: see above
+ if (kind == FILL) {
+ sp_repr_css_change_recursive(item->getRepr(), css, "style");
+ }
+
+ SPGradient *gr = sp_item_set_gradient(item, vector, gradient_type, (kind == FILL) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE);
+ psel->pushAttrsToGradient( gr );
+ }
+ }
+
+ if (css) {
+ sp_repr_css_attr_unref(css);
+ css = nullptr;
+ }
+
+ DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE,
+ (kind == FILL) ? _("Set gradient on fill") : _("Set gradient on stroke"));
+ }
+ break;
+
+#ifdef WITH_MESH
+ case SPPaintSelector::MODE_GRADIENT_MESH:
+
+ if (!items.empty()) {
+ SPGradientType const gradient_type = SP_GRADIENT_TYPE_MESH;
+
+ SPCSSAttr *css = nullptr;
+ if (kind == FILL) {
+ // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
+ css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "fill-opacity", "1.0");
+ }
+
+ Inkscape::XML::Document *xml_doc = document->getReprDoc();
+ SPDefs *defs = document->getDefs();
+
+ SPMeshGradient * mesh = psel->getMeshGradient();
+
+ for(auto item : items){
+
+ //FIXME: see above
+ if (kind == FILL) {
+ sp_repr_css_change_recursive(item->getRepr(), css, "style");
+ }
+
+ // Check if object already has mesh.
+ bool has_mesh = false;
+ SPStyle *style = item->style;
+ if (style) {
+ SPPaintServer *server =
+ (kind==FILL) ? style->getFillPaintServer():style->getStrokePaintServer();
+ if (server && SP_IS_MESHGRADIENT(server))
+ has_mesh = true;
+ }
+
+ if (!mesh || !has_mesh) {
+ // No mesh in document or object does not already have mesh ->
+ // Create new mesh.
+
+ // Create mesh element
+ Inkscape::XML::Node *repr = xml_doc->createElement("svg:meshgradient");
+
+ // privates are garbage-collectable
+ repr->setAttribute("inkscape:collect", "always");
+
+ // Attach to document
+ defs->getRepr()->appendChild(repr);
+ Inkscape::GC::release(repr);
+
+ // Get corresponding object
+ SPMeshGradient *mg = static_cast<SPMeshGradient *>(document->getObjectByRepr(repr));
+ mg->array.create(mg, item, (kind==FILL) ?
+ item->geometricBounds() : item->visualBounds());
+
+ bool isText = SP_IS_TEXT(item);
+ sp_style_set_property_url (item, ((kind == FILL) ? "fill":"stroke"),
+ mg, isText);
+
+ // (*i)->requestModified(SP_OBJECT_MODIFIED_FLAG|SP_OBJECT_STYLE_MODIFIED_FLAG);
+
+ } else {
+ // Using found mesh
+
+ // Duplicate
+ Inkscape::XML::Node *mesh_repr = mesh->getRepr();
+ Inkscape::XML::Node *copy_repr = mesh_repr->duplicate(xml_doc);
+
+ // privates are garbage-collectable
+ copy_repr->setAttribute("inkscape:collect", "always");
+
+ // Attach to document
+ defs->getRepr()->appendChild(copy_repr);
+ Inkscape::GC::release(copy_repr);
+
+ // Get corresponding object
+ SPMeshGradient *mg =
+ static_cast<SPMeshGradient *>(document->getObjectByRepr(copy_repr));
+ // std::cout << " " << (mg->getId()?mg->getId():"null") << std::endl;
+ mg->array.read(mg);
+
+ Geom::OptRect item_bbox = (kind==FILL) ?
+ item->geometricBounds() : item->visualBounds();
+ mg->array.fill_box( item_bbox );
+
+ bool isText = SP_IS_TEXT(item);
+ sp_style_set_property_url (item, ((kind == FILL) ? "fill":"stroke"),
+ mg, isText);
+ }
+ }
+
+ if (css) {
+ sp_repr_css_attr_unref(css);
+ css = nullptr;
+ }
+
+ DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE,
+ (kind == FILL) ? _("Set mesh on fill") : _("Set mesh on stroke"));
+ }
+ break;
+#endif
+
+ case SPPaintSelector::MODE_PATTERN:
+
+ if (!items.empty()) {
+
+ SPPattern *pattern = psel->getPattern();
+ if (!pattern) {
+
+ /* No Pattern in paint selector should mean that we just
+ * changed mode - don't do jack.
+ */
+
+ } else {
+ Inkscape::XML::Node *patrepr = pattern->getRepr();
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ gchar *urltext = g_strdup_printf("url(#%s)", patrepr->attribute("id"));
+ sp_repr_css_set_property(css, (kind == FILL) ? "fill" : "stroke", urltext);
+
+ // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
+ if (kind == FILL) {
+ sp_repr_css_set_property(css, "fill-opacity", "1.0");
+ }
+
+ // cannot just call sp_desktop_set_style, because we don't want to touch those
+ // objects who already have the same root pattern but through a different href
+ // chain. FIXME: move this to a sp_item_set_pattern
+ for(auto item : items){
+ Inkscape::XML::Node *selrepr = item->getRepr();
+ if ( (kind == STROKE) && !selrepr) {
+ continue;
+ }
+ SPObject *selobj = item;
+
+ SPStyle *style = selobj->style;
+ if (style && ((kind == FILL) ? style->fill.isPaintserver() : style->stroke.isPaintserver())) {
+ SPPaintServer *server = (kind == FILL) ?
+ selobj->style->getFillPaintServer() :
+ selobj->style->getStrokePaintServer();
+ if (SP_IS_PATTERN(server) && SP_PATTERN(server)->rootPattern() == pattern)
+ // only if this object's pattern is not rooted in our selected pattern, apply
+ continue;
+ }
+
+ if (kind == FILL) {
+ sp_desktop_apply_css_recursive(selobj, css, true);
+ } else {
+ sp_repr_css_change_recursive(selrepr, css, "style");
+ }
+ }
+
+ sp_repr_css_attr_unref(css);
+ css = nullptr;
+ g_free(urltext);
+
+ } // end if
+
+ DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE,
+ (kind == FILL) ? _("Set pattern on fill") :
+ _("Set pattern on stroke"));
+ } // end if
+
+ break;
+
+ case SPPaintSelector::MODE_UNSET:
+ if (!items.empty()) {
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ if (kind == FILL) {
+ sp_repr_css_unset_property(css, "fill");
+ } else {
+ sp_repr_css_unset_property(css, "stroke");
+ sp_repr_css_unset_property(css, "stroke-opacity");
+ sp_repr_css_unset_property(css, "stroke-width");
+ sp_repr_css_unset_property(css, "stroke-miterlimit");
+ sp_repr_css_unset_property(css, "stroke-linejoin");
+ sp_repr_css_unset_property(css, "stroke-linecap");
+ sp_repr_css_unset_property(css, "stroke-dashoffset");
+ sp_repr_css_unset_property(css, "stroke-dasharray");
+ }
+
+ sp_desktop_set_style(desktop, css);
+ sp_repr_css_attr_unref(css);
+ css = nullptr;
+
+ DocumentUndo::done(document, SP_VERB_DIALOG_FILL_STROKE,
+ (kind == FILL) ? _("Unset fill") : _("Unset stroke"));
+ }
+ break;
+
+ default:
+ g_warning( "file %s: line %d: Paint selector should not be in "
+ "mode %d",
+ __FILE__, __LINE__,
+ psel->mode );
+ break;
+ }
+
+ update = false;
+}
+
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :