diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:29:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:29:01 +0000 |
commit | 35a96bde514a8897f6f0fcc41c5833bf63df2e2a (patch) | |
tree | 657d15a03cc46bd099fc2c6546a7a4ad43815d9f /src/widgets/gradient-vector.cpp | |
parent | Initial commit. (diff) | |
download | inkscape-35a96bde514a8897f6f0fcc41c5833bf63df2e2a.tar.xz inkscape-35a96bde514a8897f6f0fcc41c5833bf63df2e2a.zip |
Adding upstream version 1.0.2.upstream/1.0.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/widgets/gradient-vector.cpp')
-rw-r--r-- | src/widgets/gradient-vector.cpp | 1314 |
1 files changed, 1314 insertions, 0 deletions
diff --git a/src/widgets/gradient-vector.cpp b/src/widgets/gradient-vector.cpp new file mode 100644 index 0000000..a2ca973 --- /dev/null +++ b/src/widgets/gradient-vector.cpp @@ -0,0 +1,1314 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Gradient vector selection widget + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * bulia byak <buliabyak@users.sf.net> + * MenTaLguY <mental@rydia.net> + * Jon A. Cruz <jon@joncruz.org> + * Abhishek Sharma + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2001 Ximian, Inc. + * Copyright (C) 2004 Monash University + * Copyright (C) 2004 David Turner + * Copyright (C) 2006 MenTaLguY + * Copyright (C) 2010 Jon A. Cruz + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + * + */ + +#include <set> + +#include <glibmm.h> +#include <glibmm/i18n.h> + + + + +#include "gradient-chemistry.h" +#include "inkscape.h" +#include "preferences.h" +#include "desktop.h" +#include "document-undo.h" +#include "gradient-vector.h" +#include "layer-manager.h" +#include "include/macros.h" +#include "selection-chemistry.h" +#include "verbs.h" + +#include "io/resource.h" + +#include "object/sp-defs.h" +#include "object/sp-linear-gradient.h" +#include "object/sp-radial-gradient.h" +#include "object/sp-root.h" +#include "object/sp-stop.h" +#include "style.h" + +#include "svg/css-ostringstream.h" + +#include "ui/dialog-events.h" +#include "ui/selected-color.h" +#include "ui/widget/color-notebook.h" +#include "ui/widget/color-preview.h" + +#include "widgets/gradient-image.h" + +#include "xml/repr.h" + +using Inkscape::DocumentUndo; +using Inkscape::UI::SelectedColor; + +enum { + VECTOR_SET, + LAST_SIGNAL +}; + +static void sp_gradient_vector_selector_destroy(GtkWidget *object); + +static void sp_gvs_gradient_release(SPObject *obj, SPGradientVectorSelector *gvs); +static void sp_gvs_defs_release(SPObject *defs, SPGradientVectorSelector *gvs); +static void sp_gvs_defs_modified(SPObject *defs, guint flags, SPGradientVectorSelector *gvs); + +static void sp_gvs_rebuild_gui_full(SPGradientVectorSelector *gvs); +static SPStop *get_selected_stop( GtkWidget *vb); +void gr_get_usage_counts(SPDocument *doc, std::map<SPGradient *, gint> *mapUsageCount ); +unsigned long sp_gradient_to_hhssll(SPGradient *gr); + +static guint signals[LAST_SIGNAL] = {0}; + +// TODO FIXME kill these globals!!! +static GtkWidget *dlg = nullptr; +static win_data wd; +static gint x = -1000, y = -1000, w = 0, h = 0; // impossible original values to make sure they are read from prefs +static Glib::ustring const prefs_path = "/dialogs/gradienteditor/"; + +G_DEFINE_TYPE(SPGradientVectorSelector, sp_gradient_vector_selector, GTK_TYPE_BOX); + +static void sp_gradient_vector_selector_class_init(SPGradientVectorSelectorClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + signals[VECTOR_SET] = g_signal_new( "vector_set", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(SPGradientVectorSelectorClass, vector_set), + nullptr, nullptr, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); + + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + widget_class->destroy = sp_gradient_vector_selector_destroy; +} + +static void sp_gradient_vector_selector_init(SPGradientVectorSelector *gvs) +{ + gtk_orientable_set_orientation(GTK_ORIENTABLE(gvs), GTK_ORIENTATION_VERTICAL); + + gvs->idlabel = TRUE; + + gvs->swatched = false; + + gvs->doc = nullptr; + gvs->gr = nullptr; + + new (&gvs->gradient_release_connection) sigc::connection(); + new (&gvs->defs_release_connection) sigc::connection(); + new (&gvs->defs_modified_connection) sigc::connection(); + + gvs->columns = new SPGradientSelector::ModelColumns(); + gvs->store = Gtk::ListStore::create(*gvs->columns); + new (&gvs->tree_select_connection) sigc::connection(); + +} + +static void sp_gradient_vector_selector_destroy(GtkWidget *object) +{ + SPGradientVectorSelector *gvs = SP_GRADIENT_VECTOR_SELECTOR(object); + + if (gvs->gr) { + gvs->gradient_release_connection.disconnect(); + gvs->tree_select_connection.disconnect(); + gvs->gr = nullptr; + } + + if (gvs->doc) { + gvs->defs_release_connection.disconnect(); + gvs->defs_modified_connection.disconnect(); + gvs->doc = nullptr; + } + + gvs->gradient_release_connection.~connection(); + gvs->defs_release_connection.~connection(); + gvs->defs_modified_connection.~connection(); + gvs->tree_select_connection.~connection(); + + if ((GTK_WIDGET_CLASS(sp_gradient_vector_selector_parent_class))->destroy) { + (GTK_WIDGET_CLASS(sp_gradient_vector_selector_parent_class))->destroy(object); + } +} + +GtkWidget *sp_gradient_vector_selector_new(SPDocument *doc, SPGradient *gr) +{ + GtkWidget *gvs; + + g_return_val_if_fail(!gr || SP_IS_GRADIENT(gr), NULL); + g_return_val_if_fail(!gr || (gr->document == doc), NULL); + + gvs = static_cast<GtkWidget*>(g_object_new(SP_TYPE_GRADIENT_VECTOR_SELECTOR, nullptr)); + + if (doc) { + sp_gradient_vector_selector_set_gradient(SP_GRADIENT_VECTOR_SELECTOR(gvs), doc, gr); + } else { + sp_gvs_rebuild_gui_full(SP_GRADIENT_VECTOR_SELECTOR(gvs)); + } + + return gvs; +} + +void sp_gradient_vector_selector_set_gradient(SPGradientVectorSelector *gvs, SPDocument *doc, SPGradient *gr) +{ +// g_message("sp_gradient_vector_selector_set_gradient(%p, %p, %p) [%s] %d %d", gvs, doc, gr, +// (gr ? gr->getId():"N/A"), +// (gr ? gr->isSwatch() : -1), +// (gr ? gr->isSolid() : -1)); + static gboolean suppress = FALSE; + + g_return_if_fail(gvs != nullptr); + g_return_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs)); + g_return_if_fail(!gr || (doc != nullptr)); + g_return_if_fail(!gr || SP_IS_GRADIENT(gr)); + g_return_if_fail(!gr || (gr->document == doc)); + g_return_if_fail(!gr || gr->hasStops()); + + if (doc != gvs->doc) { + /* Disconnect signals */ + if (gvs->gr) { + gvs->gradient_release_connection.disconnect(); + gvs->gr = nullptr; + } + if (gvs->doc) { + gvs->defs_release_connection.disconnect(); + gvs->defs_modified_connection.disconnect(); + gvs->doc = nullptr; + } + + // Connect signals + if (doc) { + gvs->defs_release_connection = doc->getDefs()->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gvs_defs_release), gvs)); + gvs->defs_modified_connection = doc->getDefs()->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_gvs_defs_modified), gvs)); + } + if (gr) { + gvs->gradient_release_connection = gr->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gvs_gradient_release), gvs)); + } + gvs->doc = doc; + gvs->gr = gr; + sp_gvs_rebuild_gui_full(gvs); + if (!suppress) g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, gr); + } else if (gr != gvs->gr) { + // Harder case - keep document, rebuild list and stuff + // fixme: (Lauris) + suppress = TRUE; + sp_gradient_vector_selector_set_gradient(gvs, nullptr, nullptr); + sp_gradient_vector_selector_set_gradient(gvs, doc, gr); + suppress = FALSE; + g_signal_emit(G_OBJECT(gvs), signals[VECTOR_SET], 0, gr); + } + /* The case of setting NULL -> NULL is not very interesting */ +} + +SPDocument *sp_gradient_vector_selector_get_document(SPGradientVectorSelector *gvs) +{ + g_return_val_if_fail(gvs != nullptr, NULL); + g_return_val_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs), NULL); + + return gvs->doc; +} + +SPGradient *sp_gradient_vector_selector_get_gradient(SPGradientVectorSelector *gvs) +{ + g_return_val_if_fail(gvs != nullptr, NULL); + g_return_val_if_fail(SP_IS_GRADIENT_VECTOR_SELECTOR(gvs), NULL); + + return gvs->gr; +} + +Glib::ustring gr_prepare_label (SPObject *obj) +{ + const gchar *id = obj->label() ? obj->label() : obj->getId(); + if (!id) { + id = obj->getRepr()->name(); + } + + if (strlen(id) > 14 && (!strncmp (id, "linearGradient", 14) || !strncmp (id, "radialGradient", 14))) + return gr_ellipsize_text (g_strdup_printf ("%s", id+14), 35); + return gr_ellipsize_text (id, 35); +} + +/* + * Ellipse text if longer than maxlen, "50% start text + ... + ~50% end text" + * Text should be > length 8 or just return the original text + */ +Glib::ustring gr_ellipsize_text(Glib::ustring const &src, size_t maxlen) +{ + if (src.length() > maxlen && maxlen > 8) { + size_t p1 = (size_t) maxlen / 2; + size_t p2 = (size_t) src.length() - (maxlen - p1 - 1); + return src.substr(0, p1) + "…" + src.substr(p2); + } + return src; +} + +static void sp_gvs_rebuild_gui_full(SPGradientVectorSelector *gvs) +{ + + gvs->tree_select_connection.block(); + + /* Clear old list, if there is any */ + gvs->store->clear(); + + /* Pick up all gradients with vectors */ + std::vector<SPGradient *> gl; + if (gvs->gr) { + std::vector<SPObject *> gradients = gvs->gr->document->getResourceList("gradient"); + for (auto gradient : gradients) { + SPGradient* grad = SP_GRADIENT(gradient); + if ( grad->hasStops() && (grad->isSwatch() == gvs->swatched) ) { + gl.push_back(SP_GRADIENT(gradient)); + } + } + } + + /* Get usage count of all the gradients */ + std::map<SPGradient *, gint> usageCount; + gr_get_usage_counts(gvs->doc, &usageCount); + + if (!gvs->doc) { + Gtk::TreeModel::Row row = *(gvs->store->append()); + row[gvs->columns->name] = _("No document selected"); + + } else if (gl.empty()) { + Gtk::TreeModel::Row row = *(gvs->store->append()); + row[gvs->columns->name] = _("No gradients in document"); + + } else if (!gvs->gr) { + Gtk::TreeModel::Row row = *(gvs->store->append()); + row[gvs->columns->name] = _("No gradient selected"); + + } else { + for (auto gr:gl) { + unsigned long hhssll = sp_gradient_to_hhssll(gr); + GdkPixbuf *pixb = sp_gradient_to_pixbuf (gr, 64, 18); + Glib::ustring label = gr_prepare_label(gr); + + Gtk::TreeModel::Row row = *(gvs->store->append()); + row[gvs->columns->name] = label.c_str(); + row[gvs->columns->color] = hhssll; + row[gvs->columns->refcount] = usageCount[gr]; + row[gvs->columns->data] = gr; + row[gvs->columns->pixbuf] = Glib::wrap(pixb); + } + } + + gvs->tree_select_connection.unblock(); + +} + +/* + * Return a "HHSSLL" version of the first stop color so we can sort by it + */ +unsigned long sp_gradient_to_hhssll(SPGradient *gr) +{ + SPStop *stop = gr->getFirstStop(); + unsigned long rgba = stop->get_rgba32(); + float hsl[3]; + SPColor::rgb_to_hsl_floatv (hsl, SP_RGBA32_R_F(rgba), SP_RGBA32_G_F(rgba), SP_RGBA32_B_F(rgba)); + + return ((int)(hsl[0]*100 * 10000)) + ((int)(hsl[1]*100 * 100)) + ((int)(hsl[2]*100 * 1)); +} + +static void get_all_doc_items(std::vector<SPItem*> &list, SPObject *from) +{ + for (auto& child: from->children) { + if (SP_IS_ITEM(&child)) { + list.push_back(SP_ITEM(&child)); + } + get_all_doc_items(list, &child); + } +} + +/* + * Return a SPItem's gradient + */ +static SPGradient * gr_item_get_gradient(SPItem *item, gboolean fillorstroke) +{ + SPIPaint *item_paint = item->style->getFillOrStroke(fillorstroke); + if (item_paint->isPaintserver()) { + + SPPaintServer *item_server = (fillorstroke) ? + item->style->getFillPaintServer() : item->style->getStrokePaintServer(); + + if (SP_IS_LINEARGRADIENT(item_server) || SP_IS_RADIALGRADIENT(item_server) || + (SP_IS_GRADIENT(item_server) && SP_GRADIENT(item_server)->getVector()->isSwatch())) { + + return SP_GRADIENT(item_server)->getVector(); + } + } + + return nullptr; +} + +/* + * Map each gradient to its usage count for both fill and stroke styles + */ +void gr_get_usage_counts(SPDocument *doc, std::map<SPGradient *, gint> *mapUsageCount ) +{ + if (!doc) + return; + + std::vector<SPItem *> all_list; + get_all_doc_items(all_list, doc->getRoot()); + + for (auto item:all_list) { + if (!item->getId()) + continue; + SPGradient *gr = nullptr; + gr = gr_item_get_gradient(item, true); // fill + if (gr) { + mapUsageCount->count(gr) > 0 ? (*mapUsageCount)[gr] += 1 : (*mapUsageCount)[gr] = 1; + } + gr = gr_item_get_gradient(item, false); // stroke + if (gr) { + mapUsageCount->count(gr) > 0 ? (*mapUsageCount)[gr] += 1 : (*mapUsageCount)[gr] = 1; + } + } +} + +static void sp_gvs_gradient_release(SPObject */*obj*/, SPGradientVectorSelector *gvs) +{ + /* Disconnect gradient */ + if (gvs->gr) { + gvs->gradient_release_connection.disconnect(); + gvs->gr = nullptr; + } + + /* Rebuild GUI */ + sp_gvs_rebuild_gui_full(gvs); +} + +static void sp_gvs_defs_release(SPObject */*defs*/, SPGradientVectorSelector *gvs) +{ + gvs->doc = nullptr; + + gvs->defs_release_connection.disconnect(); + gvs->defs_modified_connection.disconnect(); + + /* Disconnect gradient as well */ + if (gvs->gr) { + gvs->gradient_release_connection.disconnect(); + gvs->gr = nullptr; + } + + /* Rebuild GUI */ + sp_gvs_rebuild_gui_full(gvs); +} + +static void sp_gvs_defs_modified(SPObject */*defs*/, guint /*flags*/, SPGradientVectorSelector *gvs) +{ + /* fixme: We probably have to check some flags here (Lauris) */ + sp_gvs_rebuild_gui_full(gvs); +} + +void SPGradientVectorSelector::setSwatched() +{ + swatched = true; + sp_gvs_rebuild_gui_full(this); +} + +/*################################################################## + ### Vector Editing Widget + ##################################################################*/ + +#include "widgets/widget-sizes.h" +#include "xml/node-event-vector.h" +#include "svg/svg-color.h" + +#define PAD 4 + +static GtkWidget *sp_gradient_vector_widget_new(SPGradient *gradient, SPStop *stop); + +static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradient *gradient); +static gint sp_gradient_vector_dialog_delete(GtkWidget *widget, GdkEvent *event, GtkWidget *dialog); +static void sp_gradient_vector_dialog_destroy(GtkWidget *object, gpointer data); +static void sp_gradient_vector_widget_destroy(GtkWidget *object, gpointer data); +static void sp_gradient_vector_gradient_release(SPObject *obj, GtkWidget *widget); +static void sp_gradient_vector_gradient_modified(SPObject *obj, guint flags, GtkWidget *widget); +static void sp_gradient_vector_color_dragged(Inkscape::UI::SelectedColor *selected_color, GObject *object); +static void sp_gradient_vector_color_changed(Inkscape::UI::SelectedColor *selected_color, GObject *object); +static void update_stop_list( GtkWidget *vb, SPGradient *gradient, SPStop *new_stop); + +static gboolean blocked = FALSE; + +static void grad_edit_dia_stop_added_or_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node */*child*/, Inkscape::XML::Node */*ref*/, gpointer data) +{ + GtkWidget *vb = GTK_WIDGET(data); + SPGradient *gradient = static_cast<SPGradient *>(g_object_get_data(G_OBJECT(vb), "gradient")); + update_stop_list(vb, gradient, nullptr); +} + +//FIXME!!! We must also listen to attr changes on all children (i.e. stops) too, +//otherwise the dialog does not reflect undoing color or offset change. This is a major +//hassle, unless we have a "one of the descendants changed in some way" signal. +static Inkscape::XML::NodeEventVector grad_edit_dia_repr_events = +{ + grad_edit_dia_stop_added_or_removed, /* child_added */ + grad_edit_dia_stop_added_or_removed, /* child_removed */ + nullptr, /* attr_changed*/ + nullptr, /* content_changed */ + nullptr /* order_changed */ +}; + +static void verify_grad(SPGradient *gradient) +{ + int i = 0; + SPStop *stop = nullptr; + /* count stops */ + for (auto& ochild: gradient->children) { + if (SP_IS_STOP(&ochild)) { + i++; + stop = SP_STOP(&ochild); + } + } + + Inkscape::XML::Document *xml_doc; + xml_doc = gradient->getRepr()->document(); + + if (i < 1) { + Inkscape::CSSOStringStream os; + os << "stop-color: #000000;stop-opacity:" << 1.0 << ";"; + + Inkscape::XML::Node *child; + + child = xml_doc->createElement("svg:stop"); + sp_repr_set_css_double(child, "offset", 0.0); + child->setAttribute("style", os.str()); + gradient->getRepr()->addChild(child, nullptr); + Inkscape::GC::release(child); + + child = xml_doc->createElement("svg:stop"); + sp_repr_set_css_double(child, "offset", 1.0); + child->setAttribute("style", os.str()); + gradient->getRepr()->addChild(child, nullptr); + Inkscape::GC::release(child); + return; + } + if (i < 2) { + sp_repr_set_css_double(stop->getRepr(), "offset", 0.0); + Inkscape::XML::Node *child = stop->getRepr()->duplicate(gradient->getRepr()->document()); + sp_repr_set_css_double(child, "offset", 1.0); + gradient->getRepr()->addChild(child, stop->getRepr()); + Inkscape::GC::release(child); + } +} + +static void select_stop_in_list( GtkWidget *vb, SPGradient *gradient, SPStop *new_stop) +{ + GtkWidget *combo_box = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(vb), "combo_box")); + + int i = 0; + for (auto& ochild: gradient->children) { + if (SP_IS_STOP(&ochild)) { + if (&ochild == new_stop) { + gtk_combo_box_set_active (GTK_COMBO_BOX(combo_box) , i); + break; + } + i++; + } + } +} + +static void update_stop_list( GtkWidget *vb, SPGradient *gradient, SPStop *new_stop) +{ + + if (!SP_IS_GRADIENT(gradient)) { + return; + } + + blocked = TRUE; + + /* Clear old list, if there is any */ + GtkWidget *combo_box = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(vb), "combo_box")); + if (!combo_box) { + return; + } + GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo_box))); + if (!store) { + return; + } + gtk_list_store_clear(store); + GtkTreeIter iter; + + /* Populate the combobox store */ + std::vector<SPStop *> sl; + if ( gradient->hasStops() ) { + for (auto& ochild: gradient->children) { + if (SP_IS_STOP(&ochild)) { + sl.push_back(SP_STOP(&ochild)); + } + } + } + if (sl.empty()) { + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, NULL, 1, _("No stops in gradient"), 2, NULL, -1); + gtk_widget_set_sensitive (combo_box, FALSE); + + } else { + + for (auto stop:sl) { + Inkscape::XML::Node *repr = stop->getRepr(); + Inkscape::UI::Widget::ColorPreview *cpv = Gtk::manage(new Inkscape::UI::Widget::ColorPreview(stop->get_rgba32())); + GdkPixbuf *pb = cpv->toPixbuf(64, 16); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, pb, 1, repr->attribute("id"), 2, stop, -1); + gtk_widget_set_sensitive (combo_box, FALSE); + } + gtk_widget_set_sensitive(combo_box, TRUE); + } + + /* Set history */ + if (new_stop == nullptr) { + gtk_combo_box_set_active (GTK_COMBO_BOX(combo_box) , 0); + } else { + select_stop_in_list(vb, gradient, new_stop); + } + + blocked = FALSE; +} + + +// user selected existing stop from list +static void sp_grad_edit_combo_box_changed (GtkComboBox * /*widget*/, GtkWidget *tbl) +{ + SPStop *stop = get_selected_stop(tbl); + if (!stop) { + return; + } + + blocked = TRUE; + + SelectedColor *csel = static_cast<SelectedColor*>(g_object_get_data(G_OBJECT(tbl), "cselector")); + // set its color, from the stored array + g_object_set_data(G_OBJECT(tbl), "updating_color", reinterpret_cast<void*>(1)); + csel->setColorAlpha(stop->getColor(), stop->getOpacity()); + g_object_set_data(G_OBJECT(tbl), "updating_color", reinterpret_cast<void*>(0)); + GtkWidget *offspin = GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offspn")); + GtkWidget *offslide =GTK_WIDGET(g_object_get_data(G_OBJECT(tbl), "offslide")); + + GtkAdjustment *adj = static_cast<GtkAdjustment*>(g_object_get_data(G_OBJECT(tbl), "offset")); + + bool isEndStop = false; + + SPStop *prev = nullptr; + prev = stop->getPrevStop(); + if (prev != nullptr ) { + gtk_adjustment_set_lower (adj, prev->offset); + } else { + isEndStop = true; + gtk_adjustment_set_lower (adj, 0); + } + + SPStop *next = nullptr; + next = stop->getNextStop(); + if (next != nullptr ) { + gtk_adjustment_set_upper (adj, next->offset); + } else { + isEndStop = true; + gtk_adjustment_set_upper (adj, 1.0); + } + + //fixme: does this work on all possible input gradients? + if (!isEndStop) { + gtk_widget_set_sensitive(offslide, TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(offspin), TRUE); + } else { + gtk_widget_set_sensitive(offslide, FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(offspin), FALSE); + } + + gtk_adjustment_set_value(adj, stop->offset); + + blocked = FALSE; +} + +static SPStop *get_selected_stop( GtkWidget *vb) +{ + SPStop *stop = nullptr; + GtkWidget *combo_box = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(vb), "combo_box")); + if (combo_box) { + GtkTreeIter iter; + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX(combo_box), &iter)) { + GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo_box))); + gtk_tree_model_get (GTK_TREE_MODEL(store), &iter, 2, &stop, -1); + } + } + return stop; +} + +static void offadjustmentChanged( GtkAdjustment *adjustment, GtkWidget *vb) +{ + if (!blocked) { + blocked = TRUE; + + SPStop *stop = get_selected_stop(vb); + if (stop) { + stop->offset = gtk_adjustment_get_value (adjustment); + sp_repr_set_css_double(stop->getRepr(), "offset", stop->offset); + + DocumentUndo::maybeDone(stop->document, "gradient:stop:offset", SP_VERB_CONTEXT_GRADIENT, + _("Change gradient stop offset")); + + } + + blocked = FALSE; + } +} + +guint32 sp_average_color(guint32 c1, guint32 c2, gdouble p/* = 0.5*/) +{ + guint32 r = (guint32) (SP_RGBA32_R_U(c1) * p + SP_RGBA32_R_U(c2) * (1 - p)); + guint32 g = (guint32) (SP_RGBA32_G_U(c1) * p + SP_RGBA32_G_U(c2) * (1 - p)); + guint32 b = (guint32) (SP_RGBA32_B_U(c1) * p + SP_RGBA32_B_U(c2) * (1 - p)); + guint32 a = (guint32) (SP_RGBA32_A_U(c1) * p + SP_RGBA32_A_U(c2) * (1 - p)); + + return SP_RGBA32_U_COMPOSE(r, g, b, a); +} + + +static void sp_grd_ed_add_stop(GtkWidget */*widget*/, GtkWidget *vb) +{ + SPGradient *gradient = static_cast<SPGradient *>(g_object_get_data(G_OBJECT(vb), "gradient")); + verify_grad(gradient); + + SPStop *stop = get_selected_stop(vb); + if (!stop) { + return; + } + + Inkscape::XML::Node *new_stop_repr = nullptr; + + SPStop *next = stop->getNextStop(); + + if (next == nullptr) { + SPStop *prev = stop->getPrevStop(); + if (prev != nullptr) { + next = stop; + stop = prev; + } + } + + if (next != nullptr) { + new_stop_repr = stop->getRepr()->duplicate(gradient->getRepr()->document()); + gradient->getRepr()->addChild(new_stop_repr, stop->getRepr()); + } else { + next = stop; + new_stop_repr = stop->getPrevStop()->getRepr()->duplicate(gradient->getRepr()->document()); + gradient->getRepr()->addChild(new_stop_repr, stop->getPrevStop()->getRepr()); + } + + SPStop *newstop = reinterpret_cast<SPStop *>(gradient->document->getObjectByRepr(new_stop_repr)); + + newstop->offset = (stop->offset + next->offset) * 0.5 ; + + guint32 const c1 = stop->get_rgba32(); + guint32 const c2 = next->get_rgba32(); + guint32 cnew = sp_average_color(c1, c2); + + Inkscape::CSSOStringStream os; + gchar c[64]; + sp_svg_write_color(c, sizeof(c), cnew); + gdouble opacity = static_cast<gdouble>(SP_RGBA32_A_F(cnew)); + os << "stop-color:" << c << ";stop-opacity:" << opacity <<";"; + newstop->setAttribute("style", os.str()); + sp_repr_set_css_double( newstop->getRepr(), "offset", (double)newstop->offset); + + sp_gradient_vector_widget_load_gradient(vb, gradient); + Inkscape::GC::release(new_stop_repr); + update_stop_list(GTK_WIDGET(vb), gradient, newstop); + GtkWidget *offspin = GTK_WIDGET(g_object_get_data(G_OBJECT(vb), "offspn")); + GtkWidget *offslide =GTK_WIDGET(g_object_get_data(G_OBJECT(vb), "offslide")); + gtk_widget_set_sensitive(offslide, TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(offspin), TRUE); + DocumentUndo::done(gradient->document, SP_VERB_CONTEXT_GRADIENT, + _("Add gradient stop")); +} + +static void sp_grd_ed_del_stop(GtkWidget */*widget*/, GtkWidget *vb) +{ + SPGradient *gradient = static_cast<SPGradient *>(g_object_get_data(G_OBJECT(vb), "gradient")); + + SPStop *stop = get_selected_stop(vb); + if (!stop) { + return; + } + + if (gradient->vector.stops.size() > 2) { // 2 is the minimum + + // if we delete first or last stop, move the next/previous to the edge + if (stop->offset == 0) { + SPStop *next = stop->getNextStop(); + if (next) { + next->offset = 0; + sp_repr_set_css_double(next->getRepr(), "offset", 0); + } + } else if (stop->offset == 1) { + SPStop *prev = stop->getPrevStop(); + if (prev) { + prev->offset = 1; + sp_repr_set_css_double(prev->getRepr(), "offset", 1); + } + } + + gradient->getRepr()->removeChild(stop->getRepr()); + sp_gradient_vector_widget_load_gradient(vb, gradient); + update_stop_list(GTK_WIDGET(vb), gradient, nullptr); + DocumentUndo::done(gradient->document, SP_VERB_CONTEXT_GRADIENT, + _("Delete gradient stop")); + } + +} + +static GtkWidget * sp_gradient_vector_widget_new(SPGradient *gradient, SPStop *select_stop) +{ + using Inkscape::UI::Widget::ColorNotebook; + + GtkWidget *vb, *w, *f; + + g_return_val_if_fail(gradient != nullptr, NULL); + g_return_val_if_fail(SP_IS_GRADIENT(gradient), NULL); + + vb = gtk_box_new(GTK_ORIENTATION_VERTICAL, PAD); + gtk_box_set_homogeneous(GTK_BOX(vb), FALSE); + g_signal_connect(G_OBJECT(vb), "destroy", G_CALLBACK(sp_gradient_vector_widget_destroy), NULL); + + w = sp_gradient_image_new(gradient); + g_object_set_data(G_OBJECT(vb), "preview", w); + gtk_widget_show(w); + gtk_box_pack_start(GTK_BOX(vb), w, TRUE, TRUE, PAD); + + sp_repr_add_listener(gradient->getRepr(), &grad_edit_dia_repr_events, vb); + + /* ComboBox of stops with 3 columns, + * The color preview, the label and a pointer to the SPStop + */ + GtkListStore *store = gtk_list_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER); + GtkWidget *combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store)); + + GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, "pixbuf", 0, NULL); + gtk_cell_renderer_set_padding(renderer, 5, 0); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, "text", 1, NULL); + gtk_widget_show(combo_box); + gtk_box_pack_start(GTK_BOX(vb), combo_box, FALSE, FALSE, 0); + g_object_set_data(G_OBJECT(vb), "combo_box", combo_box); + + update_stop_list(GTK_WIDGET(vb), gradient, nullptr); + + g_signal_connect(G_OBJECT(combo_box), "changed", G_CALLBACK(sp_grad_edit_combo_box_changed), vb); + + /* Add and Remove buttons */ + auto hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1); + gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); + // TRANSLATORS: "Stop" means: a "phase" of a gradient + GtkWidget *b = gtk_button_new_with_label(_("Add stop")); + gtk_widget_show(b); + gtk_container_add(GTK_CONTAINER(hb), b); + gtk_widget_set_tooltip_text(b, _("Add another control stop to gradient")); + g_signal_connect(G_OBJECT(b), "clicked", G_CALLBACK(sp_grd_ed_add_stop), vb); + b = gtk_button_new_with_label(_("Delete stop")); + gtk_widget_show(b); + gtk_container_add(GTK_CONTAINER(hb), b); + gtk_widget_set_tooltip_text(b, _("Delete current control stop from gradient")); + g_signal_connect(G_OBJECT(b), "clicked", G_CALLBACK(sp_grd_ed_del_stop), vb); + + gtk_widget_show(hb); + gtk_box_pack_start(GTK_BOX(vb),hb, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); + + /* Offset Slider and stuff */ + hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); + + /* Label */ + GtkWidget *l = gtk_label_new(C_("Gradient","Offset:")); + gtk_widget_set_halign(l, GTK_ALIGN_END); + + gtk_box_pack_start(GTK_BOX(hb),l, FALSE, FALSE, AUX_BETWEEN_BUTTON_GROUPS); + gtk_widget_show(l); + + /* Adjustment */ + GtkAdjustment *Offset_adj = nullptr; + Offset_adj= GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 1.0, 0.01, 0.01, 0.0)); + g_object_set_data(G_OBJECT(vb), "offset", Offset_adj); + + SPStop *stop = get_selected_stop(vb); + if (!stop) { + return nullptr; + } + + gtk_adjustment_set_value(Offset_adj, stop->offset); + + /* Slider */ + auto slider = gtk_scale_new(GTK_ORIENTATION_HORIZONTAL, Offset_adj); + gtk_scale_set_draw_value( GTK_SCALE(slider), FALSE ); + gtk_widget_show(slider); + gtk_box_pack_start(GTK_BOX(hb),slider, TRUE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); + g_object_set_data(G_OBJECT(vb), "offslide", slider); + + /* Spinbutton */ + GtkWidget *sbtn = gtk_spin_button_new(GTK_ADJUSTMENT(Offset_adj), 0.01, 2); + sp_dialog_defocus_on_enter(sbtn); + gtk_widget_show(sbtn); + gtk_box_pack_start(GTK_BOX(hb),sbtn, FALSE, TRUE, AUX_BETWEEN_BUTTON_GROUPS); + g_object_set_data(G_OBJECT(vb), "offspn", sbtn); + + if (stop->offset>0 && stop->offset<1) { + gtk_widget_set_sensitive(slider, TRUE); + gtk_widget_set_sensitive(GTK_WIDGET(sbtn), TRUE); + } else { + gtk_widget_set_sensitive(slider, FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(sbtn), FALSE); + } + + + /* Signals */ + g_signal_connect(G_OBJECT(Offset_adj), "value_changed", + G_CALLBACK(offadjustmentChanged), vb); + + // g_signal_connect(G_OBJECT(slider), "changed", G_CALLBACK(offsliderChanged), vb); + gtk_widget_show(hb); + gtk_box_pack_start(GTK_BOX(vb), hb, FALSE, FALSE, PAD); + + // TRANSLATORS: "Stop" means: a "phase" of a gradient + f = gtk_frame_new(_("Stop Color")); + gtk_widget_show(f); + gtk_box_pack_start(GTK_BOX(vb), f, TRUE, TRUE, PAD); + + Inkscape::UI::SelectedColor *selected_color = new Inkscape::UI::SelectedColor; + g_object_set_data(G_OBJECT(vb), "cselector", selected_color); + g_object_set_data(G_OBJECT(vb), "updating_color", reinterpret_cast<void*>(0)); + selected_color->signal_changed.connect(sigc::bind(sigc::ptr_fun(&sp_gradient_vector_color_changed), selected_color, G_OBJECT(vb))); + selected_color->signal_dragged.connect(sigc::bind(sigc::ptr_fun(&sp_gradient_vector_color_changed), selected_color, G_OBJECT(vb))); + + Gtk::Widget *color_selector = Gtk::manage(new ColorNotebook(*selected_color)); + color_selector->show(); + gtk_container_add(GTK_CONTAINER(f), color_selector->gobj()); + + /* + gtk_widget_show(csel); + gtk_container_add(GTK_CONTAINER(f), csel); + g_signal_connect(G_OBJECT(csel), "dragged", G_CALLBACK(sp_gradient_vector_color_dragged), vb); + g_signal_connect(G_OBJECT(csel), "changed", G_CALLBACK(sp_gradient_vector_color_changed), vb); + */ + + gtk_widget_show(vb); + + sp_gradient_vector_widget_load_gradient(vb, gradient); + + if (select_stop) { + select_stop_in_list(GTK_WIDGET(vb), gradient, select_stop); + } + + return vb; +} + + + +GtkWidget * sp_gradient_vector_editor_new(SPGradient *gradient, SPStop *stop) +{ + if (dlg == nullptr) { + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + + dlg = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title ((GtkWindow *) dlg, _("Gradient editor")); + gtk_window_set_resizable ((GtkWindow *) dlg, true); + + if (x == -1000 || y == -1000) { + x = prefs->getInt(prefs_path + "x", -1000); + y = prefs->getInt(prefs_path + "y", -1000); + } + if (w ==0 || h == 0) { + w = prefs->getInt(prefs_path + "w", 0); + h = prefs->getInt(prefs_path + "h", 0); + } + + if (x<0) { + x=0; + } + if (y<0) { + y=0; + } + + if (x != 0 || y != 0) { + gtk_window_move(reinterpret_cast<GtkWindow *>(dlg), x, y); + } else { + gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER); + } + if (w && h) { + gtk_window_resize(reinterpret_cast<GtkWindow *>(dlg), w, h); + } + sp_transientize(dlg); + wd.win = dlg; + wd.stop = 0; + + GObject *obj = G_OBJECT(dlg); + sigc::connection *conn = nullptr; + + conn = new sigc::connection(INKSCAPE.signal_activate_desktop.connect(sigc::bind(sigc::ptr_fun(&sp_transientize_callback), &wd))); + g_object_set_data(obj, "desktop-activate-connection", conn); + + g_signal_connect(obj, "event", G_CALLBACK(sp_dialog_event_handler), dlg); + g_signal_connect(obj, "destroy", G_CALLBACK(sp_gradient_vector_dialog_destroy), dlg); + g_signal_connect(obj, "delete_event", G_CALLBACK(sp_gradient_vector_dialog_delete), dlg); + + conn = new sigc::connection(INKSCAPE.signal_shut_down.connect( + sigc::hide_return( + sigc::bind(sigc::ptr_fun(&sp_gradient_vector_dialog_delete), (GtkWidget *) nullptr, (GdkEvent *) nullptr, (GtkWidget *) nullptr) + ))); + g_object_set_data(obj, "shutdown-connection", conn); + + conn = new sigc::connection(INKSCAPE.signal_dialogs_hide.connect(sigc::bind(sigc::ptr_fun(>k_widget_hide), dlg))); + g_object_set_data(obj, "dialog-hide-connection", conn); + + conn = new sigc::connection(INKSCAPE.signal_dialogs_unhide.connect(sigc::bind(sigc::ptr_fun(>k_widget_show), dlg))); + g_object_set_data(obj, "dialog-unhide-connection", conn); + + gtk_container_set_border_width(GTK_CONTAINER(dlg), PAD); + + GtkWidget *wid = static_cast<GtkWidget*>(sp_gradient_vector_widget_new(gradient, stop)); + g_object_set_data(G_OBJECT(dlg), "gradient-vector-widget", wid); + /* Connect signals */ + gtk_widget_show(wid); + gtk_container_add(GTK_CONTAINER(dlg), wid); + } else { + // FIXME: temp fix for 0.38 + // Simply load_gradient into the editor does not work for multi-stop gradients, + // as the stop list and other widgets are in a wrong state and crash readily. + // Instead we just delete the window (by sending the delete signal) + // and call sp_gradient_vector_editor_new again, so it creates the window anew. + + GdkEventAny event; + GtkWidget *widget = static_cast<GtkWidget *>(dlg); + event.type = GDK_DELETE; + event.window = gtk_widget_get_window (widget); + event.send_event = TRUE; + g_object_ref(G_OBJECT(event.window)); + gtk_main_do_event(reinterpret_cast<GdkEvent*>(&event)); + g_object_unref(G_OBJECT(event.window)); + + g_assert(dlg == nullptr); + sp_gradient_vector_editor_new(gradient, stop); + } + + return dlg; +} + +static void sp_gradient_vector_widget_load_gradient(GtkWidget *widget, SPGradient *gradient) +{ + blocked = TRUE; + + SPGradient *old; + + old = static_cast<SPGradient*>(g_object_get_data(G_OBJECT(widget), "gradient")); + + if (old != gradient) { + sigc::connection *release_connection; + sigc::connection *modified_connection; + + release_connection = static_cast<sigc::connection *>(g_object_get_data(G_OBJECT(widget), "gradient_release_connection")); + modified_connection = static_cast<sigc::connection *>(g_object_get_data(G_OBJECT(widget), "gradient_modified_connection")); + + if (old) { + g_assert( release_connection != nullptr ); + g_assert( modified_connection != nullptr ); + release_connection->disconnect(); + modified_connection->disconnect(); + sp_signal_disconnect_by_data(old, widget); + } + + if (gradient) { + if (!release_connection) { + release_connection = new sigc::connection(); + } + if (!modified_connection) { + modified_connection = new sigc::connection(); + } + *release_connection = gradient->connectRelease(sigc::bind<1>(sigc::ptr_fun(&sp_gradient_vector_gradient_release), widget)); + *modified_connection = gradient->connectModified(sigc::bind<2>(sigc::ptr_fun(&sp_gradient_vector_gradient_modified), widget)); + } else { + if (release_connection) { + delete release_connection; + release_connection = nullptr; + } + if (modified_connection) { + delete modified_connection; + modified_connection = nullptr; + } + } + + g_object_set_data(G_OBJECT(widget), "gradient_release_connection", release_connection); + g_object_set_data(G_OBJECT(widget), "gradient_modified_connection", modified_connection); + } + + g_object_set_data(G_OBJECT(widget), "gradient", gradient); + + if (gradient) { + gtk_widget_set_sensitive(widget, TRUE); + + gradient->ensureVector(); + + SPStop *stop = get_selected_stop(widget); + if (!stop) { + return; + } + + // get the color selector + SelectedColor *csel = static_cast<SelectedColor*>(g_object_get_data(G_OBJECT(widget), "cselector")); + + g_object_set_data(G_OBJECT(widget), "updating_color", reinterpret_cast<void*>(1)); + csel->setColorAlpha(stop->getColor(), stop->getOpacity()); + g_object_set_data(G_OBJECT(widget), "updating_color", reinterpret_cast<void*>(0)); + + /* Fill preview */ + GtkWidget *w = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(widget), "preview")); + sp_gradient_image_set_gradient(SP_GRADIENT_IMAGE(w), gradient); + + update_stop_list(GTK_WIDGET(widget), gradient, nullptr); + + // Once the user edits a gradient, it stops being auto-collectable + if (gradient->getRepr()->attribute("inkscape:collect")) { + SPDocument *document = gradient->document; + DocumentUndo::ScopedInsensitive _no_undo(document); + gradient->removeAttribute("inkscape:collect"); + } + } else { // no gradient, disable everything + gtk_widget_set_sensitive(widget, FALSE); + } + + blocked = FALSE; +} + +static void sp_gradient_vector_dialog_destroy(GtkWidget * /*object*/, gpointer /*data*/) +{ + GObject *obj = G_OBJECT(dlg); + assert(obj != NULL); + + sigc::connection *conn = static_cast<sigc::connection *>(g_object_get_data(obj, "desktop-activate-connection")); + assert(conn != NULL); + conn->disconnect(); + delete conn; + + conn = static_cast<sigc::connection *>(g_object_get_data(obj, "shutdown-connection")); + assert(conn != NULL); + conn->disconnect(); + delete conn; + + conn = static_cast<sigc::connection *>(g_object_get_data(obj, "dialog-hide-connection")); + assert(conn != NULL); + conn->disconnect(); + delete conn; + + conn = static_cast<sigc::connection *>(g_object_get_data(obj, "dialog-unhide-connection")); + assert(conn != NULL); + conn->disconnect(); + delete conn; + + wd.win = dlg = nullptr; + wd.stop = 0; +} + +static gboolean sp_gradient_vector_dialog_delete(GtkWidget */*widget*/, GdkEvent */*event*/, GtkWidget */*dialog*/) +{ + gtk_window_get_position(GTK_WINDOW(dlg), &x, &y); + gtk_window_get_size(GTK_WINDOW(dlg), &w, &h); + + if (x<0) { + x=0; + } + if (y<0) { + y=0; + } + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setInt(prefs_path + "x", x); + prefs->setInt(prefs_path + "y", y); + prefs->setInt(prefs_path + "w", w); + prefs->setInt(prefs_path + "h", h); + + return FALSE; // which means, go ahead and destroy it +} + +/* Widget destroy handler */ +static void sp_gradient_vector_widget_destroy(GtkWidget *object, gpointer /*data*/) +{ + SPObject *gradient = SP_OBJECT(g_object_get_data(G_OBJECT(object), "gradient")); + + sigc::connection *release_connection = static_cast<sigc::connection *>(g_object_get_data(G_OBJECT(object), "gradient_release_connection")); + sigc::connection *modified_connection = static_cast<sigc::connection *>(g_object_get_data(G_OBJECT(object), "gradient_modified_connection")); + + if (gradient) { + g_assert( release_connection != nullptr ); + g_assert( modified_connection != nullptr ); + release_connection->disconnect(); + modified_connection->disconnect(); + sp_signal_disconnect_by_data(gradient, object); + + if (gradient->getRepr()) { + sp_repr_remove_listener_by_data(gradient->getRepr(), object); + } + } + + SelectedColor *selected_color = static_cast<SelectedColor *>(g_object_get_data(G_OBJECT(object), "cselector")); + if (selected_color) { + delete selected_color; + g_object_set_data(G_OBJECT(object), "cselector", nullptr); + } +} + +static void sp_gradient_vector_gradient_release(SPObject */*object*/, GtkWidget *widget) +{ + sp_gradient_vector_widget_load_gradient(widget, nullptr); +} + +static void sp_gradient_vector_gradient_modified(SPObject *object, guint /*flags*/, GtkWidget *widget) +{ + SPGradient *gradient=SP_GRADIENT(object); + if (!blocked) { + blocked = TRUE; + sp_gradient_vector_widget_load_gradient(widget, gradient); + blocked = FALSE; + } +} + +static void sp_gradient_vector_color_dragged(Inkscape::UI::SelectedColor *selected_color, GObject *object) +{ + SPGradient *gradient, *ngr; + + if (blocked) { + return; + } + + gradient = static_cast<SPGradient*>(g_object_get_data(G_OBJECT(object), "gradient")); + if (!gradient) { + return; + } + + blocked = TRUE; + + ngr = sp_gradient_ensure_vector_normalized(gradient); + if (ngr != gradient) { + /* Our master gradient has changed */ + sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr); + } + + ngr->ensureVector(); + + SPStop *stop = get_selected_stop(GTK_WIDGET(object)); + if (!stop) { + return; + } + + SPColor color = stop->getColor(); + gfloat opacity = stop->getOpacity(); + selected_color->colorAlpha(color, opacity); + stop->style->stop_color.currentcolor = false; + + blocked = FALSE; +} + +static void sp_gradient_vector_color_changed(Inkscape::UI::SelectedColor *selected_color, GObject *object) +{ + (void)selected_color; + + void* updating_color = g_object_get_data(G_OBJECT(object), "updating_color"); + if (updating_color) { + return; + } + + if (blocked) { + return; + } + + SPGradient *gradient = static_cast<SPGradient*>(g_object_get_data(G_OBJECT(object), "gradient")); + if (!gradient) { + return; + } + + blocked = TRUE; + + SPGradient *ngr = sp_gradient_ensure_vector_normalized(gradient); + if (ngr != gradient) { + /* Our master gradient has changed */ + sp_gradient_vector_widget_load_gradient(GTK_WIDGET(object), ngr); + } + + ngr->ensureVector(); + + /* Set start parameters */ + /* We rely on normalized vector, i.e. stops HAVE to exist */ + g_return_if_fail(ngr->getFirstStop() != nullptr); + + SPStop *stop = get_selected_stop(GTK_WIDGET(object)); + if (!stop) { + return; + } + + SelectedColor *csel = static_cast<SelectedColor *>(g_object_get_data(G_OBJECT(object), "cselector")); + SPColor color; + float alpha = 0; + csel->colorAlpha(color, alpha); + + sp_repr_set_css_double(stop->getRepr(), "offset", stop->offset); + Inkscape::CSSOStringStream os; + os << "stop-color:" << color.toString() << ";stop-opacity:" << static_cast<gdouble>(alpha) <<";"; + stop->setAttribute("style", os.str()); + // g_snprintf(c, 256, "stop-color:#%06x;stop-opacity:%g;", rgb >> 8, static_cast<gdouble>(alpha)); + //stop->setAttribute("style", c); + + DocumentUndo::done(ngr->document, SP_VERB_CONTEXT_GRADIENT, + _("Change gradient stop color")); + + blocked = FALSE; + + // Set the color in the selected stop after change + GtkWidget *combo_box = static_cast<GtkWidget *>(g_object_get_data(G_OBJECT(object), "combo_box")); + if (combo_box) { + GtkTreeIter iter; + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX(combo_box), &iter)) { + GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(combo_box))); + + Inkscape::UI::Widget::ColorPreview *cp = Gtk::manage(new Inkscape::UI::Widget::ColorPreview(stop->get_rgba32())); + GdkPixbuf *pb = cp->toPixbuf(64, 16); + + gtk_list_store_set (store, &iter, 0, pb, /*1, repr->attribute("id"),*/ 2, stop, -1); + } + } + +} + +/* + 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 : |