summaryrefslogtreecommitdiffstats
path: root/src/ui/toolbar/gradient-toolbar.cpp
diff options
context:
space:
mode:
authorAlexander Valavanis <valavanisalex@gmail.com>2018-06-17 16:00:04 +0000
committerAlexander Valavanis <valavanisalex@gmail.com>2018-06-17 16:00:04 +0000
commit039408dbad2877c3b75d08f66dddaff17fe3dcf3 (patch)
tree5f6072daaa0f4a406b3b0e2f04f81f3c4140f51e /src/ui/toolbar/gradient-toolbar.cpp
parentMinor documentation changes. (diff)
downloadinkscape-039408dbad2877c3b75d08f66dddaff17fe3dcf3.tar.gz
inkscape-039408dbad2877c3b75d08f66dddaff17fe3dcf3.zip
Move toolbars to ui/toolbar
Diffstat (limited to 'src/ui/toolbar/gradient-toolbar.cpp')
-rw-r--r--src/ui/toolbar/gradient-toolbar.cpp1274
1 files changed, 1274 insertions, 0 deletions
diff --git a/src/ui/toolbar/gradient-toolbar.cpp b/src/ui/toolbar/gradient-toolbar.cpp
new file mode 100644
index 000000000..38724aca4
--- /dev/null
+++ b/src/ui/toolbar/gradient-toolbar.cpp
@@ -0,0 +1,1274 @@
+/*
+ * 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, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glibmm/i18n.h>
+
+
+#include "desktop.h"
+#include "document-undo.h"
+#include "document.h"
+#include "gradient-chemistry.h"
+#include "gradient-drag.h"
+#include "gradient-toolbar.h"
+#include "widgets/ink-action.h"
+#include "widgets/ink-toggle-action.h"
+#include "widgets/toolbox.h"
+#include "macros.h"
+#include "selection.h"
+#include "verbs.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/color-preview.h"
+#include "ui/widget/ink-select-one-action.h"
+
+#include "widgets/ege-adjustment-action.h"
+#include "widgets/gradient-image.h"
+#include "widgets/gradient-vector.h"
+
+using Inkscape::DocumentUndo;
+using Inkscape::UI::ToolboxFactory;
+using Inkscape::UI::Tools::ToolBase;
+
+static bool blocked = false;
+
+//########################
+//## Gradient ##
+//########################
+
+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(std::vector<GrDraggable *>::const_iterator i = dragger->draggables.begin(); i != dragger->draggables.end(); ++i) { //for all draggables of dragger
+ GrDraggable *draggable = *i;
+ 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 (std::vector<SPObject *>::const_iterator it = gradients.begin(); it != gradients.end(); ++it) {
+ SPGradient *grad = SP_GRADIENT(*it);
+ if ( grad->hasStops() && !grad->isSolid() ) {
+ gl.push_back(*it);
+ }
+ }
+
+ store->clear();
+
+ InkSelectOneActionColumns 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 == NULL) {
+ 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 (std::vector<SPObject *>::const_iterator it = gl.begin(); it != gl.end(); ++it) {
+ 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 = 0;
+
+ 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 = 0;
+
+ 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 = 0;
+ }
+
+ 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(std::vector<GrDraggable *>::const_iterator i = dragger->draggables.begin(); i != dragger->draggables.end(); ++i) { //for all draggables of dragger
+ GrDraggable *draggable = *i;
+ 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 = 0;
+ }
+
+ 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 = 0;
+ }
+
+ 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 = 0;
+ }
+
+ 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;
+ }
+ }
+ }
+ }
+ }
+ }
+
+/* Get stop selected by menu */
+static SPStop *get_selected_stop( GObject *data)
+{
+ InkSelectOneAction *act =
+ static_cast<InkSelectOneAction *>(g_object_get_data(data, "gradient_stop_action"));
+
+ int active = act->get_active();
+
+ Glib::RefPtr<Gtk::ListStore> store = act->get_store();
+ Gtk::TreeModel::Row row = store->children()[active];
+ InkSelectOneActionColumns columns;
+ void* pointer = row[columns.col_data];
+ SPStop *stop = static_cast<SPStop *>(pointer);
+
+ return stop;
+}
+
+/* Add stop to gradient */
+static void gr_add_stop(GtkWidget * /*button*/, GObject *data)
+{
+ SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(data, "desktop"));
+ if (!desktop) {
+ return;
+ }
+
+ Inkscape::Selection *selection = desktop->getSelection();
+ if (!selection) {
+ return;
+ }
+
+ ToolBase *ev = desktop->getEventContext();
+ Inkscape::UI::Tools::GradientTool *rc = SP_GRADIENT_CONTEXT(ev);
+
+ if (rc) {
+ sp_gradient_context_add_stops_between_selected_stops(rc);
+ }
+
+}
+
+/* Remove stop from vector */
+static void gr_remove_stop(GtkWidget * /*button*/, GObject *data)
+{
+
+ SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(data, "desktop"));
+ if (!desktop) {
+ return;
+ }
+
+ Inkscape::Selection *selection = desktop->getSelection(); // take from desktop, not from args
+ if (!selection) {
+ return;
+ }
+
+ ToolBase *ev = desktop->getEventContext();
+ GrDrag *drag = NULL;
+ if (ev) {
+ drag = ev->get_drag();
+ }
+
+ if (drag) {
+ drag->deleteSelected();
+ }
+
+}
+
+/* Lock or unlock links */
+static void gr_linked_changed(GtkToggleAction *act, gpointer /*data*/)
+{
+ gboolean active = gtk_toggle_action_get_active( act );
+ if ( active ) {
+ g_object_set( G_OBJECT(act), "iconId", INKSCAPE_ICON("object-locked"), NULL );
+ } else {
+ g_object_set( G_OBJECT(act), "iconId", INKSCAPE_ICON("object-unlocked"), NULL );
+ }
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setBool("/options/forkgradientvectors/value", !active);
+}
+
+/* Reverse vector */
+static void gr_reverse(GtkWidget * /*button*/, gpointer data)
+{
+ SPDesktop *desktop = static_cast<SPDesktop *>(data);
+ sp_gradient_reverse_selected_gradients(desktop);
+}
+
+/*
+ * Change desktop dragger selection to this stop
+ */
+/* Set the offset widget value (based on which stop is selected). */
+static void gr_stop_set_offset (GObject *data)
+{
+ if (!blocked) {
+ std::cerr << "gr_stop_set_offset: should be blocked!" << std::endl;
+ }
+
+ SPStop *stop = get_selected_stop(data);
+ if (!stop) {
+ // std::cerr << "gr_stop_set_offset: no stop!" << std::endl;
+ return;
+ }
+
+ EgeAdjustmentAction* act = (EgeAdjustmentAction *)g_object_get_data( data, "offset_action");
+ if (!act) {
+ return;
+ }
+ GtkAdjustment *adj = ege_adjustment_action_get_adjustment(act);
+
+ bool isEndStop = false;
+
+ SPStop *prev = NULL;
+ prev = stop->getPrevStop();
+ if (prev != NULL ) {
+ gtk_adjustment_set_lower(adj, prev->offset);
+ } else {
+ isEndStop = true;
+ gtk_adjustment_set_lower(adj, 0);
+ }
+
+ SPStop *next = NULL;
+ next = stop->getNextStop();
+ if (next != NULL ) {
+ gtk_adjustment_set_upper(adj, next->offset);
+ } else {
+ isEndStop = true;
+ gtk_adjustment_set_upper(adj, 1.0);
+ }
+
+ gtk_adjustment_set_value(adj, stop->offset);
+ gtk_action_set_sensitive( GTK_ACTION(act), !isEndStop );
+ gtk_adjustment_changed(adj);
+}
+
+static void select_dragger_by_stop( GObject *data, 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(data);
+
+ drag->selectByStop(stop, false, true);
+
+ gr_stop_set_offset(data);
+}
+
+/* Find position of new_stop in menu. */
+static int select_stop_in_list (SPGradient *gradient, SPStop *new_stop, GObject *data)
+{
+ int i = 0;
+ for (auto& ochild: gradient->children) {
+ if (SP_IS_STOP(&ochild)) {
+ if (&ochild == new_stop) {
+ return i;
+ }
+ i++;
+ }
+ }
+ return -1;
+}
+
+/* Construct stop list */
+static int update_stop_list( SPGradient *gradient, SPStop *new_stop, GObject *data, bool gr_multi)
+{
+ if (!blocked) {
+ std::cerr << "update_stop_list should be blocked!" << std::endl;
+ }
+
+ int selected = -1;
+
+ auto act = static_cast<InkSelectOneAction*>(g_object_get_data(data, "gradient_stop_action"));
+ Glib::RefPtr<Gtk::ListStore> store = act->get_store();
+
+ if (!store) {
+ return selected;
+ }
+
+ store->clear();
+
+ InkSelectOneActionColumns 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, data);
+ }
+
+ return selected;
+}
+
+/* Set stop in menu to match stops selected by draggers. */
+static void select_stop_by_draggers(SPGradient *gradient, ToolBase *ev, GObject *data)
+{
+ if (!blocked) {
+ std::cerr << "select_stop_by_draggers should be blocked!" << std::endl;
+ }
+
+ if (!ev || !gradient)
+ return;
+
+ SPGradient *vector = gradient->getVector();
+ if (!vector)
+ return;
+
+ InkSelectOneAction *act =
+ static_cast<InkSelectOneAction *>(g_object_get_data(data, "gradient_stop_action"));
+
+ GrDrag *drag = ev->get_drag();
+
+ if (!drag || drag->selected.empty()) {
+ act->set_active(0);
+ gr_stop_set_offset(data);
+ return;
+ }
+
+ gint n = 0;
+ SPStop *stop = nullptr;
+ int selected = -1;
+
+ // For all selected draggers
+ for(auto i = drag->selected.begin(); i != drag->selected.end(); ++i) {
+
+ GrDragger *dragger = *i;
+
+ // For all draggables of dragger
+ for(auto j = dragger->draggables.begin(); j != dragger->draggables.end(); ++j) {
+
+ GrDraggable *draggable = *j;
+
+ 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
+
+ EgeAdjustmentAction* offset = (EgeAdjustmentAction *)g_object_get_data( G_OBJECT(data), "offset_action");
+ if (offset) {
+ gtk_action_set_sensitive( GTK_ACTION(offset), FALSE);
+ }
+
+ // Stop list always updated first... reinsert "Multiple stops" as first entry.
+ InkSelectOneActionColumns columns;
+ Glib::RefPtr<Gtk::ListStore> store = act->get_store();
+
+ Gtk::TreeModel::Row 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, data);
+ }
+
+ if (selected < 0) {
+ act->set_active (0);
+ act->set_sensitive (false);
+ } else {
+ act->set_active (selected);
+ act->set_sensitive (true);
+ gr_stop_set_offset(data);
+ }
+}
+
+/* Return gradient selected in menu. */
+static SPGradient *gr_get_selected_gradient(GObject *data)
+{
+ InkSelectOneAction *act =
+ static_cast<InkSelectOneAction *>(g_object_get_data(data, "gradient_select_action"));
+
+ int active = act->get_active();
+
+ Glib::RefPtr<Gtk::ListStore> store = act->get_store();
+ Gtk::TreeModel::Row row = store->children()[active];
+ InkSelectOneActionColumns columns;
+
+ void* pointer = row[columns.col_data];
+ SPGradient *gr = static_cast<SPGradient *>(pointer);
+
+ return gr;
+}
+
+/*
+ * Callback functions for user actions
+ */
+
+static void gr_new_type_changed( GObject * /*data*/, int mode )
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ prefs->setInt("/tools/gradient/newgradient",
+ mode == 0 ? SP_GRADIENT_TYPE_LINEAR : SP_GRADIENT_TYPE_RADIAL);
+}
+
+static void gr_new_fillstroke_changed( GObject * /*data*/, 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
+ */
+static void gr_gradient_changed( GObject *data, int active )
+{
+ if (blocked) {
+ return;
+ }
+
+ if (active < 0) {
+ return;
+ }
+
+ blocked = true;
+
+ SPGradient *gr = gr_get_selected_gradient (data);
+
+ if (gr) {
+ gr = sp_gradient_ensure_vector_normalized(gr);
+
+ SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(data, "desktop"));
+ Inkscape::Selection *selection = desktop->getSelection();
+ ToolBase *ev = desktop->getEventContext();
+
+ gr_apply_gradient(selection, ev ? ev->get_drag() : NULL, gr);
+
+ DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_GRADIENT,
+ _("Assign gradient to object"));
+ }
+
+ blocked = false;
+}
+
+/*
+ * User selected a spread method from the combobox
+ */
+static void gr_spread_changed( GObject *data, int active )
+{
+ if (blocked) {
+ return;
+ }
+
+ blocked = true;
+
+ SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(data, "desktop"));
+ Inkscape::Selection *selection = desktop->getSelection();
+ SPGradient *gradient = 0;
+ gr_get_dt_selected_gradient(selection, gradient);
+
+ if (gradient) {
+ SPGradientSpread spread = (SPGradientSpread) active;
+ gradient->setSpread(spread);
+ gradient->updateRepr();
+
+ DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_GRADIENT,
+ _("Set gradient repeat"));
+ }
+
+ blocked = false;
+}
+
+/*
+ * User selected a stop from the combobox
+ */
+static void gr_stop_changed( GObject *data, int active )
+{
+ if (blocked) {
+ return;
+ }
+
+ blocked = true;
+
+ SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(G_OBJECT(data), "desktop"));
+ ToolBase *ev = desktop->getEventContext();
+ SPGradient *gr = gr_get_selected_gradient(data);
+
+ select_dragger_by_stop(data, gr, ev);
+
+ blocked = false;
+}
+
+/*
+ * User selected a stop from the combobox
+ */
+static void gr_stop_combo_changed(GtkComboBox * /*widget*/, GObject *data)
+{
+ if (blocked) {
+ return;
+ }
+
+ blocked = true;
+
+ SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(G_OBJECT(data), "desktop"));
+ ToolBase *ev = desktop->getEventContext();
+ SPGradient *gr = gr_get_selected_gradient(data);
+
+ select_dragger_by_stop(data, gr, ev);
+
+ blocked = false;
+}
+
+/*
+ * User changed the offset
+ */
+static void gr_stop_offset_adjustment_changed(GtkAdjustment *adj, GObject *data)
+{
+ if (blocked) {
+ return;
+ }
+
+ blocked = true;
+
+ SPStop *stop = get_selected_stop(data);
+ if (stop) {
+ stop->offset = gtk_adjustment_get_value(adj);
+ 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;
+}
+
+
+static void gradient_toolbox_check_ec(SPDesktop* dt, Inkscape::UI::Tools::ToolBase* ec, GObject* data);
+
+/**
+ * Gradient auxiliary toolbar construction and setup.
+ *
+ */
+void sp_gradient_toolbox_prep(SPDesktop * desktop, GtkActionGroup* mainActions, GObject* data)
+{
+ GtkIconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ /* New gradient linear or radial */
+ {
+ InkSelectOneActionColumns columns;
+
+ Glib::RefPtr<Gtk::ListStore> store = Gtk::ListStore::create(columns);
+
+ Gtk::TreeModel::Row row;
+
+ row = *(store->append());
+ row[columns.col_label ] = _("linear");
+ row[columns.col_tooltip ] = _("Create linear gradient");
+ row[columns.col_icon ] = INKSCAPE_ICON("paint-gradient-linear");
+ row[columns.col_sensitive] = true;
+
+ row = *(store->append());
+ row[columns.col_label ] = _("radial");
+ row[columns.col_tooltip ] = _("Create radial (elliptic or circular) gradient");
+ row[columns.col_icon ] = INKSCAPE_ICON("paint-gradient-radial");
+ row[columns.col_sensitive] = true;
+
+ InkSelectOneAction* act =
+ InkSelectOneAction::create( "GradientNewTypeAction", // Name
+ _("New"), // Label
+ "", // Tooltip
+ "Not Used", // Icon
+ store ); // Tree store
+
+ act->use_radio( true );
+ act->use_group_label( true );
+ gint mode = prefs->getInt("/tools/gradient/newgradient", SP_GRADIENT_TYPE_LINEAR);
+ act->set_active( mode == SP_GRADIENT_TYPE_LINEAR ? 0 : 1 ); // linear == 1, radial == 2
+
+ gtk_action_group_add_action( mainActions, GTK_ACTION( act->gobj() ));
+ g_object_set_data( data, "gradient_new_type_mode", act );
+
+ act->signal_changed().connect(sigc::bind<0>(sigc::ptr_fun(&gr_new_type_changed), data));
+ }
+
+ /* New gradient on fill or stroke*/
+ {
+ InkSelectOneActionColumns columns;
+
+ Glib::RefPtr<Gtk::ListStore> store = Gtk::ListStore::create(columns);
+
+ Gtk::TreeModel::Row row;
+
+ row = *(store->append());
+ row[columns.col_label ] = _("fill");
+ row[columns.col_tooltip ] = _("Create gradient in the fill");
+ row[columns.col_icon ] = INKSCAPE_ICON("object-fill");
+ row[columns.col_sensitive] = true;
+
+ row = *(store->append());
+ row[columns.col_label ] = _("stroke");
+ row[columns.col_tooltip ] = _("Create gradient in the stroke");
+ row[columns.col_icon ] = INKSCAPE_ICON("object-stroke");
+ row[columns.col_sensitive] = true;
+
+ InkSelectOneAction* act =
+ InkSelectOneAction::create( "GradientNewFillStrokeAction", // Name
+ "", // Label
+ "", // Tooltip
+ "Not Used", // Icon
+ store ); // Tree store
+
+ act->use_radio( true );
+ act->use_group_label( false );
+ Inkscape::PaintTarget fsmode = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
+ act->set_active( fsmode == Inkscape::FOR_FILL ? 0 : 1 );
+
+ gtk_action_group_add_action( mainActions, GTK_ACTION( act->gobj() ));
+ g_object_set_data( data, "gradient_new_fillstroke_action", act );
+
+ act->signal_changed().connect(sigc::bind<0>(sigc::ptr_fun(&gr_new_fillstroke_changed), data));
+ }
+
+ /* Gradient Select list*/
+ {
+ InkSelectOneActionColumns columns;
+
+ Glib::RefPtr<Gtk::ListStore> 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;
+
+ InkSelectOneAction* act =
+ InkSelectOneAction::create( "GradientSelectGradientAction", // Name
+ _("Select"), // Label
+ "", // Tooltip
+ "Not Used", // Icon
+ store ); // Tree store
+
+ act->use_radio( false );
+ act->use_icon( false );
+ act->use_pixbuf( true );
+ act->use_group_label( true );
+ act->set_active( 0 );
+ act->set_sensitive( false );
+
+ gtk_action_group_add_action( mainActions, GTK_ACTION( act->gobj() ));
+ g_object_set_data( data, "gradient_select_action", act );
+
+ act->signal_changed().connect(sigc::bind<0>(sigc::ptr_fun(&gr_gradient_changed), data));
+ }
+
+ // Gradient Spread type (how a gradient is drawn outside it's nominal area)
+ {
+ InkSelectOneActionColumns columns;
+
+ Glib::RefPtr<Gtk::ListStore> store = Gtk::ListStore::create(columns);
+
+ Gtk::TreeModel::Row row;
+
+ row = *(store->append());
+ row[columns.col_label ] = C_("Gradient repeat type", "None");
+ row[columns.col_tooltip ] = "";
+ row[columns.col_icon ] = "NotUsed";
+ row[columns.col_sensitive] = true;
+
+ row = *(store->append());
+ row[columns.col_label ] = _("Reflected");
+ row[columns.col_tooltip ] = "";
+ row[columns.col_icon ] = "NotUsed";
+ row[columns.col_sensitive] = true;
+
+ row = *(store->append());
+ row[columns.col_label ] = _("Direct");
+ row[columns.col_tooltip ] = "";
+ row[columns.col_icon ] = "NotUsed";
+ row[columns.col_sensitive] = true;
+
+ InkSelectOneAction* act =
+ InkSelectOneAction::create( "GradientSelectSpreadAction", // Name
+ _("Repeat"), // Label
+ // 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\")"), // Tooltip
+ "Not Used", // Icon
+ store ); // Tree store
+
+ act->use_radio( false );
+ act->use_icon( false );
+ act->use_group_label( true );
+ act->set_sensitive( false );
+
+ gtk_action_group_add_action( mainActions, GTK_ACTION( act->gobj() ));
+ g_object_set_data( data, "gradient_spread_action", act );
+
+ act->signal_changed().connect(sigc::bind<0>(sigc::ptr_fun(&gr_spread_changed), data));
+ }
+
+ /* Gradidnt Stop list */
+ {
+ InkSelectOneActionColumns columns;
+
+ Glib::RefPtr<Gtk::ListStore> 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;
+
+ InkSelectOneAction* act =
+ InkSelectOneAction::create( "GradientStopAction", // Name
+ _("Stops" ), // Label
+ "", // Tooltip
+ "Not Used", // Icon
+ store ); // Tree store
+
+ act->use_radio( false );
+ act->use_icon( false );
+ act->use_pixbuf( true );
+ act->use_group_label( true );
+ act->set_active( 0 );
+ act->set_sensitive( false );
+
+ gtk_action_group_add_action( mainActions, GTK_ACTION( act->gobj() ));
+ g_object_set_data( data, "gradient_stop_action", act );
+
+ act->signal_changed().connect(sigc::bind<0>(sigc::ptr_fun(&gr_stop_changed), data));
+ }
+
+ /* Offset */
+ {
+ EgeAdjustmentAction* eact = 0;
+ eact = create_adjustment_action( "GradientEditOffsetAction",
+ _("Offset"), C_("Gradient", "Offset:"), _("Offset of selected stop"),
+ "/tools/gradient/stopoffset", 0,
+ GTK_WIDGET(desktop->canvas), data, FALSE, NULL,
+ 0.0, 1.0, 0.01, 0.1,
+ 0, 0, 0,
+ gr_stop_offset_adjustment_changed,
+ NULL /*unit tracker*/,
+ 0.01, 2, 1.0);
+
+ gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
+ g_object_set_data( data, "offset_action", eact );
+ gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
+
+ }
+
+ /* Add stop */
+ {
+ InkAction* inky = ink_action_new( "GradientEditAddAction",
+ _("Insert new stop"),
+ _("Insert new stop"),
+ INKSCAPE_ICON("node-add"),
+ secondarySize );
+ g_object_set( inky, "short_label", _("Delete"), NULL );
+ g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(gr_add_stop), data );
+ gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
+ gtk_action_set_sensitive( GTK_ACTION(inky), FALSE );
+ g_object_set_data( data, "gradient_stops_add_action", inky );
+ }
+
+ /* Delete stop */
+ {
+ InkAction* inky = ink_action_new( "GradientEditDeleteAction",
+ _("Delete stop"),
+ _("Delete stop"),
+ INKSCAPE_ICON("node-delete"),
+ secondarySize );
+ g_object_set( inky, "short_label", _("Delete"), NULL );
+ g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(gr_remove_stop), data );
+ gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
+ gtk_action_set_sensitive( GTK_ACTION(inky), FALSE );
+ g_object_set_data( data, "gradient_stops_delete_action", inky );
+ }
+
+ /* Reverse */
+ {
+ InkAction* inky = ink_action_new( "GradientEditReverseAction",
+ _("Reverse"),
+ _("Reverse the direction of the gradient"),
+ INKSCAPE_ICON("object-flip-horizontal"),
+ secondarySize );
+ g_object_set( inky, "short_label", _("Delete"), NULL );
+ g_signal_connect_after( G_OBJECT(inky), "activate", G_CALLBACK(gr_reverse), desktop );
+ gtk_action_group_add_action( mainActions, GTK_ACTION(inky) );
+ gtk_action_set_sensitive( GTK_ACTION(inky), FALSE );
+ g_object_set_data( data, "gradient_stops_reverse_action", inky );
+
+ }
+
+ // Gradients Linked toggle
+ {
+ InkToggleAction* itact = ink_toggle_action_new( "GradientEditLinkAction",
+ _("Link gradients"),
+ _("Link gradients to change all related gradients"),
+ INKSCAPE_ICON("object-unlocked"),
+ GTK_ICON_SIZE_MENU );
+ g_object_set( itact, "short_label", "Lock", NULL );
+ g_signal_connect_after( G_OBJECT(itact), "toggled", G_CALLBACK(gr_linked_changed), desktop) ;
+ gtk_action_group_add_action( mainActions, GTK_ACTION(itact) );
+
+ bool linkedmode = prefs->getBool("/options/forkgradientvectors/value", true);
+ gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(itact), !linkedmode );
+ }
+
+ g_object_set_data(data, "desktop", desktop);
+
+ desktop->connectEventContextChanged(sigc::bind(sigc::ptr_fun(&gradient_toolbox_check_ec), data));
+}
+
+
+// =================== Selection Changed Callbacks ======================= //
+
+/*
+ * Core function, setup all the widgets whenever something changes on the desktop
+ */
+static void gr_tb_selection_changed(Inkscape::Selection * /*selection*/, GObject* data)
+{
+ if (blocked)
+ return;
+
+ blocked = true;
+
+ SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(data, "desktop"));
+ if (!desktop) {
+ return;
+ }
+
+ Inkscape::Selection *selection = desktop->getSelection(); // take from desktop, not from args
+ if (selection) {
+
+ ToolBase *ev = desktop->getEventContext();
+ GrDrag *drag = NULL;
+ if (ev) {
+ drag = ev->get_drag();
+ }
+
+ SPGradient *gr_selected = 0;
+ 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 sel = static_cast<InkSelectOneAction*>(g_object_get_data(data, "gradient_select_action"));
+ Glib::RefPtr<Gtk::ListStore> store = sel->get_store();
+ int gradient = gr_vector_list (store, desktop, selection->isEmpty(), gr_selected, gr_multi);
+
+ if (gradient < 0) {
+ // No selection or no gradients
+ sel->set_active( 0 );
+ sel->set_sensitive (false);
+ } else {
+ // Single gradient or multiple gradients
+ sel->set_active( gradient );
+ sel->set_sensitive (true);
+ }
+
+ // Spread menu
+ auto spread = static_cast<InkSelectOneAction*>(g_object_get_data(data, "gradient_spread_action"));
+ spread->set_sensitive( gr_selected && !gr_multi );
+ spread->set_active( gr_selected ? (int)spr_selected : 0 );
+
+ InkAction *add = (InkAction *) g_object_get_data(data, "gradient_stops_add_action");
+ gtk_action_set_sensitive(GTK_ACTION(add), (gr_selected && !gr_multi && drag && !drag->selected.empty()));
+
+ InkAction *del = (InkAction *) g_object_get_data(data, "gradient_stops_delete_action");
+ gtk_action_set_sensitive(GTK_ACTION(del), (gr_selected && !gr_multi && drag && !drag->selected.empty()));
+
+ InkAction *reverse = (InkAction *) g_object_get_data(data, "gradient_stops_reverse_action");
+ gtk_action_set_sensitive(GTK_ACTION(reverse), (gr_selected!= NULL));
+
+ InkSelectOneAction *stops_action = (InkSelectOneAction *) g_object_get_data(data, "gradient_stop_action");
+ stops_action->set_sensitive( gr_selected && !gr_multi);
+
+ int stop = update_stop_list (gr_selected, nullptr, data, gr_multi);
+ select_stop_by_draggers(gr_selected, ev, data);
+ }
+
+ blocked = false;
+}
+
+static void gr_tb_selection_modified(Inkscape::Selection *selection, guint /*flags*/, GObject* data)
+{
+ gr_tb_selection_changed(selection, data);
+}
+
+static void gr_drag_selection_changed(gpointer /*dragger*/, GObject* data)
+{
+ gr_tb_selection_changed(NULL, data);
+}
+
+static void gr_defs_release(SPObject * /*defs*/, GObject* data)
+{
+ gr_tb_selection_changed(NULL, data);
+}
+
+static void gr_defs_modified(SPObject * /*defs*/, guint /*flags*/, GObject* data)
+{
+ gr_tb_selection_changed(NULL, data);
+}
+
+// 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.
+ */
+static void gradient_toolbox_check_ec(SPDesktop* desktop, Inkscape::UI::Tools::ToolBase* ec, GObject* data)
+{
+ static sigc::connection connChanged;
+ static sigc::connection connModified;
+ static sigc::connection connSubselectionChanged;
+ static sigc::connection connDefsRelease;
+ static sigc::connection connDefsModified;
+
+ if (SP_IS_GRADIENT_CONTEXT(ec)) {
+ Inkscape::Selection *selection = desktop->getSelection();
+ SPDocument *document = desktop->getDocument();
+
+ // connect to selection modified and changed signals
+ connChanged = selection->connectChanged(sigc::bind(sigc::ptr_fun(&gr_tb_selection_changed), data));
+ connModified = selection->connectModified(sigc::bind(sigc::ptr_fun(&gr_tb_selection_modified), data));
+ connSubselectionChanged = desktop->connectToolSubselectionChanged(sigc::bind(sigc::ptr_fun(&gr_drag_selection_changed), data));
+
+ // Is this necessary? Couldn't hurt.
+ gr_tb_selection_changed(selection, data);
+
+ // connect to release and modified signals of the defs (i.e. when someone changes gradient)
+ connDefsRelease = document->getDefs()->connectRelease( sigc::bind<1>(sigc::ptr_fun(&gr_defs_release), data));
+ connDefsModified = document->getDefs()->connectModified(sigc::bind<2>(sigc::ptr_fun(&gr_defs_modified), data));
+ } else {
+ if (connChanged)
+ connChanged.disconnect();
+ if (connModified)
+ connModified.disconnect();
+ if (connSubselectionChanged)
+ connSubselectionChanged.disconnect();
+ if (connDefsRelease)
+ connDefsRelease.disconnect();
+ if (connDefsModified)
+ connDefsModified.disconnect();
+ }
+}
+
+/*
+ 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 :