diff options
Diffstat (limited to 'src/widgets/gradient-selector.cpp')
-rw-r--r-- | src/widgets/gradient-selector.cpp | 660 |
1 files changed, 660 insertions, 0 deletions
diff --git a/src/widgets/gradient-selector.cpp b/src/widgets/gradient-selector.cpp new file mode 100644 index 0000000..387f077 --- /dev/null +++ b/src/widgets/gradient-selector.cpp @@ -0,0 +1,660 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Gradient vector widget + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * bulia byak <buliabyak@users.sf.net> + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2001 Ximian, Inc. + * Copyright (C) 2010 Jon A. Cruz + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <glibmm/i18n.h> +#include <gtkmm/treeview.h> +#include <vector> + +#include "document-undo.h" +#include "document.h" +#include "gradient-chemistry.h" +#include "gradient-vector.h" +#include "id-clash.h" +#include "inkscape.h" +#include "paint-selector.h" +#include "preferences.h" +#include "verbs.h" + +#include "object/sp-defs.h" +#include "style.h" + +#include "helper/action.h" +#include "ui/icon-loader.h" + +#include "ui/icon-names.h" + +enum { + GRABBED, + DRAGGED, + RELEASED, + CHANGED, + LAST_SIGNAL +}; + + +static void sp_gradient_selector_dispose(GObject *object); + +/* Signal handlers */ +static void sp_gradient_selector_vector_set (SPGradientVectorSelector *gvs, SPGradient *gr, SPGradientSelector *sel); +static void sp_gradient_selector_edit_vector_clicked (GtkWidget *w, SPGradientSelector *sel); +static void sp_gradient_selector_add_vector_clicked (GtkWidget *w, SPGradientSelector *sel); +static void sp_gradient_selector_delete_vector_clicked (GtkWidget *w, SPGradientSelector *sel); + +static guint signals[LAST_SIGNAL] = {0}; + +G_DEFINE_TYPE(SPGradientSelector, sp_gradient_selector, GTK_TYPE_BOX); + +static void sp_gradient_selector_class_init(SPGradientSelectorClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + signals[GRABBED] = g_signal_new ("grabbed", + G_TYPE_FROM_CLASS(object_class), + (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), + G_STRUCT_OFFSET (SPGradientSelectorClass, grabbed), + nullptr, nullptr, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[DRAGGED] = g_signal_new ("dragged", + G_TYPE_FROM_CLASS(object_class), + (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), + G_STRUCT_OFFSET (SPGradientSelectorClass, dragged), + nullptr, nullptr, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[RELEASED] = g_signal_new ("released", + G_TYPE_FROM_CLASS(object_class), + (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), + G_STRUCT_OFFSET (SPGradientSelectorClass, released), + nullptr, nullptr, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[CHANGED] = g_signal_new ("changed", + G_TYPE_FROM_CLASS(object_class), + (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE), + G_STRUCT_OFFSET (SPGradientSelectorClass, changed), + nullptr, nullptr, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + object_class->dispose = sp_gradient_selector_dispose; +} + +static void gradsel_style_button(GtkWidget *gtkbtn, char const *iconName) +{ + Gtk::Button *btn = Glib::wrap(GTK_BUTTON(gtkbtn)); + GtkWidget *child = sp_get_icon_image(iconName, GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_widget_show(child); + btn->add(*manage(Glib::wrap(child))); + btn->set_relief(Gtk::RELIEF_NONE); +} + +static void sp_gradient_selector_init(SPGradientSelector *sel) +{ + sel->safelyInit = true; + sel->blocked = false; + + gtk_orientable_set_orientation(GTK_ORIENTABLE(sel), GTK_ORIENTATION_VERTICAL); + + new (&sel->nonsolid) std::vector<GtkWidget*>(); + new (&sel->swatch_widgets) std::vector<GtkWidget*>(); + + sel->mode = SPGradientSelector::MODE_LINEAR; + + sel->gradientUnits = SP_GRADIENT_UNITS_USERSPACEONUSE; + sel->gradientSpread = SP_GRADIENT_SPREAD_PAD; + + /* Vectors */ + sel->vectors = sp_gradient_vector_selector_new (nullptr, nullptr); + SPGradientVectorSelector *gvs = SP_GRADIENT_VECTOR_SELECTOR(sel->vectors); + sel->store = gvs->store; + sel->columns = gvs->columns; + + sel->treeview = Gtk::manage(new Gtk::TreeView()); + sel->treeview->set_model(gvs->store); + sel->treeview->set_headers_clickable (true); + sel->treeview->set_search_column(1); + sel->treeview->set_vexpand(); + sel->icon_renderer = Gtk::manage(new Gtk::CellRendererPixbuf()); + sel->text_renderer = Gtk::manage(new Gtk::CellRendererText()); + + sel->treeview->append_column(_("Gradient"), *sel->icon_renderer); + Gtk::TreeView::Column* icon_column = sel->treeview->get_column(0); + icon_column->add_attribute(sel->icon_renderer->property_pixbuf(), sel->columns->pixbuf); + icon_column->set_sort_column(sel->columns->color); + icon_column->set_clickable(true); + + sel->treeview->append_column(_("Name"), *sel->text_renderer); + Gtk::TreeView::Column* name_column = sel->treeview->get_column(1); + sel->text_renderer->property_editable() = true; + name_column->add_attribute(sel->text_renderer->property_text(), sel->columns->name); + name_column->set_min_width(180); + name_column->set_clickable(true); + name_column->set_resizable(true); + + sel->treeview->append_column("#", sel->columns->refcount); + Gtk::TreeView::Column* count_column = sel->treeview->get_column(2); + count_column->set_clickable(true); + count_column->set_resizable(true); + + sel->treeview->signal_key_press_event().connect(sigc::mem_fun(*sel, &SPGradientSelector::onKeyPressEvent), false); + + sel->treeview->show(); + + icon_column->signal_clicked().connect( sigc::mem_fun(*sel, &SPGradientSelector::onTreeColorColClick) ); + name_column->signal_clicked().connect( sigc::mem_fun(*sel, &SPGradientSelector::onTreeNameColClick) ); + count_column->signal_clicked().connect( sigc::mem_fun(*sel, &SPGradientSelector::onTreeCountColClick) ); + + gvs->tree_select_connection = sel->treeview->get_selection()->signal_changed().connect( sigc::mem_fun(*sel, &SPGradientSelector::onTreeSelection) ); + sel->text_renderer->signal_edited().connect( sigc::mem_fun(*sel, &SPGradientSelector::onGradientRename) ); + + sel->scrolled_window = Gtk::manage(new Gtk::ScrolledWindow()); + sel->scrolled_window->add(*sel->treeview); + sel->scrolled_window->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + sel->scrolled_window->set_shadow_type(Gtk::SHADOW_IN); + sel->scrolled_window->set_size_request(0, 180); + sel->scrolled_window->set_hexpand(); + sel->scrolled_window->show(); + + gtk_box_pack_start (GTK_BOX (sel), GTK_WIDGET(sel->scrolled_window->gobj()), TRUE, TRUE, 4); + + + /* Create box for buttons */ + auto hb = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_set_homogeneous(GTK_BOX(hb), FALSE); + gtk_box_pack_start( GTK_BOX(sel), hb, FALSE, FALSE, 0 ); + + sel->add = gtk_button_new(); + gradsel_style_button(sel->add, INKSCAPE_ICON("list-add")); + + sel->nonsolid.push_back(sel->add); + gtk_box_pack_start (GTK_BOX (hb), sel->add, FALSE, FALSE, 0); + + g_signal_connect (G_OBJECT (sel->add), "clicked", G_CALLBACK (sp_gradient_selector_add_vector_clicked), sel); + gtk_widget_set_sensitive (sel->add, FALSE); + gtk_button_set_relief(GTK_BUTTON(sel->add), GTK_RELIEF_NONE); + gtk_widget_set_tooltip_text( sel->add, _("Create a duplicate gradient")); + + sel->edit = gtk_button_new(); + gradsel_style_button(sel->edit, INKSCAPE_ICON("edit")); + + sel->nonsolid.push_back(sel->edit); + gtk_box_pack_start (GTK_BOX (hb), sel->edit, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (sel->edit), "clicked", G_CALLBACK (sp_gradient_selector_edit_vector_clicked), sel); + gtk_widget_set_sensitive (sel->edit, FALSE); + gtk_button_set_relief(GTK_BUTTON(sel->edit), GTK_RELIEF_NONE); + gtk_widget_set_tooltip_text( sel->edit, _("Edit gradient")); + + sel->del = gtk_button_new (); + gradsel_style_button(sel->del, INKSCAPE_ICON("list-remove")); + + sel->swatch_widgets.push_back(sel->del); + gtk_box_pack_start (GTK_BOX (hb), sel->del, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (sel->del), "clicked", G_CALLBACK (sp_gradient_selector_delete_vector_clicked), sel); + gtk_widget_set_sensitive (sel->del, FALSE); + gtk_button_set_relief(GTK_BUTTON(sel->del), GTK_RELIEF_NONE); + gtk_widget_set_tooltip_text( sel->del, _("Delete swatch")); + + gtk_widget_show_all(hb); +} + +static void sp_gradient_selector_dispose(GObject *object) +{ + SPGradientSelector *sel = SP_GRADIENT_SELECTOR( object ); + + if ( sel->safelyInit ) { + sel->safelyInit = false; + sel->nonsolid.~vector<GtkWidget*>(); + sel->swatch_widgets.~vector<GtkWidget*>(); + } + + if (sel->icon_renderer) { + delete sel->icon_renderer; + sel->icon_renderer = nullptr; + } + if (sel->text_renderer) { + delete sel->text_renderer; + sel->text_renderer = nullptr; + } + + if ((G_OBJECT_CLASS(sp_gradient_selector_parent_class))->dispose) { + (G_OBJECT_CLASS(sp_gradient_selector_parent_class))->dispose(object); + } +} + +void SPGradientSelector::setSpread(SPGradientSpread spread) +{ + gradientSpread = spread; + //gtk_combo_box_set_active (GTK_COMBO_BOX(this->spread), gradientSpread); +} + + +GtkWidget *sp_gradient_selector_new() +{ + SPGradientSelector *sel = SP_GRADIENT_SELECTOR(g_object_new (SP_TYPE_GRADIENT_SELECTOR, nullptr)); + + return GTK_WIDGET(sel); +} + +void SPGradientSelector::setMode(SelectorMode mode) +{ + if (mode != this->mode) { + this->mode = mode; + if (mode == MODE_SWATCH) { + for (auto & it : nonsolid) + { + gtk_widget_hide(it); + } + for (auto & swatch_widget : swatch_widgets) + { + gtk_widget_show_all(swatch_widget); + } + + Gtk::TreeView::Column* icon_column = treeview->get_column(0); + icon_column->set_title(_("Swatch")); + + SPGradientVectorSelector* vs = SP_GRADIENT_VECTOR_SELECTOR(vectors); + vs->setSwatched(); + } else { + for (auto & it : nonsolid) + { + gtk_widget_show_all(it); + } + for (auto & swatch_widget : swatch_widgets) + { + gtk_widget_hide(swatch_widget); + } + Gtk::TreeView::Column* icon_column = treeview->get_column(0); + icon_column->set_title(_("Gradient")); + + } + } +} + +void SPGradientSelector::setUnits(SPGradientUnits units) +{ + gradientUnits = units; +} + +SPGradientUnits SPGradientSelector::getUnits() +{ + return gradientUnits; +} + +SPGradientSpread SPGradientSelector::getSpread() +{ + return gradientSpread; +} + +void SPGradientSelector::onGradientRename( const Glib::ustring& path_string, const Glib::ustring& new_text) +{ + Gtk::TreePath path(path_string); + Gtk::TreeModel::iterator iter = store->get_iter(path); + + if( iter ) + { + Gtk::TreeModel::Row row = *iter; + if ( row ) { + SPObject* obj = row[columns->data]; + if ( obj ) { + row[columns->name] = gr_prepare_label(obj); + if (!new_text.empty() && new_text != row[columns->name]) { + rename_id(obj, new_text ); + Inkscape::DocumentUndo::done(obj->document, SP_VERB_CONTEXT_GRADIENT, + _("Rename gradient")); + } + } + } + } +} + +void SPGradientSelector::onTreeColorColClick() { + Gtk::TreeView::Column* column = treeview->get_column(0); + column->set_sort_column(columns->color); +} + +void SPGradientSelector::onTreeNameColClick() { + Gtk::TreeView::Column* column = treeview->get_column(1); + column->set_sort_column(columns->name); +} + + +void SPGradientSelector::onTreeCountColClick() { + Gtk::TreeView::Column* column = treeview->get_column(2); + column->set_sort_column(columns->refcount); +} + +void SPGradientSelector::moveSelection(int amount, bool down, bool toEnd) +{ + Glib::RefPtr<Gtk::TreeSelection> select = treeview->get_selection(); + auto iter = select->get_selected(); + + if (amount < 0) { + down = !down; + amount = -amount; + } + + auto canary = iter; + if (down) { + ++canary; + } else { + --canary; + } + while (canary && (toEnd || amount > 0)) { + --amount; + if (down) { + ++canary; + ++iter; + } else { + --canary; + --iter; + } + } + + select->select(iter); + treeview->scroll_to_row(store->get_path(iter), 0.5); +} + +bool SPGradientSelector::onKeyPressEvent(GdkEventKey *event) +{ + bool consume = false; + auto display = Gdk::Display::get_default(); + auto keymap = display->get_keymap(); + guint key = 0; + gdk_keymap_translate_keyboard_state(keymap, event->hardware_keycode, + static_cast<GdkModifierType>(event->state), + 0, &key, 0, 0, 0); + + switch (key) { + case GDK_KEY_Up: + case GDK_KEY_KP_Up: { + moveSelection(-1); + consume = true; + break; + } + case GDK_KEY_Down: + case GDK_KEY_KP_Down: { + moveSelection(1); + consume = true; + break; + } + case GDK_KEY_Page_Up: + case GDK_KEY_KP_Page_Up: { + moveSelection(-5); + consume = true; + break; + } + + case GDK_KEY_Page_Down: + case GDK_KEY_KP_Page_Down: { + moveSelection(5); + consume = true; + break; + } + + case GDK_KEY_End: + case GDK_KEY_KP_End: { + moveSelection(0, true, true); + consume = true; + break; + } + + case GDK_KEY_Home: + case GDK_KEY_KP_Home: { + moveSelection(0, false, true); + consume = true; + break; + } + } + return consume; +} + +void SPGradientSelector::onTreeSelection() +{ + if (!treeview) { + return; + } + + if (blocked) { + return; + } + + if (!treeview->has_focus()) { + /* Workaround for GTK bug on Windows/OS X + * When the treeview initially doesn't have focus and is clicked + * sometimes get_selection()->signal_changed() has the wrong selection + */ + treeview->grab_focus(); + } + + const Glib::RefPtr<Gtk::TreeSelection> sel = treeview->get_selection(); + if (!sel) { + return; + } + + SPGradient *obj = nullptr; + /* Single selection */ + Gtk::TreeModel::iterator iter = sel->get_selected(); + if ( iter ) { + Gtk::TreeModel::Row row = *iter; + obj = row[columns->data]; + } + + if (obj) { + sp_gradient_selector_vector_set (nullptr, SP_GRADIENT(obj), this); + } +} + +bool SPGradientSelector::_checkForSelected(const Gtk::TreePath &path, const Gtk::TreeIter& iter, SPGradient *vector) +{ + bool found = false; + + Gtk::TreeModel::Row row = *iter; + if ( vector == row[columns->data] ) + { + treeview->scroll_to_row(path, 0.5); + Glib::RefPtr<Gtk::TreeSelection> select = treeview->get_selection(); + bool wasBlocked = blocked; + blocked = true; + select->select(iter); + blocked = wasBlocked; + found = true; + } + + return found; +} + +void SPGradientSelector::selectGradientInTree(SPGradient *vector) +{ + store->foreach( sigc::bind<SPGradient*>(sigc::mem_fun(*this, &SPGradientSelector::_checkForSelected), vector) ); +} + +void SPGradientSelector::setVector(SPDocument *doc, SPGradient *vector) +{ + g_return_if_fail(!vector || SP_IS_GRADIENT(vector)); + g_return_if_fail(!vector || (vector->document == doc)); + + if (vector && !vector->hasStops()) { + return; + } + + sp_gradient_vector_selector_set_gradient(SP_GRADIENT_VECTOR_SELECTOR(vectors), doc, vector); + + selectGradientInTree(vector); + + if (vector) { + if ( (mode == MODE_SWATCH) && vector->isSwatch() ) { + if ( vector->isSolid() ) { + for (auto & it : nonsolid) + { + gtk_widget_hide(it); + } + } else { + for (auto & it : nonsolid) + { + gtk_widget_show_all(it); + } + } + } else if (mode != MODE_SWATCH) { + + for (auto & swatch_widget : swatch_widgets) + { + gtk_widget_hide(swatch_widget); + } + for (auto & it : nonsolid) + { + gtk_widget_show_all(it); + } + + } + + if (edit) { + gtk_widget_set_sensitive(edit, TRUE); + } + if (add) { + gtk_widget_set_sensitive(add, TRUE); + } + if (del) { + gtk_widget_set_sensitive(del, TRUE); + } + } else { + if (edit) { + gtk_widget_set_sensitive(edit, FALSE); + } + if (add) { + gtk_widget_set_sensitive(add, (doc != nullptr)); + } + if (del) { + gtk_widget_set_sensitive(del, FALSE); + } + } +} + +SPGradient *SPGradientSelector::getVector() +{ + /* fixme: */ + return SP_GRADIENT_VECTOR_SELECTOR(vectors)->gr; +} + + +static void sp_gradient_selector_vector_set(SPGradientVectorSelector * /*gvs*/, SPGradient *gr, SPGradientSelector *sel) +{ + + if (!sel->blocked) { + sel->blocked = TRUE; + gr = sp_gradient_ensure_vector_normalized (gr); + sel->setVector((gr) ? gr->document : nullptr, gr); + g_signal_emit (G_OBJECT (sel), signals[CHANGED], 0, gr); + sel->blocked = FALSE; + } +} + + +static void +sp_gradient_selector_delete_vector_clicked (GtkWidget */*w*/, SPGradientSelector *sel) +{ + const Glib::RefPtr<Gtk::TreeSelection> selection = sel->treeview->get_selection(); + if (!selection) { + return; + } + + SPGradient *obj = nullptr; + /* Single selection */ + Gtk::TreeModel::iterator iter = selection->get_selected(); + if ( iter ) { + Gtk::TreeModel::Row row = *iter; + obj = row[sel->columns->data]; + } + + if (obj) { + sp_gradient_unset_swatch(SP_ACTIVE_DESKTOP, obj->getId()); + } + +} + +static void +sp_gradient_selector_edit_vector_clicked (GtkWidget */*w*/, SPGradientSelector *sel) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + if (prefs->getBool("/dialogs/gradienteditor/showlegacy", false)) { + // Legacy gradient dialog + GtkWidget *dialog; + dialog = sp_gradient_vector_editor_new (SP_GRADIENT_VECTOR_SELECTOR (sel->vectors)->gr); + gtk_widget_show (dialog); + } else { + // Invoke the gradient tool + Inkscape::Verb *verb = Inkscape::Verb::get( SP_VERB_CONTEXT_GRADIENT ); + if ( verb ) { + SPAction *action = verb->get_action( Inkscape::ActionContext( ( Inkscape::UI::View::View * ) SP_ACTIVE_DESKTOP ) ); + if ( action ) { + sp_action_perform( action, nullptr ); + } + } + } +} + +static void +sp_gradient_selector_add_vector_clicked (GtkWidget */*w*/, SPGradientSelector *sel) +{ + SPDocument *doc = sp_gradient_vector_selector_get_document (SP_GRADIENT_VECTOR_SELECTOR (sel->vectors)); + + if (!doc) + return; + + SPGradient *gr = sp_gradient_vector_selector_get_gradient( SP_GRADIENT_VECTOR_SELECTOR (sel->vectors)); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); + + Inkscape::XML::Node *repr = nullptr; + + if (gr) { + repr = gr->getRepr()->duplicate(xml_doc); + // Rename the new gradients id to be similar to the cloned gradients + Glib::ustring old_id = gr->getId(); + rename_id(gr, old_id); + doc->getDefs()->getRepr()->addChild(repr, nullptr); + } else { + repr = xml_doc->createElement("svg:linearGradient"); + Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop"); + stop->setAttribute("offset", "0"); + stop->setAttribute("style", "stop-color:#000;stop-opacity:1;"); + repr->appendChild(stop); + Inkscape::GC::release(stop); + stop = xml_doc->createElement("svg:stop"); + stop->setAttribute("offset", "1"); + stop->setAttribute("style", "stop-color:#fff;stop-opacity:1;"); + repr->appendChild(stop); + Inkscape::GC::release(stop); + doc->getDefs()->getRepr()->addChild(repr, nullptr); + gr = SP_GRADIENT(doc->getObjectByRepr(repr)); + } + + sp_gradient_vector_selector_set_gradient( SP_GRADIENT_VECTOR_SELECTOR (sel->vectors), doc, gr); + + sel->selectGradientInTree(gr); + + Inkscape::GC::release(repr); +} + +/* + 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 : |