diff options
Diffstat (limited to 'src/ui/widget/gradient-selector.cpp')
-rw-r--r-- | src/ui/widget/gradient-selector.cpp | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/src/ui/widget/gradient-selector.cpp b/src/ui/widget/gradient-selector.cpp new file mode 100644 index 0000000..e6bfa7e --- /dev/null +++ b/src/ui/widget/gradient-selector.cpp @@ -0,0 +1,612 @@ +// 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 "id-clash.h" +#include "inkscape.h" +#include "preferences.h" + +#include "object/sp-defs.h" +#include "style.h" + +#include "actions/actions-tools.h" // Invoke gradient tool +#include "ui/icon-loader.h" +#include "ui/icon-names.h" +#include "ui/widget/gradient-vector-selector.h" + +namespace Inkscape { +namespace UI { +namespace Widget { + +void GradientSelector::style_button(Gtk::Button *btn, char const *iconName) +{ + 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); +} + +GradientSelector::GradientSelector() + : _blocked(false) + , _mode(MODE_LINEAR) + , _gradientUnits(SP_GRADIENT_UNITS_USERSPACEONUSE) + , _gradientSpread(SP_GRADIENT_SPREAD_PAD) +{ + set_orientation(Gtk::ORIENTATION_VERTICAL); + + /* Vectors */ + _vectors = Gtk::manage(new GradientVectorSelector(nullptr, nullptr)); + _store = _vectors->get_store(); + _columns = _vectors->get_columns(); + + _treeview = Gtk::manage(new Gtk::TreeView()); + _treeview->set_model(_store); + _treeview->set_headers_clickable(true); + _treeview->set_search_column(1); + _treeview->set_vexpand(); + _icon_renderer = Gtk::manage(new Gtk::CellRendererPixbuf()); + _text_renderer = Gtk::manage(new Gtk::CellRendererText()); + + _treeview->append_column(_("Gradient"), *_icon_renderer); + auto icon_column = _treeview->get_column(0); + icon_column->add_attribute(_icon_renderer->property_pixbuf(), _columns->pixbuf); + icon_column->set_sort_column(_columns->color); + icon_column->set_clickable(true); + + _treeview->append_column(_("Name"), *_text_renderer); + auto name_column = _treeview->get_column(1); + _text_renderer->property_editable() = true; + name_column->add_attribute(_text_renderer->property_text(), _columns->name); + name_column->set_min_width(180); + name_column->set_clickable(true); + name_column->set_resizable(true); + + _treeview->append_column("#", _columns->refcount); + auto count_column = _treeview->get_column(2); + count_column->set_clickable(true); + count_column->set_resizable(true); + + _treeview->signal_key_press_event().connect(sigc::mem_fun(this, &GradientSelector::onKeyPressEvent), false); + + _treeview->show(); + + icon_column->signal_clicked().connect(sigc::mem_fun(this, &GradientSelector::onTreeColorColClick)); + name_column->signal_clicked().connect(sigc::mem_fun(this, &GradientSelector::onTreeNameColClick)); + count_column->signal_clicked().connect(sigc::mem_fun(this, &GradientSelector::onTreeCountColClick)); + + auto tree_select_connection = _treeview->get_selection()->signal_changed().connect(sigc::mem_fun(this, &GradientSelector::onTreeSelection)); + _vectors->set_tree_select_connection(tree_select_connection); + _text_renderer->signal_edited().connect(sigc::mem_fun(this, &GradientSelector::onGradientRename)); + + _scrolled_window = Gtk::manage(new Gtk::ScrolledWindow()); + _scrolled_window->add(*_treeview); + _scrolled_window->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + _scrolled_window->set_shadow_type(Gtk::SHADOW_IN); + _scrolled_window->set_size_request(0, 180); + _scrolled_window->set_hexpand(); + _scrolled_window->show(); + + pack_start(*_scrolled_window, true, true, 4); + + + /* Create box for buttons */ + auto hb = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0)); + hb->set_homogeneous(false); + pack_start(*hb, false, false, 0); + + _add = Gtk::manage(new Gtk::Button()); + style_button(_add, INKSCAPE_ICON("list-add")); + + _nonsolid.push_back(_add); + hb->pack_start(*_add, false, false, 0); + + _add->signal_clicked().connect(sigc::mem_fun(this, &GradientSelector::add_vector_clicked)); + _add->set_sensitive(false); + _add->set_relief(Gtk::RELIEF_NONE); + _add->set_tooltip_text(_("Create a duplicate gradient")); + + _del2 = Gtk::manage(new Gtk::Button()); + style_button(_del2, INKSCAPE_ICON("list-remove")); + + _nonsolid.push_back(_del2); + hb->pack_start(*_del2, false, false, 0); + _del2->signal_clicked().connect(sigc::mem_fun(this, &GradientSelector::delete_vector_clicked_2)); + _del2->set_sensitive(false); + _del2->set_relief(Gtk::RELIEF_NONE); + _del2->set_tooltip_text(_("Delete unused gradient")); + + // The only use of this button is hidden! + _edit = Gtk::manage(new Gtk::Button()); + style_button(_edit, INKSCAPE_ICON("edit")); + + _nonsolid.push_back(_edit); + hb->pack_start(*_edit, false, false, 0); + _edit->signal_clicked().connect(sigc::mem_fun(this, &GradientSelector::edit_vector_clicked)); + _edit->set_sensitive(false); + _edit->set_relief(Gtk::RELIEF_NONE); + _edit->set_tooltip_text(_("Edit gradient")); + _edit->set_no_show_all(); + + _del = Gtk::manage(new Gtk::Button()); + style_button(_del, INKSCAPE_ICON("list-remove")); + + _swatch_widgets.push_back(_del); + hb->pack_start(*_del, false, false, 0); + _del->signal_clicked().connect(sigc::mem_fun(this, &GradientSelector::delete_vector_clicked)); + _del->set_sensitive(false); + _del->set_relief(Gtk::RELIEF_NONE); + _del->set_tooltip_text(_("Delete swatch")); + + hb->show_all(); +} + +void GradientSelector::setSpread(SPGradientSpread spread) +{ + _gradientSpread = spread; + // gtk_combo_box_set_active (GTK_COMBO_BOX(this->spread), gradientSpread); +} + +void GradientSelector::setMode(SelectorMode mode) +{ + if (mode != _mode) { + _mode = mode; + if (mode == MODE_SWATCH) { + for (auto &it : _nonsolid) { + it->hide(); + } + for (auto &swatch_widget : _swatch_widgets) { + swatch_widget->show_all(); + } + + auto icon_column = _treeview->get_column(0); + icon_column->set_title(_("Swatch")); + + _vectors->setSwatched(); + } else { + for (auto &it : _nonsolid) { + it->show_all(); + } + for (auto &swatch_widget : _swatch_widgets) { + swatch_widget->hide(); + } + auto icon_column = _treeview->get_column(0); + icon_column->set_title(_("Gradient")); + } + } +} + +void GradientSelector::setUnits(SPGradientUnits units) { _gradientUnits = units; } + +SPGradientUnits GradientSelector::getUnits() { return _gradientUnits; } + +SPGradientSpread GradientSelector::getSpread() { return _gradientSpread; } + +void GradientSelector::onGradientRename(const Glib::ustring &path_string, const Glib::ustring &new_text) +{ + Gtk::TreePath path(path_string); + auto 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, _("Rename gradient"), INKSCAPE_ICON("color-gradient")); + } + } + } + } +} + +void GradientSelector::onTreeColorColClick() +{ + auto column = _treeview->get_column(0); + column->set_sort_column(_columns->color); +} + +void GradientSelector::onTreeNameColClick() +{ + auto column = _treeview->get_column(1); + column->set_sort_column(_columns->name); +} + + +void GradientSelector::onTreeCountColClick() +{ + auto column = _treeview->get_column(2); + column->set_sort_column(_columns->refcount); +} + +void GradientSelector::moveSelection(int amount, bool down, bool toEnd) +{ + auto 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 GradientSelector::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 GradientSelector::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 auto sel = _treeview->get_selection(); + if (!sel) { + return; + } + + SPGradient *obj = nullptr; + /* Single selection */ + auto iter = sel->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + obj = row[_columns->data]; + } + + if (obj) { + vector_set(obj); + } + + check_del_button(); +} + +void GradientSelector::check_del_button() { + const auto sel = _treeview->get_selection(); + if (!sel) { + return; + } + + SPGradient *obj = nullptr; + /* Single selection */ + auto iter = sel->get_selected(); + if (iter) { + Gtk::TreeModel::Row row = *iter; + obj = row[_columns->data]; + } + if (_del2) { + _del2->set_sensitive(obj && sp_get_gradient_refcount(obj->document, obj) < 2 && _store->children().size() > 1); + } +} + +bool GradientSelector::_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); + auto select = _treeview->get_selection(); + bool wasBlocked = _blocked; + _blocked = true; + select->select(iter); + _blocked = wasBlocked; + found = true; + } + + return found; +} + +void GradientSelector::selectGradientInTree(SPGradient *vector) +{ + _store->foreach (sigc::bind<SPGradient *>(sigc::mem_fun(*this, &GradientSelector::_checkForSelected), vector)); +} + +void GradientSelector::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; + } + + _vectors->set_gradient(doc, vector); + + selectGradientInTree(vector); + + if (vector) { + if ((_mode == MODE_SWATCH) && vector->isSwatch()) { + if (vector->isSolid()) { + for (auto &it : _nonsolid) { + it->hide(); + } + } else { + for (auto &it : _nonsolid) { + it->show_all(); + } + } + } else if (_mode != MODE_SWATCH) { + + for (auto &swatch_widget : _swatch_widgets) { + swatch_widget->hide(); + } + for (auto &it : _nonsolid) { + it->show_all(); + } + } + + if (_edit) { + _edit->set_sensitive(true); + } + if (_add) { + _add->set_sensitive(true); + } + if (_del) { + _del->set_sensitive(true); + } + check_del_button(); + } else { + if (_edit) { + _edit->set_sensitive(false); + } + if (_add) { + _add->set_sensitive(doc != nullptr); + } + if (_del) { + _del->set_sensitive(false); + } + if (_del2) { + _del2->set_sensitive(false); + } + } +} + +SPGradient *GradientSelector::getVector() +{ + return _vectors->get_gradient(); +} + + +void GradientSelector::vector_set(SPGradient *gr) +{ + if (!_blocked) { + _blocked = true; + gr = sp_gradient_ensure_vector_normalized(gr); + setVector((gr) ? gr->document : nullptr, gr); + _signal_changed.emit(gr); + _blocked = false; + } +} + +void GradientSelector::delete_vector_clicked_2() { + const auto selection = _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[_columns->data]; + } + + if (obj) { + if (auto repr = obj->getRepr()) { + repr->setAttribute("inkscape:collect", "always"); + + auto move = iter; + --move; + if (!move) { + move = iter; + ++move; + } + if (move) { + selection->select(move); + _treeview->scroll_to_row(_store->get_path(move), 0.5); + } + } + } +} + +void GradientSelector::delete_vector_clicked() +{ + const auto selection = _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[_columns->data]; + } + + if (obj) { + std::string id = obj->getId(); + sp_gradient_unset_swatch(SP_ACTIVE_DESKTOP, id); + } +} + +void GradientSelector::edit_vector_clicked() +{ + // Invoke the gradient tool.... never actually called as button is hidden in only use! + set_active_tool(SP_ACTIVE_DESKTOP, "Gradient"); +} + +void GradientSelector::add_vector_clicked() +{ + auto doc = _vectors->get_document(); + + if (!doc) + return; + + auto gr = _vectors->get_gradient(); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); + + Inkscape::XML::Node *repr = nullptr; + + if (gr) { + gr->getRepr()->removeAttribute("inkscape:collect"); + repr = gr->getRepr()->duplicate(xml_doc); + // Rename the new gradients id to be similar to the cloned gradients + auto new_id = generate_unique_id(doc, gr->getId()); + gr->setAttribute("id", new_id.c_str()); + 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)); + } + + _vectors->set_gradient(doc, gr); + + selectGradientInTree(gr); + + // assign gradient to selection + vector_set(gr); + + Inkscape::GC::release(repr); +} + +void GradientSelector::show_edit_button(bool show) { + if (show) _edit->show(); else _edit->hide(); +} + +void GradientSelector::set_name_col_size(int min_width) { + auto name_column = _treeview->get_column(1); + name_column->set_min_width(min_width); +} + +void GradientSelector::set_gradient_size(int width, int height) { + _vectors->set_pixmap_size(width, height); +} + +} // namespace Widget +} // namespace UI +} // namespace Inkscape + +/* + 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 : |