// SPDX-License-Identifier: GPL-2.0-or-later /** @file * SPPaintSelector: Generic paint selector widget. *//* * Authors: * see git history * Lauris Kaplinski * bulia byak * John Cliff * Jon A. Cruz * Abhishek Sharma * * Copyright (C) 2018 Authors * Released under GNU GPL v2+, read the file 'COPYING' for more information. */ #define noSP_PS_VERBOSE #include #include #include #include #include "desktop-style.h" #include "gradient-selector.h" #include "inkscape.h" #include "paint-selector.h" #include "path-prefix.h" #include "helper/stock-items.h" #include "ui/icon-loader.h" #include "style.h" #include "io/sys.h" #include "object/sp-hatch.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-stop.h" #include "svg/css-ostringstream.h" #include "ui/icon-names.h" #include "ui/widget/color-notebook.h" #include "widgets/swatch-selector.h" #include "widgets/widget-sizes.h" #include "xml/repr.h" #ifdef SP_PS_VERBOSE #include "svg/svg-icc-color.h" #endif // SP_PS_VERBOSE using Inkscape::Widgets::SwatchSelector; using Inkscape::UI::SelectedColor; enum { MODE_CHANGED, GRABBED, DRAGGED, RELEASED, CHANGED, FILLRULE_CHANGED, LAST_SIGNAL }; static void sp_paint_selector_dispose(GObject *object); static GtkWidget *sp_paint_selector_style_button_add(SPPaintSelector *psel, gchar const *px, SPPaintSelector::Mode mode, gchar const *tip); static void sp_paint_selector_style_button_toggled(GtkToggleButton *tb, SPPaintSelector *psel); static void sp_paint_selector_fillrule_toggled(GtkToggleButton *tb, SPPaintSelector *psel); static void sp_paint_selector_set_mode_empty(SPPaintSelector *psel); static void sp_paint_selector_set_mode_multiple(SPPaintSelector *psel); static void sp_paint_selector_set_mode_none(SPPaintSelector *psel); static void sp_paint_selector_set_mode_color(SPPaintSelector *psel, SPPaintSelector::Mode mode); static void sp_paint_selector_set_mode_gradient(SPPaintSelector *psel, SPPaintSelector::Mode mode); #ifdef WITH_MESH static void sp_paint_selector_set_mode_mesh(SPPaintSelector *psel, SPPaintSelector::Mode mode); #endif static void sp_paint_selector_set_mode_pattern(SPPaintSelector *psel, SPPaintSelector::Mode mode); static void sp_paint_selector_set_mode_hatch(SPPaintSelector *psel, SPPaintSelector::Mode mode); static void sp_paint_selector_set_mode_swatch(SPPaintSelector *psel, SPPaintSelector::Mode mode); static void sp_paint_selector_set_mode_unset(SPPaintSelector *psel); static void sp_paint_selector_set_style_buttons(SPPaintSelector *psel, GtkWidget *active); static guint psel_signals[LAST_SIGNAL] = {0}; #ifdef SP_PS_VERBOSE static gchar const* modeStrings[] = { "MODE_EMPTY", "MODE_MULTIPLE", "MODE_NONE", "MODE_SOLID_COLOR", "MODE_GRADIENT_LINEAR", "MODE_GRADIENT_RADIAL", #ifdef WITH_MESH "MODE_GRADIENT_MESH", #endif "MODE_PATTERN", "MODE_SWATCH", "MODE_UNSET", ".", ".", }; #endif static bool isPaintModeGradient(SPPaintSelector::Mode mode) { bool isGrad = (mode == SPPaintSelector::MODE_GRADIENT_LINEAR) || (mode == SPPaintSelector::MODE_GRADIENT_RADIAL) || (mode == SPPaintSelector::MODE_SWATCH); return isGrad; } static SPGradientSelector *getGradientFromData(SPPaintSelector const *psel) { SPGradientSelector *grad = nullptr; if (psel->mode == SPPaintSelector::MODE_SWATCH) { SwatchSelector *swatchsel = static_cast(g_object_get_data(G_OBJECT(psel->selector), "swatch-selector")); if (swatchsel) { grad = swatchsel->getGradientSelector(); } } else { grad = reinterpret_cast(g_object_get_data(G_OBJECT(psel->selector), "gradient-selector")); } return grad; } G_DEFINE_TYPE(SPPaintSelector, sp_paint_selector, GTK_TYPE_BOX); static void sp_paint_selector_class_init(SPPaintSelectorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); psel_signals[MODE_CHANGED] = g_signal_new("mode_changed", G_TYPE_FROM_CLASS(object_class), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), G_STRUCT_OFFSET(SPPaintSelectorClass, mode_changed), nullptr, nullptr, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); psel_signals[GRABBED] = g_signal_new("grabbed", G_TYPE_FROM_CLASS(object_class), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), G_STRUCT_OFFSET(SPPaintSelectorClass, grabbed), nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); psel_signals[DRAGGED] = g_signal_new("dragged", G_TYPE_FROM_CLASS(object_class), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), G_STRUCT_OFFSET(SPPaintSelectorClass, dragged), nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); psel_signals[RELEASED] = g_signal_new("released", G_TYPE_FROM_CLASS(object_class), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), G_STRUCT_OFFSET(SPPaintSelectorClass, released), nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); psel_signals[CHANGED] = g_signal_new("changed", G_TYPE_FROM_CLASS(object_class), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), G_STRUCT_OFFSET(SPPaintSelectorClass, changed), nullptr, nullptr, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); psel_signals[FILLRULE_CHANGED] = g_signal_new("fillrule_changed", G_TYPE_FROM_CLASS(object_class), (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), G_STRUCT_OFFSET(SPPaintSelectorClass, fillrule_changed), nullptr, nullptr, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT); object_class->dispose = sp_paint_selector_dispose; } #define XPAD 4 #define YPAD 1 static void sp_paint_selector_init(SPPaintSelector *psel) { gtk_orientable_set_orientation(GTK_ORIENTABLE(psel), GTK_ORIENTATION_VERTICAL); psel->mode = static_cast(-1); // huh? do you mean 0xff? -- I think this means "not in the enum" /* Paint style button box */ psel->style = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_homogeneous(GTK_BOX(psel->style), FALSE); gtk_widget_set_name(psel->style,"PaintSelector"); gtk_widget_show(psel->style); gtk_container_set_border_width(GTK_CONTAINER(psel->style), 4); gtk_box_pack_start(GTK_BOX(psel), psel->style, FALSE, FALSE, 0); /* Buttons */ psel->none = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-none"), SPPaintSelector::MODE_NONE, _("No paint")); psel->solid = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-solid"), SPPaintSelector::MODE_SOLID_COLOR, _("Flat color")); psel->gradient = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-gradient-linear"), SPPaintSelector::MODE_GRADIENT_LINEAR, _("Linear gradient")); psel->radial = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-gradient-radial"), SPPaintSelector::MODE_GRADIENT_RADIAL, _("Radial gradient")); #ifdef WITH_MESH psel->mesh = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-gradient-mesh"), SPPaintSelector::MODE_GRADIENT_MESH, _("Mesh gradient")); #endif psel->pattern = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-pattern"), SPPaintSelector::MODE_PATTERN, _("Pattern")); psel->swatch = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-swatch"), SPPaintSelector::MODE_SWATCH, _("Swatch")); psel->unset = sp_paint_selector_style_button_add(psel, INKSCAPE_ICON("paint-unknown"), SPPaintSelector::MODE_UNSET, _("Unset paint (make it undefined so it can be inherited)")); /* Fillrule */ { psel->fillrulebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_homogeneous(GTK_BOX(psel->fillrulebox), FALSE); gtk_box_pack_end(GTK_BOX(psel->style), psel->fillrulebox, FALSE, FALSE, 0); GtkWidget *w; psel->evenodd = gtk_radio_button_new(nullptr); gtk_button_set_relief(GTK_BUTTON(psel->evenodd), GTK_RELIEF_NONE); gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(psel->evenodd), FALSE); // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/painting.html#FillRuleProperty gtk_widget_set_tooltip_text(psel->evenodd, _("Any path self-intersections or subpaths create holes in the fill (fill-rule: evenodd)")); g_object_set_data(G_OBJECT(psel->evenodd), "mode", GUINT_TO_POINTER(SPPaintSelector::FILLRULE_EVENODD)); w = sp_get_icon_image("fill-rule-even-odd", GTK_ICON_SIZE_MENU); gtk_container_add(GTK_CONTAINER(psel->evenodd), w); gtk_box_pack_start(GTK_BOX(psel->fillrulebox), psel->evenodd, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(psel->evenodd), "toggled", G_CALLBACK(sp_paint_selector_fillrule_toggled), psel); psel->nonzero = gtk_radio_button_new(gtk_radio_button_get_group(GTK_RADIO_BUTTON(psel->evenodd))); gtk_button_set_relief(GTK_BUTTON(psel->nonzero), GTK_RELIEF_NONE); gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(psel->nonzero), FALSE); // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/painting.html#FillRuleProperty gtk_widget_set_tooltip_text(psel->nonzero, _("Fill is solid unless a subpath is counterdirectional (fill-rule: nonzero)")); g_object_set_data(G_OBJECT(psel->nonzero), "mode", GUINT_TO_POINTER(SPPaintSelector::FILLRULE_NONZERO)); w = sp_get_icon_image("fill-rule-nonzero", GTK_ICON_SIZE_MENU); gtk_container_add(GTK_CONTAINER(psel->nonzero), w); gtk_box_pack_start(GTK_BOX(psel->fillrulebox), psel->nonzero, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(psel->nonzero), "toggled", G_CALLBACK(sp_paint_selector_fillrule_toggled), psel); } /* Frame */ psel->label = gtk_label_new(""); auto lbbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4); gtk_box_set_homogeneous(GTK_BOX(lbbox), FALSE); gtk_widget_show(psel->label); gtk_box_pack_start(GTK_BOX(lbbox), psel->label, false, false, 4); gtk_box_pack_start(GTK_BOX(psel), lbbox, false, false, 4); psel->frame = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); gtk_box_set_homogeneous(GTK_BOX(psel->frame), FALSE); gtk_widget_show(psel->frame); //gtk_container_set_border_width(GTK_CONTAINER(psel->frame), 0); gtk_box_pack_start(GTK_BOX(psel), psel->frame, TRUE, TRUE, 0); /* Last used color */ psel->selected_color = new SelectedColor; psel->updating_color = false; psel->selected_color->signal_grabbed.connect(sigc::mem_fun(psel, &SPPaintSelector::onSelectedColorGrabbed)); psel->selected_color->signal_dragged.connect(sigc::mem_fun(psel, &SPPaintSelector::onSelectedColorDragged)); psel->selected_color->signal_released.connect(sigc::mem_fun(psel, &SPPaintSelector::onSelectedColorReleased)); psel->selected_color->signal_changed.connect(sigc::mem_fun(psel, &SPPaintSelector::onSelectedColorChanged)); } static void sp_paint_selector_dispose(GObject *object) { SPPaintSelector *psel = SP_PAINT_SELECTOR(object); // clean up our long-living pattern menu g_object_set_data(G_OBJECT(psel),"patternmenu",nullptr); #ifdef WITH_MESH // clean up our long-living mesh menu g_object_set_data(G_OBJECT(psel),"meshmenu",nullptr); #endif if (psel->selected_color) { delete psel->selected_color; psel->selected_color = nullptr; } if ((G_OBJECT_CLASS(sp_paint_selector_parent_class))->dispose) (G_OBJECT_CLASS(sp_paint_selector_parent_class))->dispose(object); } static GtkWidget *sp_paint_selector_style_button_add(SPPaintSelector *psel, gchar const *pixmap, SPPaintSelector::Mode mode, gchar const *tip) { GtkWidget *b, *w; b = gtk_toggle_button_new(); gtk_widget_set_tooltip_text(b, tip); gtk_widget_show(b); gtk_container_set_border_width(GTK_CONTAINER(b), 0); gtk_button_set_relief(GTK_BUTTON(b), GTK_RELIEF_NONE); gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(b), FALSE); g_object_set_data(G_OBJECT(b), "mode", GUINT_TO_POINTER(mode)); w = sp_get_icon_image(pixmap, GTK_ICON_SIZE_BUTTON); gtk_container_add(GTK_CONTAINER(b), w); gtk_box_pack_start(GTK_BOX(psel->style), b, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(b), "toggled", G_CALLBACK(sp_paint_selector_style_button_toggled), psel); return b; } static void sp_paint_selector_style_button_toggled(GtkToggleButton *tb, SPPaintSelector *psel) { if (!psel->update && gtk_toggle_button_get_active(tb)) { psel->setMode(static_cast(GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(tb), "mode")))); } } static void sp_paint_selector_fillrule_toggled(GtkToggleButton *tb, SPPaintSelector *psel) { if (!psel->update && gtk_toggle_button_get_active(tb)) { SPPaintSelector::FillRule fr = static_cast(GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(tb), "mode"))); g_signal_emit(G_OBJECT(psel), psel_signals[FILLRULE_CHANGED], 0, fr); } } static void sp_paint_selector_show_fillrule(SPPaintSelector *psel, bool is_fill) { if (psel->fillrulebox) { if (is_fill) { gtk_widget_show_all(psel->fillrulebox); } else { gtk_widget_destroy(psel->fillrulebox); psel->fillrulebox = nullptr; } } } SPPaintSelector *sp_paint_selector_new(FillOrStroke kind) { SPPaintSelector *psel = static_cast(g_object_new(SP_TYPE_PAINT_SELECTOR, nullptr)); psel->setMode(SPPaintSelector::MODE_MULTIPLE); // This silliness is here because I don't know how to pass a parameter to the // GtkObject "constructor" (sp_paint_selector_init). Remove it when paint_selector // becomes a normal class. sp_paint_selector_show_fillrule(psel, kind == FILL); return psel; } void SPPaintSelector::setMode(Mode mode) { if (this->mode != mode) { update = TRUE; #ifdef SP_PS_VERBOSE g_print("Mode change %d -> %d %s -> %s\n", this->mode, mode, modeStrings[this->mode], modeStrings[mode]); #endif switch (mode) { case MODE_EMPTY: sp_paint_selector_set_mode_empty(this); break; case MODE_MULTIPLE: sp_paint_selector_set_mode_multiple(this); break; case MODE_NONE: sp_paint_selector_set_mode_none(this); break; case MODE_SOLID_COLOR: sp_paint_selector_set_mode_color(this, mode); break; case MODE_GRADIENT_LINEAR: case MODE_GRADIENT_RADIAL: sp_paint_selector_set_mode_gradient(this, mode); break; #ifdef WITH_MESH case MODE_GRADIENT_MESH: sp_paint_selector_set_mode_mesh(this, mode); break; #endif case MODE_PATTERN: sp_paint_selector_set_mode_pattern(this, mode); break; case MODE_HATCH: sp_paint_selector_set_mode_hatch(this, mode); break; case MODE_SWATCH: sp_paint_selector_set_mode_swatch(this, mode); break; case MODE_UNSET: sp_paint_selector_set_mode_unset(this); break; default: g_warning("file %s: line %d: Unknown paint mode %d", __FILE__, __LINE__, mode); break; } this->mode = mode; g_signal_emit(G_OBJECT(this), psel_signals[MODE_CHANGED], 0, this->mode); update = FALSE; } } void SPPaintSelector::setFillrule(FillRule fillrule) { if (fillrulebox) { // TODO this flips widgets but does not use a member to store state. Revisit gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(evenodd), (fillrule == FILLRULE_EVENODD)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(nonzero), (fillrule == FILLRULE_NONZERO)); } } void SPPaintSelector::setColorAlpha(SPColor const &color, float alpha) { g_return_if_fail( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); /* guint32 rgba = 0; if ( sp_color_get_colorspace_type(color) == SP_COLORSPACE_TYPE_CMYK ) { #ifdef SP_PS_VERBOSE g_print("PaintSelector set CMYKA\n"); #endif sp_paint_selector_set_mode(psel, MODE_COLOR_CMYK); } else */ { #ifdef SP_PS_VERBOSE g_print("PaintSelector set RGBA\n"); #endif setMode(MODE_SOLID_COLOR); } updating_color = true; selected_color->setColorAlpha(color, alpha); updating_color = false; //rgba = color.toRGBA32( alpha ); } void SPPaintSelector::setSwatch(SPGradient *vector ) { #ifdef SP_PS_VERBOSE g_print("PaintSelector set SWATCH\n"); #endif setMode(MODE_SWATCH); SwatchSelector *swatchsel = static_cast(g_object_get_data(G_OBJECT(selector), "swatch-selector")); if (swatchsel) { swatchsel->setVector( (vector) ? vector->document : nullptr, vector ); } } void SPPaintSelector::setGradientLinear(SPGradient *vector) { #ifdef SP_PS_VERBOSE g_print("PaintSelector set GRADIENT LINEAR\n"); #endif setMode(MODE_GRADIENT_LINEAR); SPGradientSelector *gsel = getGradientFromData(this); gsel->setMode(SPGradientSelector::MODE_LINEAR); gsel->setVector((vector) ? vector->document : nullptr, vector); } void SPPaintSelector::setGradientRadial(SPGradient *vector) { #ifdef SP_PS_VERBOSE g_print("PaintSelector set GRADIENT RADIAL\n"); #endif setMode(MODE_GRADIENT_RADIAL); SPGradientSelector *gsel = getGradientFromData(this); gsel->setMode(SPGradientSelector::MODE_RADIAL); gsel->setVector((vector) ? vector->document : nullptr, vector); } #ifdef WITH_MESH void SPPaintSelector::setGradientMesh(SPMeshGradient *array) { #ifdef SP_PS_VERBOSE g_print("PaintSelector set GRADIENT MESH\n"); #endif setMode(MODE_GRADIENT_MESH); // SPGradientSelector *gsel = getGradientFromData(this); // gsel->setMode(SPGradientSelector::MODE_GRADIENT_MESH); // gsel->setVector((mesh) ? mesh->document : 0, mesh); } #endif void SPPaintSelector::setGradientProperties( SPGradientUnits units, SPGradientSpread spread ) { g_return_if_fail(isPaintModeGradient(mode)); SPGradientSelector *gsel = getGradientFromData(this); gsel->setUnits(units); gsel->setSpread(spread); } void SPPaintSelector::getGradientProperties( SPGradientUnits &units, SPGradientSpread &spread) const { g_return_if_fail(isPaintModeGradient(mode)); SPGradientSelector *gsel = getGradientFromData(this); units = gsel->getUnits(); spread = gsel->getSpread(); } /** * \post (alpha == NULL) || (*alpha in [0.0, 1.0]). */ void SPPaintSelector::getColorAlpha(SPColor &color, gfloat &alpha) const { selected_color->colorAlpha(color, alpha); g_assert( ( 0.0 <= alpha ) && ( alpha <= 1.0 ) ); } SPGradient *SPPaintSelector::getGradientVector() { SPGradient* vect = nullptr; if (isPaintModeGradient(mode)) { SPGradientSelector *gsel = getGradientFromData(this); vect = gsel->getVector(); } return vect; } void SPPaintSelector::pushAttrsToGradient( SPGradient *gr ) const { SPGradientUnits units = SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX; SPGradientSpread spread = SP_GRADIENT_SPREAD_PAD; getGradientProperties( units, spread ); gr->setUnits(units); gr->setSpread(spread); gr->updateRepr(); } static void sp_paint_selector_clear_frame(SPPaintSelector *psel) { g_return_if_fail( psel != nullptr); if (psel->selector) { //This is a hack to work around GtkNotebook bug in ColorSelector. Is sends signal switch-page on destroy //The widget is hidden first so it can recognize that it should not process signals from notebook child gtk_widget_set_visible(psel->selector, false); gtk_widget_destroy(psel->selector); psel->selector = nullptr; } } static void sp_paint_selector_set_mode_empty(SPPaintSelector *psel) { sp_paint_selector_set_style_buttons(psel, nullptr); gtk_widget_set_sensitive(psel->style, FALSE); sp_paint_selector_clear_frame(psel); gtk_label_set_markup(GTK_LABEL(psel->label), _("No objects")); } static void sp_paint_selector_set_mode_multiple(SPPaintSelector *psel) { sp_paint_selector_set_style_buttons(psel, nullptr); gtk_widget_set_sensitive(psel->style, TRUE); sp_paint_selector_clear_frame(psel); gtk_label_set_markup(GTK_LABEL(psel->label), _("Multiple styles")); } static void sp_paint_selector_set_mode_unset(SPPaintSelector *psel) { sp_paint_selector_set_style_buttons(psel, psel->unset); gtk_widget_set_sensitive(psel->style, TRUE); sp_paint_selector_clear_frame(psel); gtk_label_set_markup(GTK_LABEL(psel->label), _("Paint is undefined")); } static void sp_paint_selector_set_mode_none(SPPaintSelector *psel) { sp_paint_selector_set_style_buttons(psel, psel->none); gtk_widget_set_sensitive(psel->style, TRUE); sp_paint_selector_clear_frame(psel); gtk_label_set_markup(GTK_LABEL(psel->label), _("No paint")); } /* Color paint */ void SPPaintSelector::onSelectedColorGrabbed() { g_signal_emit(G_OBJECT(this), psel_signals[GRABBED], 0); } void SPPaintSelector::onSelectedColorDragged() { if (updating_color) { return; } g_signal_emit(G_OBJECT(this), psel_signals[DRAGGED], 0); } void SPPaintSelector::onSelectedColorReleased() { g_signal_emit(G_OBJECT(this), psel_signals[RELEASED], 0); } void SPPaintSelector::onSelectedColorChanged() { if (updating_color) { return; } if (mode == MODE_SOLID_COLOR) { g_signal_emit(G_OBJECT(this), psel_signals[CHANGED], 0); } else { g_warning("SPPaintSelector::onSelectedColorChanged(): selected color changed while not in color selection mode"); } } static void sp_paint_selector_set_mode_color(SPPaintSelector *psel, SPPaintSelector::Mode /*mode*/) { using Inkscape::UI::Widget::ColorNotebook; if ((psel->mode == SPPaintSelector::MODE_SWATCH) || (psel->mode == SPPaintSelector::MODE_GRADIENT_LINEAR) || (psel->mode == SPPaintSelector::MODE_GRADIENT_RADIAL) ) { SPGradientSelector *gsel = getGradientFromData(psel); if (gsel) { SPGradient *gradient = gsel->getVector(); // Gradient can be null if object paint is changed externally (ie. with a color picker tool) if (gradient) { SPColor color = gradient->getFirstStop()->getColor(); float alpha = gradient->getFirstStop()->getOpacity(); psel->selected_color->setColorAlpha(color, alpha, false); } } } sp_paint_selector_set_style_buttons(psel, psel->solid); gtk_widget_set_sensitive(psel->style, TRUE); if (psel->mode == SPPaintSelector::MODE_SOLID_COLOR) { /* Already have color selector */ // Do nothing } else { sp_paint_selector_clear_frame(psel); /* Create new color selector */ /* Create vbox */ auto vb = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); gtk_box_set_homogeneous(GTK_BOX(vb), FALSE); gtk_widget_show(vb); /* Color selector */ Gtk::Widget *color_selector = Gtk::manage(new ColorNotebook(*(psel->selected_color))); color_selector->show(); gtk_box_pack_start(GTK_BOX(vb), color_selector->gobj(), TRUE, TRUE, 0); /* Pack everything to frame */ gtk_container_add(GTK_CONTAINER(psel->frame), vb); psel->selector = vb; } gtk_label_set_markup(GTK_LABEL(psel->label), _("Flat color")); #ifdef SP_PS_VERBOSE g_print("Color req\n"); #endif } /* Gradient */ static void sp_paint_selector_gradient_grabbed(SPGradientSelector * /*csel*/, SPPaintSelector *psel) { g_signal_emit(G_OBJECT(psel), psel_signals[GRABBED], 0); } static void sp_paint_selector_gradient_dragged(SPGradientSelector * /*csel*/, SPPaintSelector *psel) { g_signal_emit(G_OBJECT(psel), psel_signals[DRAGGED], 0); } static void sp_paint_selector_gradient_released(SPGradientSelector * /*csel*/, SPPaintSelector *psel) { g_signal_emit(G_OBJECT(psel), psel_signals[RELEASED], 0); } static void sp_paint_selector_gradient_changed(SPGradientSelector * /*csel*/, SPPaintSelector *psel) { g_signal_emit(G_OBJECT(psel), psel_signals[CHANGED], 0); } static void sp_paint_selector_set_mode_gradient(SPPaintSelector *psel, SPPaintSelector::Mode mode) { GtkWidget *gsel; /* fixme: We do not need function-wide gsel at all */ if (mode == SPPaintSelector::MODE_GRADIENT_LINEAR) { sp_paint_selector_set_style_buttons(psel, psel->gradient); } else if (mode == SPPaintSelector::MODE_GRADIENT_RADIAL) { sp_paint_selector_set_style_buttons(psel, psel->radial); } gtk_widget_set_sensitive(psel->style, TRUE); if ((psel->mode == SPPaintSelector::MODE_GRADIENT_LINEAR) || (psel->mode == SPPaintSelector::MODE_GRADIENT_RADIAL)) { /* Already have gradient selector */ gsel = GTK_WIDGET(g_object_get_data(G_OBJECT(psel->selector), "gradient-selector")); } else { sp_paint_selector_clear_frame(psel); /* Create new gradient selector */ gsel = sp_gradient_selector_new(); gtk_widget_show(gsel); g_signal_connect(G_OBJECT(gsel), "grabbed", G_CALLBACK(sp_paint_selector_gradient_grabbed), psel); g_signal_connect(G_OBJECT(gsel), "dragged", G_CALLBACK(sp_paint_selector_gradient_dragged), psel); g_signal_connect(G_OBJECT(gsel), "released", G_CALLBACK(sp_paint_selector_gradient_released), psel); g_signal_connect(G_OBJECT(gsel), "changed", G_CALLBACK(sp_paint_selector_gradient_changed), psel); /* Pack everything to frame */ gtk_container_add(GTK_CONTAINER(psel->frame), gsel); psel->selector = gsel; g_object_set_data(G_OBJECT(psel->selector), "gradient-selector", gsel); } /* Actually we have to set option menu history here */ if (mode == SPPaintSelector::MODE_GRADIENT_LINEAR) { SP_GRADIENT_SELECTOR(gsel)->setMode(SPGradientSelector::MODE_LINEAR); //sp_gradient_selector_set_mode(SP_GRADIENT_SELECTOR(gsel), SP_GRADIENT_SELECTOR_MODE_LINEAR); gtk_label_set_markup(GTK_LABEL(psel->label), _("Linear gradient")); } else if (mode == SPPaintSelector::MODE_GRADIENT_RADIAL) { SP_GRADIENT_SELECTOR(gsel)->setMode(SPGradientSelector::MODE_RADIAL); gtk_label_set_markup(GTK_LABEL(psel->label), _("Radial gradient")); } #ifdef SP_PS_VERBOSE g_print("Gradient req\n"); #endif } // ************************* MESH ************************ #ifdef WITH_MESH static void sp_psel_mesh_destroy(GtkWidget *widget, SPPaintSelector * /*psel*/) { // drop our reference to the mesh menu widget g_object_unref( G_OBJECT(widget) ); } static void sp_psel_mesh_change(GtkWidget * /*widget*/, SPPaintSelector *psel) { g_signal_emit(G_OBJECT(psel), psel_signals[CHANGED], 0); } /** * Returns a list of meshes in the defs of the given source document as a vector */ static std::vector ink_mesh_list_get (SPDocument *source) { std::vector pl; if (source == nullptr) return pl; std::vector meshes = source->getResourceList("gradient"); for (auto meshe : meshes) { if (SP_IS_MESHGRADIENT(meshe) && SP_GRADIENT(meshe) == SP_GRADIENT(meshe)->getArray()) { // only if this is a root mesh pl.push_back(SP_MESHGRADIENT(meshe)); } } return pl; } /** * Adds menu items for mesh list. */ static void sp_mesh_menu_build (GtkWidget *combo, std::vector &mesh_list, SPDocument */*source*/) { GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))); GtkTreeIter iter; for (auto i:mesh_list) { Inkscape::XML::Node *repr = i->getRepr(); gchar const *meshid = repr->attribute("id"); gchar const *label = meshid; // Only relevant if we supply a set of canned meshes. gboolean stockid = false; if (repr->attribute("inkscape:stockid")) { label = _(repr->attribute("inkscape:stockid")); stockid = true; } gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, COMBO_COL_LABEL, label, COMBO_COL_STOCK, stockid, COMBO_COL_MESH, meshid, COMBO_COL_SEP, FALSE, -1); } } /** * Pick up all meshes from source, except those that are in * current_doc (if non-NULL), and add items to the mesh menu. */ static void sp_mesh_list_from_doc(GtkWidget *combo, SPDocument * /*current_doc*/, SPDocument *source, SPDocument * /*mesh_doc*/) { std::vector pl = ink_mesh_list_get(source); sp_mesh_menu_build (combo, pl, source); } static void ink_mesh_menu_populate_menu(GtkWidget *combo, SPDocument *doc) { static SPDocument *meshes_doc = nullptr; // If we ever add a list of canned mesh gradients, uncomment following: // find and load meshes.svg // if (meshes_doc == NULL) { // char *meshes_source = g_build_filename(INKSCAPE_MESHESDIR, "meshes.svg", NULL); // if (Inkscape::IO::file_test(meshes_source, G_FILE_TEST_IS_REGULAR)) { // meshes_doc = SPDocument::createNewDoc(meshes_source, FALSE); // } // g_free(meshes_source); // } // suck in from current doc sp_mesh_list_from_doc ( combo, nullptr, doc, meshes_doc ); // add separator // { // GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))); // GtkTreeIter iter; // gtk_list_store_append (store, &iter); // gtk_list_store_set(store, &iter, // COMBO_COL_LABEL, "", COMBO_COL_STOCK, false, COMBO_COL_MESH, "", COMBO_COL_SEP, true, -1); // } // suck in from meshes.svg // if (meshes_doc) { // doc->ensureUpToDate(); // sp_mesh_list_from_doc ( combo, doc, meshes_doc, NULL ); // } } static GtkWidget* ink_mesh_menu(GtkWidget *combo) { SPDocument *doc = SP_ACTIVE_DOCUMENT; GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))); GtkTreeIter iter; if (!doc) { gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, COMBO_COL_LABEL, _("No document selected"), COMBO_COL_STOCK, false, COMBO_COL_MESH, "", COMBO_COL_SEP, false, -1); gtk_widget_set_sensitive(combo, FALSE); } else { ink_mesh_menu_populate_menu(combo, doc); gtk_widget_set_sensitive(combo, TRUE); } // Select the first item that is not a separator if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter)) { gboolean sep = false; gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, COMBO_COL_SEP, &sep, -1); if (sep) { gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); } gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); } return combo; } /*update mesh list*/ void SPPaintSelector::updateMeshList( SPMeshGradient *mesh ) { if (update) { return; } GtkWidget *combo = GTK_WIDGET(g_object_get_data(G_OBJECT(this), "meshmenu")); g_assert( combo != nullptr ); /* Clear existing menu if any */ GtkTreeModel *store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); gtk_list_store_clear(GTK_LIST_STORE(store)); ink_mesh_menu(combo); /* Set history */ if (mesh && !g_object_get_data(G_OBJECT(combo), "update")) { g_object_set_data(G_OBJECT(combo), "update", GINT_TO_POINTER(TRUE)); gchar const *meshname = mesh->getRepr()->attribute("id"); // Find this mesh and set it active in the combo_box GtkTreeIter iter ; gchar *meshid = nullptr; bool valid = gtk_tree_model_get_iter_first (store, &iter); if (!valid) { return; } gtk_tree_model_get (store, &iter, COMBO_COL_MESH, &meshid, -1); while (valid && strcmp(meshid, meshname) != 0) { valid = gtk_tree_model_iter_next (store, &iter); g_free(meshid); meshid = nullptr; gtk_tree_model_get (store, &iter, COMBO_COL_MESH, &meshid, -1); } if (valid) { gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); } g_object_set_data(G_OBJECT(combo), "update", GINT_TO_POINTER(FALSE)); g_free(meshid); } } static void sp_paint_selector_set_mode_mesh(SPPaintSelector *psel, SPPaintSelector::Mode mode) { if (mode == SPPaintSelector::MODE_GRADIENT_MESH) { sp_paint_selector_set_style_buttons(psel, psel->mesh); } gtk_widget_set_sensitive(psel->style, TRUE); GtkWidget *tbl = nullptr; if (psel->mode == SPPaintSelector::MODE_GRADIENT_MESH) { /* Already have mesh menu */ tbl = GTK_WIDGET(g_object_get_data(G_OBJECT(psel->selector), "mesh-selector")); } else { sp_paint_selector_clear_frame(psel); /* Create vbox */ tbl = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); gtk_box_set_homogeneous(GTK_BOX(tbl), FALSE); gtk_widget_show(tbl); { auto hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1); gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); /** * Create a combo_box and store with 4 columns, * The label, a pointer to the mesh, is stockid or not, is a separator or not. */ GtkListStore *store = gtk_list_store_new (COMBO_N_COLS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN); GtkWidget *combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(combo), SPPaintSelector::isSeparator, nullptr, nullptr); GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); gtk_cell_renderer_set_padding (renderer, 2, 0); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", COMBO_COL_LABEL, NULL); ink_mesh_menu(combo); g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(sp_psel_mesh_change), psel); g_signal_connect(G_OBJECT(combo), "destroy", G_CALLBACK(sp_psel_mesh_destroy), psel); g_object_set_data(G_OBJECT(psel), "meshmenu", combo); g_object_ref( G_OBJECT(combo)); gtk_container_add(GTK_CONTAINER(hb), combo); gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); g_object_unref( G_OBJECT(store)); } { auto hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); auto l = gtk_label_new(nullptr); gtk_label_set_markup(GTK_LABEL(l), _("Use the Mesh tool to modify the mesh.")); gtk_label_set_line_wrap(GTK_LABEL(l), true); gtk_widget_set_size_request(l, 180, -1); gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); } gtk_widget_show_all(tbl); gtk_container_add(GTK_CONTAINER(psel->frame), tbl); psel->selector = tbl; g_object_set_data(G_OBJECT(psel->selector), "mesh-selector", tbl); gtk_label_set_markup(GTK_LABEL(psel->label), _("Mesh fill")); } #ifdef SP_PS_VERBOSE g_print("Mesh req\n"); #endif } SPMeshGradient *SPPaintSelector::getMeshGradient() { g_return_val_if_fail((mode == MODE_GRADIENT_MESH) , NULL); GtkWidget *combo = GTK_WIDGET(g_object_get_data(G_OBJECT(this), "meshmenu")); /* no mesh menu if we were just selected */ if ( combo == nullptr ) { return nullptr; } GtkTreeModel *store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); /* Get the selected mesh */ GtkTreeIter iter; if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX(combo), &iter) || !gtk_list_store_iter_is_valid(GTK_LIST_STORE(store), &iter)) { return nullptr; } gchar *meshid = nullptr; gboolean stockid = FALSE; // gchar *label = nullptr; gtk_tree_model_get (store, &iter, COMBO_COL_STOCK, &stockid, COMBO_COL_MESH, &meshid, -1); // gtk_tree_model_get (store, &iter, COMBO_COL_LABEL, &label, COMBO_COL_STOCK, &stockid, COMBO_COL_MESH, &meshid, -1); // std::cout << " .. meshid: " << (meshid?meshid:"null") << " label: " << (label?label:"null") << std::endl; // g_free(label); if (meshid == nullptr) { return nullptr; } SPMeshGradient *mesh = nullptr; if (strcmp(meshid, "none")){ gchar *mesh_name; if (stockid) { mesh_name = g_strconcat("urn:inkscape:mesh:", meshid, NULL); } else { mesh_name = g_strdup(meshid); } SPObject *mesh_obj = get_stock_item(mesh_name); if (mesh_obj && SP_IS_MESHGRADIENT(mesh_obj)) { mesh = SP_MESHGRADIENT(mesh_obj); } g_free(mesh_name); } else { std::cerr << "SPPaintSelector::getMeshGradient: Unexpected meshid value." << std::endl; } g_free(meshid); return mesh; } #endif // ************************ End Mesh ************************ static void sp_paint_selector_set_style_buttons(SPPaintSelector *psel, GtkWidget *active) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->none), (active == psel->none)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->solid), (active == psel->solid)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->gradient), (active == psel->gradient)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->radial), (active == psel->radial)); #ifdef WITH_MESH gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->mesh), (active == psel->mesh)); #endif gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->pattern), (active == psel->pattern)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->swatch), (active == psel->swatch)); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psel->unset), (active == psel->unset)); } static void sp_psel_pattern_destroy(GtkWidget *widget, SPPaintSelector * /*psel*/) { // drop our reference to the pattern menu widget g_object_unref( G_OBJECT(widget) ); } static void sp_psel_pattern_change(GtkWidget * /*widget*/, SPPaintSelector *psel) { g_signal_emit(G_OBJECT(psel), psel_signals[CHANGED], 0); } /** * Returns a list of patterns in the defs of the given source document as a vector */ static std::vector ink_pattern_list_get (SPDocument *source) { std::vector pl; if (source == nullptr) return pl; std::vector patterns = source->getResourceList("pattern"); for (auto pattern : patterns) { if (SP_PATTERN(pattern) == SP_PATTERN(pattern)->rootPattern()) { // only if this is a root pattern pl.push_back(SP_PATTERN(pattern)); } } return pl; } /** * Adds menu items for pattern list - derived from marker code, left hb etc in to make addition of previews easier at some point. */ static void sp_pattern_menu_build (GtkWidget *combo, std::vector &pl, SPDocument */*source*/) { GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))); GtkTreeIter iter; for (auto i=pl.rbegin(); i!=pl.rend(); ++i) { Inkscape::XML::Node *repr = (*i)->getRepr(); // label for combobox gchar const *label; if (repr->attribute("inkscape:stockid")) { label = _(repr->attribute("inkscape:stockid")); } else { label = _(repr->attribute("id")); } gchar const *patid = repr->attribute("id"); gboolean stockid = false; if (repr->attribute("inkscape:stockid")) { stockid = true; } gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, COMBO_COL_LABEL, label, COMBO_COL_STOCK, stockid, COMBO_COL_PATTERN, patid, COMBO_COL_SEP, FALSE, -1); } } /** * Pick up all patterns from source, except those that are in * current_doc (if non-NULL), and add items to the pattern menu. */ static void sp_pattern_list_from_doc(GtkWidget *combo, SPDocument * /*current_doc*/, SPDocument *source, SPDocument * /*pattern_doc*/) { std::vector pl = ink_pattern_list_get(source); sp_pattern_menu_build (combo, pl, source); } static void ink_pattern_menu_populate_menu(GtkWidget *combo, SPDocument *doc) { static SPDocument *patterns_doc = nullptr; // find and load patterns.svg if (patterns_doc == nullptr) { char *patterns_source = g_build_filename(INKSCAPE_PAINTDIR, "patterns.svg", NULL); if (Inkscape::IO::file_test(patterns_source, G_FILE_TEST_IS_REGULAR)) { patterns_doc = SPDocument::createNewDoc(patterns_source, FALSE); } g_free(patterns_source); } // suck in from current doc sp_pattern_list_from_doc ( combo, nullptr, doc, patterns_doc ); // add separator { GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))); GtkTreeIter iter; gtk_list_store_append (store, &iter); gtk_list_store_set(store, &iter, COMBO_COL_LABEL, "", COMBO_COL_STOCK, false, COMBO_COL_PATTERN, "", COMBO_COL_SEP, true, -1); } // suck in from patterns.svg if (patterns_doc) { doc->ensureUpToDate(); sp_pattern_list_from_doc ( combo, doc, patterns_doc, nullptr ); } } static GtkWidget* ink_pattern_menu(GtkWidget *combo) { SPDocument *doc = SP_ACTIVE_DOCUMENT; GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo))); GtkTreeIter iter; if (!doc) { gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, COMBO_COL_LABEL, _("No document selected"), COMBO_COL_STOCK, false, COMBO_COL_PATTERN, "", COMBO_COL_SEP, false, -1); gtk_widget_set_sensitive(combo, FALSE); } else { ink_pattern_menu_populate_menu(combo, doc); gtk_widget_set_sensitive(combo, TRUE); } // Select the first item that is not a separator if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL(store), &iter)) { gboolean sep = false; gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, COMBO_COL_SEP, &sep, -1); if (sep) { gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); } gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); } return combo; } /*update pattern list*/ void SPPaintSelector::updatePatternList( SPPattern *pattern ) { if (update) { return; } GtkWidget *combo = GTK_WIDGET(g_object_get_data(G_OBJECT(this), "patternmenu")); g_assert( combo != nullptr ); /* Clear existing menu if any */ GtkTreeModel *store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); gtk_list_store_clear(GTK_LIST_STORE(store)); ink_pattern_menu(combo); /* Set history */ if (pattern && !g_object_get_data(G_OBJECT(combo), "update")) { g_object_set_data(G_OBJECT(combo), "update", GINT_TO_POINTER(TRUE)); gchar const *patname = pattern->getRepr()->attribute("id"); // Find this pattern and set it active in the combo_box GtkTreeIter iter ; gchar *patid = nullptr; bool valid = gtk_tree_model_get_iter_first (store, &iter); if (!valid) { return; } gtk_tree_model_get (store, &iter, COMBO_COL_PATTERN, &patid, -1); while (valid && strcmp(patid, patname) != 0) { valid = gtk_tree_model_iter_next (store, &iter); g_free(patid); patid = nullptr; gtk_tree_model_get (store, &iter, COMBO_COL_PATTERN, &patid, -1); } g_free(patid); if (valid) { gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); } g_object_set_data(G_OBJECT(combo), "update", GINT_TO_POINTER(FALSE)); } } static void sp_paint_selector_set_mode_pattern(SPPaintSelector *psel, SPPaintSelector::Mode mode) { if (mode == SPPaintSelector::MODE_PATTERN) { sp_paint_selector_set_style_buttons(psel, psel->pattern); } gtk_widget_set_sensitive(psel->style, TRUE); GtkWidget *tbl = nullptr; if (psel->mode == SPPaintSelector::MODE_PATTERN) { /* Already have pattern menu */ tbl = GTK_WIDGET(g_object_get_data(G_OBJECT(psel->selector), "pattern-selector")); } else { sp_paint_selector_clear_frame(psel); /* Create vbox */ tbl = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); gtk_box_set_homogeneous(GTK_BOX(tbl), FALSE); gtk_widget_show(tbl); { auto hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1); gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); /** * Create a combo_box and store with 4 columns, * The label, a pointer to the pattern, is stockid or not, is a separator or not. */ GtkListStore *store = gtk_list_store_new (COMBO_N_COLS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN); GtkWidget *combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); gtk_combo_box_set_row_separator_func(GTK_COMBO_BOX(combo), SPPaintSelector::isSeparator, nullptr, nullptr); GtkCellRenderer *renderer = gtk_cell_renderer_text_new (); gtk_cell_renderer_set_padding (renderer, 2, 0); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, "text", COMBO_COL_LABEL, NULL); ink_pattern_menu(combo); g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(sp_psel_pattern_change), psel); g_signal_connect(G_OBJECT(combo), "destroy", G_CALLBACK(sp_psel_pattern_destroy), psel); g_object_set_data(G_OBJECT(psel), "patternmenu", combo); g_object_ref( G_OBJECT(combo)); gtk_container_add(GTK_CONTAINER(hb), combo); gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); g_object_unref( G_OBJECT(store)); } { auto hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); auto l = gtk_label_new(nullptr); gtk_label_set_markup(GTK_LABEL(l), _("Use the Node tool to adjust position, scale, and rotation of the pattern on canvas. Use Object > Pattern > Objects to Pattern to create a new pattern from selection.")); gtk_label_set_line_wrap(GTK_LABEL(l), true); gtk_widget_set_size_request(l, 180, -1); gtk_box_pack_start(GTK_BOX(hb), l, TRUE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); gtk_box_pack_start(GTK_BOX(tbl), hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); } gtk_widget_show_all(tbl); gtk_container_add(GTK_CONTAINER(psel->frame), tbl); psel->selector = tbl; g_object_set_data(G_OBJECT(psel->selector), "pattern-selector", tbl); gtk_label_set_markup(GTK_LABEL(psel->label), _("Pattern fill")); } #ifdef SP_PS_VERBOSE g_print("Pattern req\n"); #endif } static void sp_paint_selector_set_mode_hatch(SPPaintSelector *psel, SPPaintSelector::Mode mode) { if (mode == SPPaintSelector::MODE_HATCH) { sp_paint_selector_set_style_buttons(psel, psel->unset); } gtk_widget_set_sensitive(psel->style, TRUE); if (psel->mode == SPPaintSelector::MODE_HATCH) { /* Already have hatch menu, for the moment unset */ } else { sp_paint_selector_clear_frame(psel); gtk_label_set_markup(GTK_LABEL(psel->label), _("Hatch fill")); } #ifdef SP_PS_VERBOSE g_print("Hatch req\n"); #endif } gboolean SPPaintSelector::isSeparator (GtkTreeModel *model, GtkTreeIter *iter, gpointer /*data*/) { gboolean sep = FALSE; gtk_tree_model_get(model, iter, COMBO_COL_SEP, &sep, -1); return sep; } SPPattern *SPPaintSelector::getPattern() { SPPattern *pat = nullptr; g_return_val_if_fail(mode == MODE_PATTERN, NULL); GtkWidget *combo = GTK_WIDGET(g_object_get_data(G_OBJECT(this), "patternmenu")); /* no pattern menu if we were just selected */ if (combo == nullptr) { return nullptr; } GtkTreeModel *store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); /* Get the selected pattern */ GtkTreeIter iter; if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter) || !gtk_list_store_iter_is_valid(GTK_LIST_STORE(store), &iter)) { return nullptr; } gchar *patid = nullptr; gboolean stockid = FALSE; // gchar *label = nullptr; gtk_tree_model_get(store, &iter, // COMBO_COL_LABEL, &label, COMBO_COL_STOCK, &stockid, COMBO_COL_PATTERN, &patid, -1); // g_free(label); if (patid == nullptr) { return nullptr; } if (strcmp(patid, "none") != 0) { gchar *paturn; if (stockid) { paturn = g_strconcat("urn:inkscape:pattern:", patid, NULL); } else { paturn = g_strdup(patid); } SPObject *pat_obj = get_stock_item(paturn); if (pat_obj) { pat = SP_PATTERN(pat_obj); } g_free(paturn); } else { SPDocument *doc = SP_ACTIVE_DOCUMENT; SPObject *pat_obj = doc->getObjectById(patid); if (pat_obj && SP_IS_PATTERN(pat_obj)) { pat = SP_PATTERN(pat_obj)->rootPattern(); } } g_free(patid); return pat; } static void sp_paint_selector_set_mode_swatch(SPPaintSelector *psel, SPPaintSelector::Mode mode) { if (mode == SPPaintSelector::MODE_SWATCH) { sp_paint_selector_set_style_buttons(psel, psel->swatch); } gtk_widget_set_sensitive(psel->style, TRUE); if (psel->mode == SPPaintSelector::MODE_SWATCH){ // swatchsel = static_cast(g_object_get_data(G_OBJECT(psel->selector), "swatch-selector")); } else { sp_paint_selector_clear_frame(psel); // Create new gradient selector SwatchSelector *swatchsel = new SwatchSelector(); swatchsel->show(); swatchsel->connectGrabbedHandler( G_CALLBACK(sp_paint_selector_gradient_grabbed), psel ); swatchsel->connectDraggedHandler( G_CALLBACK(sp_paint_selector_gradient_dragged), psel ); swatchsel->connectReleasedHandler( G_CALLBACK(sp_paint_selector_gradient_released), psel ); swatchsel->connectchangedHandler( G_CALLBACK(sp_paint_selector_gradient_changed), psel ); // Pack everything to frame gtk_container_add(GTK_CONTAINER(psel->frame), GTK_WIDGET(swatchsel->gobj())); psel->selector = GTK_WIDGET(swatchsel->gobj()); g_object_set_data(G_OBJECT(psel->selector), "swatch-selector", swatchsel); gtk_label_set_markup(GTK_LABEL(psel->label), _("Swatch fill")); } #ifdef SP_PS_VERBOSE g_print("Swatch req\n"); #endif } // TODO this seems very bad to be taking in a desktop pointer to muck with. Logic probably belongs elsewhere void SPPaintSelector::setFlatColor( SPDesktop *desktop, gchar const *color_property, gchar const *opacity_property ) { SPCSSAttr *css = sp_repr_css_attr_new(); SPColor color; gfloat alpha = 0; getColorAlpha( color, alpha ); std::string colorStr = color.toString(); #ifdef SP_PS_VERBOSE guint32 rgba = color.toRGBA32( alpha ); g_message("sp_paint_selector_set_flat_color() to '%s' from 0x%08x::%s", colorStr.c_str(), rgba, (color.icc ? color.icc->colorProfile.c_str():"") ); #endif // SP_PS_VERBOSE sp_repr_css_set_property(css, color_property, colorStr.c_str()); Inkscape::CSSOStringStream osalpha; osalpha << alpha; sp_repr_css_set_property(css, opacity_property, osalpha.str().c_str()); sp_desktop_set_style(desktop, css); sp_repr_css_attr_unref(css); } SPPaintSelector::Mode SPPaintSelector::getModeForStyle(SPStyle const & style, FillOrStroke kind) { Mode mode = MODE_UNSET; SPIPaint const &target = *style.getFillOrStroke(kind == FILL); if ( !target.set ) { mode = MODE_UNSET; } else if ( target.isPaintserver() ) { SPPaintServer const *server = (kind == FILL) ? style.getFillPaintServer() : style.getStrokePaintServer(); #ifdef SP_PS_VERBOSE g_message("SPPaintSelector::getModeForStyle(%p, %d)", &style, kind); g_message("==== server:%p %s grad:%s swatch:%s", server, server->getId(), (SP_IS_GRADIENT(server)?"Y":"n"), (SP_IS_GRADIENT(server) && SP_GRADIENT(server)->getVector()->isSwatch()?"Y":"n")); #endif // SP_PS_VERBOSE if (server && SP_IS_GRADIENT(server) && SP_GRADIENT(server)->getVector()->isSwatch()) { mode = MODE_SWATCH; } else if (SP_IS_LINEARGRADIENT(server)) { mode = MODE_GRADIENT_LINEAR; } else if (SP_IS_RADIALGRADIENT(server)) { mode = MODE_GRADIENT_RADIAL; #ifdef WITH_MESH } else if (SP_IS_MESHGRADIENT(server)) { mode = MODE_GRADIENT_MESH; #endif } else if (SP_IS_PATTERN(server)) { mode = MODE_PATTERN; } else if (SP_IS_HATCH(server)) { mode = MODE_HATCH; } else { g_warning( "file %s: line %d: Unknown paintserver", __FILE__, __LINE__ ); mode = MODE_NONE; } } else if ( target.isColor() ) { // TODO this is no longer a valid assertion: mode = MODE_SOLID_COLOR; // so far only rgb can be read from svg } else if ( target.isNone() ) { mode = MODE_NONE; } else { g_warning( "file %s: line %d: Unknown paint type", __FILE__, __LINE__ ); mode = MODE_NONE; } return mode; } /* 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 :