diff options
Diffstat (limited to 'src/ui/toolbar/gradient-toolbar.cpp')
-rw-r--r-- | src/ui/toolbar/gradient-toolbar.cpp | 1173 |
1 files changed, 1173 insertions, 0 deletions
diff --git a/src/ui/toolbar/gradient-toolbar.cpp b/src/ui/toolbar/gradient-toolbar.cpp new file mode 100644 index 0000000..d2faeb1 --- /dev/null +++ b/src/ui/toolbar/gradient-toolbar.cpp @@ -0,0 +1,1173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Gradient aux toolbar + * + * Authors: + * bulia byak <bulia@dr.com> + * Johan Engelen <j.b.c.engelen@ewi.utwente.nl> + * Abhishek Sharma + * + * Copyright (C) 2007 Johan Engelen + * Copyright (C) 2005 authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <glibmm/i18n.h> + +#include <gtkmm/comboboxtext.h> +#include <gtkmm/radiotoolbutton.h> +#include <gtkmm/separatortoolitem.h> + +#include "desktop.h" +#include "document-undo.h" +#include "document.h" +#include "gradient-chemistry.h" +#include "gradient-drag.h" +#include "gradient-toolbar.h" +#include "selection.h" + +#include "object/sp-defs.h" +#include "object/sp-linear-gradient.h" +#include "object/sp-radial-gradient.h" +#include "object/sp-stop.h" +#include "style.h" + +#include "ui/icon-names.h" +#include "ui/tools/gradient-tool.h" +#include "ui/util.h" +#include "ui/widget/canvas.h" +#include "ui/widget/color-preview.h" +#include "ui/widget/combo-tool-item.h" +#include "ui/widget/gradient-image.h" +#include "ui/widget/spin-button-tool-item.h" +#include "ui/widget/gradient-vector-selector.h" + +using Inkscape::DocumentUndo; +using Inkscape::UI::Tools::ToolBase; + +static bool blocked = false; + +void gr_apply_gradient_to_item( SPItem *item, SPGradient *gr, SPGradientType initialType, Inkscape::PaintTarget initialMode, Inkscape::PaintTarget mode ) +{ + SPStyle *style = item->style; + bool isFill = (mode == Inkscape::FOR_FILL); + if (style + && (isFill ? style->fill.isPaintserver() : style->stroke.isPaintserver()) + //&& SP_IS_GRADIENT(isFill ? style->getFillPaintServer() : style->getStrokePaintServer()) ) { + && (isFill ? SP_IS_GRADIENT(style->getFillPaintServer()) : SP_IS_GRADIENT(style->getStrokePaintServer())) ) { + SPPaintServer *server = isFill ? style->getFillPaintServer() : style->getStrokePaintServer(); + if ( SP_IS_LINEARGRADIENT(server) ) { + sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_LINEAR, mode); + } else if ( SP_IS_RADIALGRADIENT(server) ) { + sp_item_set_gradient(item, gr, SP_GRADIENT_TYPE_RADIAL, mode); + } + } + else if (initialMode == mode) + { + sp_item_set_gradient(item, gr, initialType, mode); + } +} + +/** +Applies gradient vector gr to the gradients attached to the selected dragger of drag, or if none, +to all objects in selection. If there was no previous gradient on an item, uses gradient type and +fill/stroke setting from preferences to create new default (linear: left/right; radial: centered) +gradient. +*/ +void gr_apply_gradient(Inkscape::Selection *selection, GrDrag *drag, SPGradient *gr) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + SPGradientType initialType = static_cast<SPGradientType>(prefs->getInt("/tools/gradient/newgradient", SP_GRADIENT_TYPE_LINEAR)); + Inkscape::PaintTarget initialMode = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE; + + // GRADIENTFIXME: make this work for multiple selected draggers. + + // First try selected dragger + if (drag && !drag->selected.empty()) { + GrDragger *dragger = *(drag->selected.begin()); + for(auto draggable : dragger->draggables) { //for all draggables of dragger + gr_apply_gradient_to_item(draggable->item, gr, initialType, initialMode, draggable->fill_or_stroke); + } + return; + } + + // If no drag or no dragger selected, act on selection + auto itemlist= selection->items(); + for(auto i=itemlist.begin();i!=itemlist.end();++i){ + gr_apply_gradient_to_item(*i, gr, initialType, initialMode, initialMode); + } +} + +int gr_vector_list(Glib::RefPtr<Gtk::ListStore> store, SPDesktop *desktop, + bool selection_empty, SPGradient *gr_selected, bool gr_multi) +{ + int selected = -1; + + if (!blocked) { + std::cerr << "gr_vector_list: should be blocked!" << std::endl; + } + + // Get list of gradients in document. + SPDocument *document = desktop->getDocument(); + std::vector<SPObject *> gl; + std::vector<SPObject *> gradients = document->getResourceList( "gradient" ); + for (auto gradient : gradients) { + SPGradient *grad = SP_GRADIENT(gradient); + if ( grad->hasStops() && !grad->isSolid() ) { + gl.push_back(gradient); + } + } + + store->clear(); + + Inkscape::UI::Widget::ComboToolItemColumns columns; + Gtk::TreeModel::Row row; + + if (gl.empty()) { + // The document has no gradients + + row = *(store->append()); + row[columns.col_label ] = _("No gradient"); + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_data ] = nullptr; + row[columns.col_sensitive] = true; + + } else if (selection_empty) { + // Document has gradients, but nothing is currently selected. + + row = *(store->append()); + row[columns.col_label ] = _("Nothing Selected"); + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_data ] = nullptr; + row[columns.col_sensitive] = true; + + } else { + + if (gr_selected == nullptr) { + row = *(store->append()); + row[columns.col_label ] = _("No gradient"); + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_data ] = nullptr; + row[columns.col_sensitive] = true; + } + + if (gr_multi) { + row = *(store->append()); + row[columns.col_label ] = _("Multiple gradients"); + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_data ] = nullptr; + row[columns.col_sensitive] = true; + } + + int idx = 0; + for (auto it : gl) { + SPGradient *gradient = SP_GRADIENT(it); + + Glib::ustring label = gr_prepare_label(gradient); + Glib::RefPtr<Gdk::Pixbuf> pixbuf = sp_gradient_to_pixbuf_ref(gradient, 64, 16); + + row = *(store->append()); + row[columns.col_label ] = label; + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_pixbuf ] = pixbuf; + row[columns.col_data ] = gradient; + row[columns.col_sensitive] = true; + + if (gradient == gr_selected) { + selected = idx; + } + idx ++; + } + + if (gr_multi) { + selected = 0; // This will show "Multiple Gradients" + } + } + + return selected; +} + +/* + * Get the gradient of the selected desktop item + * This is gradient containing the repeat settings, not the underlying "getVector" href linked gradient. + */ +void gr_get_dt_selected_gradient(Inkscape::Selection *selection, SPGradient *&gr_selected) +{ + SPGradient *gradient = nullptr; + + auto itemlist= selection->items(); + for(auto i=itemlist.begin();i!=itemlist.end();++i){ + SPItem *item = *i;// get the items gradient, not the getVector() version + SPStyle *style = item->style; + SPPaintServer *server = nullptr; + + if (style && (style->fill.isPaintserver())) { + server = item->style->getFillPaintServer(); + } + if (style && (style->stroke.isPaintserver())) { + server = item->style->getStrokePaintServer(); + } + + if ( SP_IS_GRADIENT(server) ) { + gradient = SP_GRADIENT(server); + } + } + + if (gradient && gradient->isSolid()) { + gradient = nullptr; + } + + if (gradient) { + gr_selected = gradient; + } +} + +/* + * Get the current selection and dragger status from the desktop + */ +void gr_read_selection( Inkscape::Selection *selection, + GrDrag *drag, + SPGradient *&gr_selected, + bool &gr_multi, + SPGradientSpread &spr_selected, + bool &spr_multi ) +{ + if (drag && !drag->selected.empty()) { + // GRADIENTFIXME: make this work for more than one selected dragger? + GrDragger *dragger = *(drag->selected.begin()); + for(auto draggable : dragger->draggables) { //for all draggables of dragger + SPGradient *gradient = sp_item_gradient_get_vector(draggable->item, draggable->fill_or_stroke); + SPGradientSpread spread = sp_item_gradient_get_spread(draggable->item, draggable->fill_or_stroke); + + if (gradient && gradient->isSolid()) { + gradient = nullptr; + } + + if (gradient && (gradient != gr_selected)) { + if (gr_selected) { + gr_multi = true; + } else { + gr_selected = gradient; + } + } + if (spread != spr_selected) { + if (spr_selected != SP_GRADIENT_SPREAD_UNDEFINED) { + spr_multi = true; + } else { + spr_selected = spread; + } + } + } + return; + } + + // If no selected dragger, read desktop selection + auto itemlist= selection->items(); + for(auto i=itemlist.begin();i!=itemlist.end();++i){ + SPItem *item = *i; + SPStyle *style = item->style; + + if (style && (style->fill.isPaintserver())) { + SPPaintServer *server = item->style->getFillPaintServer(); + if ( SP_IS_GRADIENT(server) ) { + SPGradient *gradient = SP_GRADIENT(server)->getVector(); + SPGradientSpread spread = SP_GRADIENT(server)->fetchSpread(); + + if (gradient && gradient->isSolid()) { + gradient = nullptr; + } + + if (gradient && (gradient != gr_selected)) { + if (gr_selected) { + gr_multi = true; + } else { + gr_selected = gradient; + } + } + if (spread != spr_selected) { + if (spr_selected != SP_GRADIENT_SPREAD_UNDEFINED) { + spr_multi = true; + } else { + spr_selected = spread; + } + } + } + } + if (style && (style->stroke.isPaintserver())) { + SPPaintServer *server = item->style->getStrokePaintServer(); + if ( SP_IS_GRADIENT(server) ) { + SPGradient *gradient = SP_GRADIENT(server)->getVector(); + SPGradientSpread spread = SP_GRADIENT(server)->fetchSpread(); + + if (gradient && gradient->isSolid()) { + gradient = nullptr; + } + + if (gradient && (gradient != gr_selected)) { + if (gr_selected) { + gr_multi = true; + } else { + gr_selected = gradient; + } + } + if (spread != spr_selected) { + if (spr_selected != SP_GRADIENT_SPREAD_UNDEFINED) { + spr_multi = true; + } else { + spr_selected = spread; + } + } + } + } + } + } + +namespace Inkscape { +namespace UI { +namespace Toolbar { +GradientToolbar::GradientToolbar(SPDesktop *desktop) + : Toolbar(desktop) +{ + auto prefs = Inkscape::Preferences::get(); + + /* New gradient linear or radial */ + { + add_label(_("New:")); + + Gtk::RadioToolButton::Group new_type_group; + + auto linear_button = Gtk::manage(new Gtk::RadioToolButton(new_type_group, _("linear"))); + linear_button->set_tooltip_text(_("Create linear gradient")); + linear_button->set_icon_name(INKSCAPE_ICON("paint-gradient-linear")); + _new_type_buttons.push_back(linear_button); + + auto radial_button = Gtk::manage(new Gtk::RadioToolButton(new_type_group, _("radial"))); + radial_button->set_tooltip_text(_("Create radial (elliptic or circular) gradient")); + radial_button->set_icon_name(INKSCAPE_ICON("paint-gradient-radial")); + _new_type_buttons.push_back(radial_button); + + gint mode = prefs->getInt("/tools/gradient/newgradient", SP_GRADIENT_TYPE_LINEAR); + _new_type_buttons[ mode == SP_GRADIENT_TYPE_LINEAR ? 0 : 1 ]->set_active(); // linear == 1, radial == 2 + + int btn_index = 0; + for (auto btn : _new_type_buttons) + { + btn->set_sensitive(true); + btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &GradientToolbar::new_type_changed), btn_index++)); + add(*btn); + } + } + + /* New gradient on fill or stroke*/ + { + Gtk::RadioToolButton::Group new_fillstroke_group; + + auto fill_btn = Gtk::manage(new Gtk::RadioToolButton(new_fillstroke_group, _("fill"))); + fill_btn->set_tooltip_text(_("Create gradient in the fill")); + fill_btn->set_icon_name(INKSCAPE_ICON("object-fill")); + _new_fillstroke_buttons.push_back(fill_btn); + + auto stroke_btn = Gtk::manage(new Gtk::RadioToolButton(new_fillstroke_group, _("stroke"))); + stroke_btn->set_tooltip_text(_("Create gradient in the stroke")); + stroke_btn->set_icon_name(INKSCAPE_ICON("object-stroke")); + _new_fillstroke_buttons.push_back(stroke_btn); + + auto fsmode = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE; + _new_fillstroke_buttons[ fsmode == Inkscape::FOR_FILL ? 0 : 1 ]->set_active(); + + auto btn_index = 0; + for (auto btn : _new_fillstroke_buttons) + { + btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &GradientToolbar::new_fillstroke_changed), btn_index++)); + btn->set_sensitive(); + add(*btn); + } + } + + add(* Gtk::manage(new Gtk::SeparatorToolItem())); + + /* Gradient Select list*/ + { + UI::Widget::ComboToolItemColumns columns; + + auto store = Gtk::ListStore::create(columns); + + Gtk::TreeModel::Row row; + + row = *(store->append()); + row[columns.col_label ] = _("No gradient"); + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_sensitive] = true; + + _select_cb = UI::Widget::ComboToolItem::create(_("Select"), // Label + "", // Tooltip + "Not Used", // Icon + store ); // Tree store + + _select_cb->use_icon( false ); + _select_cb->use_pixbuf( true ); + _select_cb->use_group_label( true ); + _select_cb->set_active( 0 ); + _select_cb->set_sensitive( false ); + + add(*_select_cb); + _select_cb->signal_changed().connect(sigc::mem_fun(*this, &GradientToolbar::gradient_changed)); + } + + // Gradients Linked toggle + { + _linked_item = add_toggle_button(_("Link gradients"), + _("Link gradients to change all related gradients")); + _linked_item->set_icon_name(INKSCAPE_ICON("object-unlocked")); + _linked_item->signal_toggled().connect(sigc::mem_fun(*this, &GradientToolbar::linked_changed)); + + bool linkedmode = prefs->getBool("/options/forkgradientvectors/value", true); + _linked_item->set_active(!linkedmode); + } + + /* Reverse */ + { + _stops_reverse_item = Gtk::manage(new Gtk::ToolButton(_("Reverse"))); + _stops_reverse_item->set_tooltip_text(_("Reverse the direction of the gradient")); + _stops_reverse_item->set_icon_name(INKSCAPE_ICON("object-flip-horizontal")); + _stops_reverse_item->signal_clicked().connect(sigc::mem_fun(*this, &GradientToolbar::reverse)); + add(*_stops_reverse_item); + _stops_reverse_item->set_sensitive(false); + } + + // Gradient Spread type (how a gradient is drawn outside its nominal area) + { + UI::Widget::ComboToolItemColumns columns; + Glib::RefPtr<Gtk::ListStore> store = Gtk::ListStore::create(columns); + + std::vector<gchar*> spread_dropdown_items_list = { + const_cast<gchar *>(C_("Gradient repeat type", "None")), + _("Reflected"), + _("Direct") + }; + + for (auto item: spread_dropdown_items_list) { + Gtk::TreeModel::Row row = *(store->append()); + row[columns.col_label ] = item; + row[columns.col_sensitive] = true; + } + + _spread_cb = Gtk::manage(UI::Widget::ComboToolItem::create(_("Repeat"), + // TRANSLATORS: for info, see http://www.w3.org/TR/2000/CR-SVG-20000802/pservers.html#LinearGradientSpreadMethodAttribute + _("Whether to fill with flat color beyond the ends of the gradient vector " + "(spreadMethod=\"pad\"), or repeat the gradient in the same direction " + "(spreadMethod=\"repeat\"), or repeat the gradient in alternating opposite " + "directions (spreadMethod=\"reflect\")"), + "Not Used", store)); + _spread_cb->use_group_label(true); + + _spread_cb->set_active(0); + _spread_cb->set_sensitive(false); + + _spread_cb->signal_changed().connect(sigc::mem_fun(*this, &GradientToolbar::spread_changed)); + add(*_spread_cb); + } + + add(* Gtk::manage(new Gtk::SeparatorToolItem())); + + /* Gradient Stop list */ + { + UI::Widget::ComboToolItemColumns columns; + + auto store = Gtk::ListStore::create(columns); + + Gtk::TreeModel::Row row; + + row = *(store->append()); + row[columns.col_label ] = _("No stops"); + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_sensitive] = true; + + _stop_cb = + UI::Widget::ComboToolItem::create(_("Stops" ), // Label + "", // Tooltip + "Not Used", // Icon + store ); // Tree store + + _stop_cb->use_icon( false ); + _stop_cb->use_pixbuf( true ); + _stop_cb->use_group_label( true ); + _stop_cb->set_active( 0 ); + _stop_cb->set_sensitive( false ); + + add(*_stop_cb); + _stop_cb->signal_changed().connect(sigc::mem_fun(*this, &GradientToolbar::stop_changed)); + } + + /* Offset */ + { + auto offset_val = prefs->getDouble("/tools/gradient/stopoffset", 0); + _offset_adj = Gtk::Adjustment::create(offset_val, 0.0, 1.0, 0.01, 0.1); + _offset_item = Gtk::manage(new UI::Widget::SpinButtonToolItem("gradient-stopoffset", C_("Gradient", "Offset:"), _offset_adj, 0.01, 2)); + _offset_item->set_tooltip_text(_("Offset of selected stop")); + _offset_item->set_focus_widget(desktop->canvas); + _offset_adj->signal_value_changed().connect(sigc::mem_fun(*this, &GradientToolbar::stop_offset_adjustment_changed)); + add(*_offset_item); + _offset_item->set_sensitive(false); + } + + /* Add stop */ + { + _stops_add_item = Gtk::manage(new Gtk::ToolButton(_("Insert new stop"))); + _stops_add_item->set_tooltip_text(_("Insert new stop")); + _stops_add_item->set_icon_name(INKSCAPE_ICON("node-add")); + _stops_add_item->signal_clicked().connect(sigc::mem_fun(*this, &GradientToolbar::add_stop)); + add(*_stops_add_item); + _stops_add_item->set_sensitive(false); + } + + /* Delete stop */ + { + _stops_delete_item = Gtk::manage(new Gtk::ToolButton(_("Delete stop"))); + _stops_delete_item->set_tooltip_text(_("Delete stop")); + _stops_delete_item->set_icon_name(INKSCAPE_ICON("node-delete")); + _stops_delete_item->signal_clicked().connect(sigc::mem_fun(*this, &GradientToolbar::remove_stop)); + add(*_stops_delete_item); + _stops_delete_item->set_sensitive(false); + } + + desktop->connectEventContextChanged(sigc::mem_fun(*this, &GradientToolbar::check_ec)); + + show_all(); +} + +/** + * Gradient auxiliary toolbar construction and setup. + * + */ +GtkWidget * +GradientToolbar::create(SPDesktop * desktop) +{ + auto toolbar = new GradientToolbar(desktop); + return GTK_WIDGET(toolbar->gobj()); +} + +void +GradientToolbar::new_type_changed(int mode) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setInt("/tools/gradient/newgradient", + mode == 0 ? SP_GRADIENT_TYPE_LINEAR : SP_GRADIENT_TYPE_RADIAL); +} + +void +GradientToolbar::new_fillstroke_changed(int mode) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + Inkscape::PaintTarget fsmode = (mode == 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE; + prefs->setInt("/tools/gradient/newfillorstroke", (fsmode == Inkscape::FOR_FILL) ? 1 : 0); +} + +/* + * User selected a gradient from the combobox + */ +void +GradientToolbar::gradient_changed(int active) +{ + if (blocked) { + return; + } + + if (active < 0) { + return; + } + + blocked = true; + + SPGradient *gr = get_selected_gradient(); + + if (gr) { + gr = sp_gradient_ensure_vector_normalized(gr); + + Inkscape::Selection *selection = _desktop->getSelection(); + ToolBase *ev = _desktop->getEventContext(); + + gr_apply_gradient(selection, ev ? ev->get_drag() : nullptr, gr); + + DocumentUndo::done(_desktop->getDocument(), _("Assign gradient to object"), INKSCAPE_ICON("color-gradient")); + } + + blocked = false; +} + +/** + * \brief Return gradient selected in menu + */ +SPGradient * +GradientToolbar::get_selected_gradient() +{ + int active = _select_cb->get_active(); + + auto store = _select_cb->get_store(); + auto row = store->children()[active]; + UI::Widget::ComboToolItemColumns columns; + + void* pointer = row[columns.col_data]; + SPGradient *gr = static_cast<SPGradient *>(pointer); + + return gr; +} + +/** + * \brief User selected a spread method from the combobox + */ +void +GradientToolbar::spread_changed(int active) +{ + if (blocked) { + return; + } + + blocked = true; + + Inkscape::Selection *selection = _desktop->getSelection(); + SPGradient *gradient = nullptr; + gr_get_dt_selected_gradient(selection, gradient); + + if (gradient) { + SPGradientSpread spread = (SPGradientSpread) active; + gradient->setSpread(spread); + gradient->updateRepr(); + + DocumentUndo::done(_desktop->getDocument(), _("Set gradient repeat"), INKSCAPE_ICON("color-gradient")); + } + + blocked = false; +} + +/** + * \brief User selected a stop from the combobox + */ +void +GradientToolbar::stop_changed(int active) +{ + if (blocked) { + return; + } + + blocked = true; + + ToolBase *ev = _desktop->getEventContext(); + SPGradient *gr = get_selected_gradient(); + + select_dragger_by_stop(gr, ev); + + blocked = false; +} + +void +GradientToolbar::select_dragger_by_stop(SPGradient *gradient, + ToolBase *ev) +{ + if (!blocked) { + std::cerr << "select_dragger_by_stop: should be blocked!" << std::endl; + } + + if (!ev || !gradient) { + return; + } + + GrDrag *drag = ev->get_drag(); + if (!drag) { + return; + } + + SPStop *stop = get_selected_stop(); + + drag->selectByStop(stop, false, true); + + stop_set_offset(); +} + +/** + * \brief Get stop selected by menu + */ +SPStop * +GradientToolbar::get_selected_stop() +{ + int active = _stop_cb->get_active(); + + auto store = _stop_cb->get_store(); + auto row = store->children()[active]; + UI::Widget::ComboToolItemColumns columns; + void* pointer = row[columns.col_data]; + SPStop *stop = static_cast<SPStop *>(pointer); + + return stop; +} + +/** + * Change desktop dragger selection to this stop + * + * Set the offset widget value (based on which stop is selected) + */ +void +GradientToolbar::stop_set_offset() +{ + if (!blocked) { + std::cerr << "gr_stop_set_offset: should be blocked!" << std::endl; + } + + SPStop *stop = get_selected_stop(); + if (!stop) { + // std::cerr << "gr_stop_set_offset: no stop!" << std::endl; + return; + } + + if (!_offset_item) { + return; + } + bool isEndStop = false; + + SPStop *prev = nullptr; + prev = stop->getPrevStop(); + if (prev != nullptr ) { + _offset_adj->set_lower(prev->offset); + } else { + isEndStop = true; + _offset_adj->set_lower(0); + } + + SPStop *next = nullptr; + next = stop->getNextStop(); + if (next != nullptr ) { + _offset_adj->set_upper(next->offset); + } else { + isEndStop = true; + _offset_adj->set_upper(1.0); + } + + _offset_adj->set_value(stop->offset); + _offset_item->set_sensitive( !isEndStop ); +} + +/** + * \brief User changed the offset + */ +void +GradientToolbar::stop_offset_adjustment_changed() +{ + if (blocked) { + return; + } + + blocked = true; + + SPStop *stop = get_selected_stop(); + if (stop) { + stop->offset = _offset_adj->get_value(); + stop->getRepr()->setAttributeCssDouble("offset", stop->offset); + + DocumentUndo::maybeDone(stop->document, "gradient:stop:offset", _("Change gradient stop offset"), INKSCAPE_ICON("color-gradient")); + } + + blocked = false; +} + +/** + * \brief Add stop to gradient + */ +void +GradientToolbar::add_stop() +{ + if (!_desktop) { + return; + } + + auto selection = _desktop->getSelection(); + if (!selection) { + return; + } + + auto ev = _desktop->getEventContext(); + if (auto rc = SP_GRADIENT_CONTEXT(ev)) { + rc->add_stops_between_selected_stops(); + } +} + +/** + * \brief Remove stop from vector + */ +void +GradientToolbar::remove_stop() +{ + if (!_desktop) { + return; + } + + auto selection = _desktop->getSelection(); // take from desktop, not from args + if (!selection) { + return; + } + + auto ev = _desktop->getEventContext(); + GrDrag *drag = nullptr; + if (ev) { + drag = ev->get_drag(); + } + + if (drag) { + drag->deleteSelected(); + } +} + +/** + * \brief Reverse vector + */ +void +GradientToolbar::reverse() +{ + sp_gradient_reverse_selected_gradients(_desktop); +} + +/** + * \brief Lock or unlock links + */ +void +GradientToolbar::linked_changed() +{ + bool active = _linked_item->get_active(); + if ( active ) { + _linked_item->set_icon_name(INKSCAPE_ICON("object-locked")); + } else { + _linked_item->set_icon_name(INKSCAPE_ICON("object-unlocked")); + } + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/options/forkgradientvectors/value", !active); +} + +// lp:1327267 +/** + * Checks the current tool and connects gradient aux toolbox signals if it happens to be the gradient tool. + * Called every time the current tool changes by signal emission. + */ +void +GradientToolbar::check_ec(SPDesktop* desktop, Inkscape::UI::Tools::ToolBase* ec) +{ + if (SP_IS_GRADIENT_CONTEXT(ec)) { + Inkscape::Selection *selection = desktop->getSelection(); + SPDocument *document = desktop->getDocument(); + + // connect to selection modified and changed signals + _connection_changed = selection->connectChanged(sigc::mem_fun(*this, &GradientToolbar::selection_changed)); + _connection_modified = selection->connectModified(sigc::mem_fun(*this, &GradientToolbar::selection_modified)); + _connection_subselection_changed = desktop->connect_gradient_stop_selected([=](void* sender, SPStop* stop){ + drag_selection_changed(nullptr); + }); + + // Is this necessary? Couldn't hurt. + selection_changed(selection); + + // connect to release and modified signals of the defs (i.e. when someone changes gradient) + _connection_defs_release = document->getDefs()->connectRelease(sigc::mem_fun(*this, &GradientToolbar::defs_release)); + _connection_defs_modified = document->getDefs()->connectModified(sigc::mem_fun(*this, &GradientToolbar::defs_modified)); + } else { + if (_connection_changed) + _connection_changed.disconnect(); + if (_connection_modified) + _connection_modified.disconnect(); + if (_connection_subselection_changed) + _connection_subselection_changed.disconnect(); + if (_connection_defs_release) + _connection_defs_release.disconnect(); + if (_connection_defs_modified) + _connection_defs_modified.disconnect(); + } +} + +/** + * Core function, setup all the widgets whenever something changes on the desktop + */ +void +GradientToolbar::selection_changed(Inkscape::Selection * /*selection*/) +{ + if (blocked) + return; + + blocked = true; + + if (!_desktop) { + return; + } + + Inkscape::Selection *selection = _desktop->getSelection(); // take from desktop, not from args + if (selection) { + + ToolBase *ev = _desktop->getEventContext(); + GrDrag *drag = nullptr; + if (ev) { + drag = ev->get_drag(); + } + + SPGradient *gr_selected = nullptr; + SPGradientSpread spr_selected = SP_GRADIENT_SPREAD_UNDEFINED; + bool gr_multi = false; + bool spr_multi = false; + + gr_read_selection(selection, drag, gr_selected, gr_multi, spr_selected, spr_multi); + + // Gradient selection menu + auto store = _select_cb->get_store(); + int gradient = gr_vector_list (store, _desktop, selection->isEmpty(), gr_selected, gr_multi); + + if (gradient < 0) { + // No selection or no gradients + _select_cb->set_active( 0 ); + _select_cb->set_sensitive (false); + } else { + // Single gradient or multiple gradients + _select_cb->set_active( gradient ); + _select_cb->set_sensitive (true); + } + + // Spread menu + _spread_cb->set_sensitive( gr_selected && !gr_multi ); + _spread_cb->set_active( gr_selected ? (int)spr_selected : 0 ); + + _stops_add_item->set_sensitive((gr_selected && !gr_multi && drag && !drag->selected.empty())); + _stops_delete_item->set_sensitive((gr_selected && !gr_multi && drag && !drag->selected.empty())); + _stops_reverse_item->set_sensitive((gr_selected!= nullptr)); + + _stop_cb->set_sensitive( gr_selected && !gr_multi); + + update_stop_list (gr_selected, nullptr, gr_multi); + select_stop_by_draggers(gr_selected, ev); + } + + blocked = false; +} + +/** + * \brief Construct stop list + */ +int +GradientToolbar::update_stop_list( SPGradient *gradient, SPStop *new_stop, bool gr_multi) +{ + if (!blocked) { + std::cerr << "update_stop_list should be blocked!" << std::endl; + } + + int selected = -1; + + auto store = _stop_cb->get_store(); + + if (!store) { + return selected; + } + + store->clear(); + + UI::Widget::ComboToolItemColumns columns; + Gtk::TreeModel::Row row; + + if (!SP_IS_GRADIENT(gradient)) { + // No valid gradient + + row = *(store->append()); + row[columns.col_label ] = _("No gradient"); + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_data ] = nullptr; + row[columns.col_sensitive] = true; + + } else if (!gradient->hasStops()) { + // Has gradient but it has no stops + + row = *(store->append()); + row[columns.col_label ] = _("No stops in gradient"); + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_data ] = nullptr; + row[columns.col_sensitive] = true; + + } else { + // Gradient has stops + + // Get list of stops + for (auto& ochild: gradient->children) { + if (SP_IS_STOP(&ochild)) { + + SPStop *stop = SP_STOP(&ochild); + Glib::RefPtr<Gdk::Pixbuf> pixbuf = sp_gradstop_to_pixbuf_ref (stop, 32, 16); + + Inkscape::XML::Node *repr = reinterpret_cast<SPItem *>(&ochild)->getRepr(); + Glib::ustring label = gr_ellipsize_text(repr->attribute("id"), 25); + + row = *(store->append()); + row[columns.col_label ] = label; + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_pixbuf ] = pixbuf; + row[columns.col_data ] = stop; + row[columns.col_sensitive] = true; + } + } + } + + if (new_stop != nullptr) { + selected = select_stop_in_list (gradient, new_stop); + } + + return selected; +} + +/** + * \brief Find position of new_stop in menu. + */ +int +GradientToolbar::select_stop_in_list(SPGradient *gradient, SPStop *new_stop) +{ + int i = 0; + for (auto& ochild: gradient->children) { + if (SP_IS_STOP(&ochild)) { + if (&ochild == new_stop) { + return i; + } + i++; + } + } + return -1; +} + +/** + * \brief Set stop in menu to match stops selected by draggers + */ +void +GradientToolbar::select_stop_by_draggers(SPGradient *gradient, ToolBase *ev) +{ + if (!blocked) { + std::cerr << "select_stop_by_draggers should be blocked!" << std::endl; + } + + if (!ev || !gradient) + return; + + SPGradient *vector = gradient->getVector(); + if (!vector) + return; + + GrDrag *drag = ev->get_drag(); + + if (!drag || drag->selected.empty()) { + _stop_cb->set_active(0); + stop_set_offset(); + return; + } + + gint n = 0; + SPStop *stop = nullptr; + int selected = -1; + + // For all selected draggers + for(auto dragger : drag->selected) { + + // For all draggables of dragger + for(auto draggable : dragger->draggables) { + + if (draggable->point_type != POINT_RG_FOCUS) { + n++; + if (n > 1) break; + } + + stop = vector->getFirstStop(); + + switch (draggable->point_type) { + case POINT_LG_MID: + case POINT_RG_MID1: + case POINT_RG_MID2: + stop = sp_get_stop_i(vector, draggable->point_i); + break; + case POINT_LG_END: + case POINT_RG_R1: + case POINT_RG_R2: + stop = sp_last_stop(vector); + break; + default: + break; + } + } + if (n > 1) break; + } + + if (n > 1) { + // Multiple stops selected + if (_offset_item) { + _offset_item->set_sensitive(false); + } + + // Stop list always updated first... reinsert "Multiple stops" as first entry. + UI::Widget::ComboToolItemColumns columns; + auto store = _stop_cb->get_store(); + + auto row = *(store->prepend()); + row[columns.col_label ] = _("Multiple stops"); + row[columns.col_tooltip ] = ""; + row[columns.col_icon ] = "NotUsed"; + row[columns.col_sensitive] = true; + selected = 0; + + } else { + selected = select_stop_in_list(gradient, stop); + } + + if (selected < 0) { + _stop_cb->set_active (0); + _stop_cb->set_sensitive (false); + } else { + _stop_cb->set_active (selected); + _stop_cb->set_sensitive (true); + stop_set_offset(); + } +} + +void +GradientToolbar::selection_modified(Inkscape::Selection *selection, guint /*flags*/) +{ + selection_changed(selection); +} + +void +GradientToolbar::drag_selection_changed(gpointer /*dragger*/) +{ + selection_changed(nullptr); +} + +void +GradientToolbar::defs_release(SPObject * /*defs*/) +{ + selection_changed(nullptr); +} + +void +GradientToolbar::defs_modified(SPObject * /*defs*/, guint /*flags*/) +{ + selection_changed(nullptr); +} + +} +} +} +/* + 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 : |