summaryrefslogtreecommitdiffstats
path: root/src/ui/dialog/livepatheffect-editor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/dialog/livepatheffect-editor.cpp')
-rw-r--r--src/ui/dialog/livepatheffect-editor.cpp1258
1 files changed, 1258 insertions, 0 deletions
diff --git a/src/ui/dialog/livepatheffect-editor.cpp b/src/ui/dialog/livepatheffect-editor.cpp
new file mode 100644
index 0000000..51e7437
--- /dev/null
+++ b/src/ui/dialog/livepatheffect-editor.cpp
@@ -0,0 +1,1258 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * @brief A dialog for Live Path Effects (LPE)
+ */
+/* Authors:
+ * Jabiertxof
+ * Adam Belis (UX/Design)
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "livepatheffect-editor.h"
+#include "live_effects/effect-enum.h"
+#include "livepatheffect-add.h"
+#include "live_effects/effect.h"
+#include "live_effects/lpeobject-reference.h"
+#include "live_effects/lpeobject.h"
+
+#include "object/sp-lpe-item.h"
+#include "svg/svg.h"
+#include "ui/icon-names.h"
+#include "ui/icon-loader.h"
+#include "ui/builder-utils.h"
+#include "io/resource.h"
+#include "object/sp-use.h"
+#include "object/sp-shape.h"
+#include "object/sp-path.h"
+#include "object/sp-flowtext.h"
+#include "object/sp-tspan.h"
+#include "object/sp-item-group.h"
+#include "object/sp-text.h"
+#include "ui/tools/node-tool.h"
+#include "ui/widget/custom-tooltip.h"
+#include "util/optstr.h"
+#include <cstddef>
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+namespace UI {
+
+
+bool sp_can_apply_lpeffect(SPLPEItem* item, LivePathEffect::EffectType etype) {
+ if (!item) return false;
+
+ auto shape = cast<SPShape>(item);
+ auto path = cast<SPPath>(item);
+ auto group = cast<SPGroup>(item);
+ Glib::ustring item_type;
+ if (group) {
+ item_type = "group";
+ } else if (path) {
+ item_type = "path";
+ } else if (shape) {
+ item_type = "shape";
+ }
+ bool has_clip = item->getClipObject() != nullptr;
+ bool has_mask = item->getMaskObject() != nullptr;
+ bool applicable = true;
+ if (!has_clip && etype == LivePathEffect::POWERCLIP) {
+ applicable = false;
+ }
+ if (!has_mask && etype == LivePathEffect::POWERMASK) {
+ applicable = false;
+ }
+ if (item_type == "group" && !Inkscape::LivePathEffect::LPETypeConverter.get_on_group(etype)) {
+ applicable = false;
+ } else if (item_type == "shape" && !Inkscape::LivePathEffect::LPETypeConverter.get_on_shape(etype)) {
+ applicable = false;
+ } else if (item_type == "path" && !Inkscape::LivePathEffect::LPETypeConverter.get_on_path(etype)) {
+ applicable = false;
+ }
+ return applicable;
+}
+
+void sp_apply_lpeffect(SPDesktop* desktop, SPLPEItem* item, LivePathEffect::EffectType etype) {
+ if (!sp_can_apply_lpeffect(item, etype)) return;
+
+ Glib::ustring key = Inkscape::LivePathEffect::LPETypeConverter.get_key(etype);
+ LivePathEffect::Effect::createAndApply(key.c_str(), item->document, item);
+ item->getCurrentLPE()->refresh_widgets = true;
+ DocumentUndo::done(item->document, _("Create and apply path effect"), INKSCAPE_ICON("dialog-path-effects"));
+
+ if (desktop) {
+ // this is rotten - UI LPE knots refresh
+ // force selection change
+ desktop->getSelection()->clear();
+ desktop->getSelection()->add(item);
+ Inkscape::UI::Tools::sp_update_helperpath(desktop);
+ }
+}
+
+namespace Dialog {
+
+/*####################
+ * Callback functions
+ */
+
+bool sp_has_fav(Glib::ustring effect)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring favlist = prefs->getString("/dialogs/livepatheffect/favs");
+ size_t pos = favlist.find(effect);
+ if (pos != Glib::ustring::npos) {
+ return true;
+ }
+ return false;
+}
+
+void sp_add_fav(Glib::ustring effect)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring favlist = prefs->getString("/dialogs/livepatheffect/favs");
+ if (!sp_has_fav(effect)) {
+ prefs->setString("/dialogs/livepatheffect/favs", favlist + effect + ";");
+ }
+}
+
+void sp_remove_fav(Glib::ustring effect)
+{
+ if (sp_has_fav(effect)) {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring favlist = prefs->getString("/dialogs/livepatheffect/favs");
+ effect += ";";
+ size_t pos = favlist.find(effect);
+ if (pos != Glib::ustring::npos) {
+ favlist.erase(pos, effect.length());
+ prefs->setString("/dialogs/livepatheffect/favs", favlist);
+ }
+ }
+}
+
+void sp_toggle_fav(Glib::ustring effect, Gtk::MenuItem *LPEtoggleFavorite)
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring favlist = prefs->getString("/dialogs/livepatheffect/favs");
+ if (sp_has_fav(effect)) {
+ sp_remove_fav(effect);
+ LPEtoggleFavorite->set_label(_("Set Favorite"));
+ } else {
+ sp_add_fav(effect);
+ LPEtoggleFavorite->set_label(_("Unset Favorite"));
+ }
+}
+
+void LivePathEffectEditor::selectionChanged(Inkscape::Selection * selection)
+{
+ if (selection_changed_lock) {
+ return;
+ }
+ onSelectionChanged(selection);
+ clearMenu();
+}
+void LivePathEffectEditor::selectionModified(Inkscape::Selection * selection, guint flags)
+{
+ current_lpeitem = cast<SPLPEItem>(selection->singleItem());
+ if (!selection_changed_lock && current_lpeitem && effectlist != current_lpeitem->getEffectList()) {
+ onSelectionChanged(selection);
+ } else if (current_lpeitem && current_lperef.first) {
+ showParams(current_lperef, false);
+ }
+ clearMenu();
+}
+
+/**
+ * Constructor
+ */
+LivePathEffectEditor::LivePathEffectEditor()
+ : DialogBase("/dialogs/livepatheffect", "LivePathEffect"),
+ _builder(create_builder("dialog-livepatheffect.glade")),
+ LPEListBox(get_widget<Gtk::ListBox>(_builder, "LPEListBox")),
+ _LPEContainer(get_widget<Gtk::Box>(_builder, "LPEContainer")),
+ _LPEAddContainer(get_widget<Gtk::Box>(_builder, "LPEAddContainer")),
+ _LPEParentBox(get_widget<Gtk::ListBox>(_builder, "LPEParentBox")),
+ _LPECurrentItem(get_widget<Gtk::Box>(_builder, "LPECurrentItem")),
+ _LPESelectionInfo(get_widget<Gtk::Label>(_builder, "LPESelectionInfo")),
+ _LPEGallery(get_widget<Gtk::Button>(_builder, "LPEGallery")),
+ _showgallery_observer(Preferences::PreferencesObserver::create(
+ "/dialogs/livepatheffect/showgallery", sigc::mem_fun(*this, &LivePathEffectEditor::on_showgallery_notify))),
+ converter(Inkscape::LivePathEffect::LPETypeConverter)
+{
+ _LPEGallery.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onAddGallery));
+ _showgallery_observer->call(); // Set initial visibility per Preference (widget is :no-show-all)
+
+ Glib::RefPtr<Gtk::EntryCompletion> LPECompletionList = Glib::RefPtr<Gtk::EntryCompletion>::cast_dynamic(_builder->get_object("LPECompletionList"));
+
+ _LPEContainer.signal_map().connect(sigc::mem_fun(*this, &LivePathEffectEditor::map_handler) );
+ _LPEContainer.signal_button_press_event().connect([=](GdkEventButton* const evt){dnd = false; /*hack to fix dnd freze expander*/ return false; }, false);
+ setMenu();
+ add(_LPEContainer);
+ selection_info();
+ _lpes_popup.get_entry().set_placeholder_text(_("Add Live Path Effect"));
+ _lpes_popup.on_match_selected().connect([=](int id){ onAdd((LivePathEffect::EffectType)id); });
+ _lpes_popup.on_button_press().connect([=](){ setMenu(); });
+ _lpes_popup.on_focus().connect([=](){ setMenu(); return true; });
+ _LPEAddContainer.pack_start(_lpes_popup);
+ show_all();
+}
+
+LivePathEffectEditor::~LivePathEffectEditor()
+{
+ sp_clear_custom_tooltip();
+}
+
+bool separator_func(const Glib::RefPtr<Gtk::TreeModel>& model,
+ const Gtk::TreeModel::iterator& iter) {
+ Gtk::TreeModel::Row row = *iter;
+ bool *separator;
+ row->get_value(3, separator);
+ return separator;
+}
+
+bool
+LivePathEffectEditor::is_appliable(LivePathEffect::EffectType etype, Glib::ustring item_type, bool has_clip, bool has_mask) {
+ bool appliable = true;
+
+ if (!has_clip && etype == LivePathEffect::POWERCLIP) {
+ appliable = false;
+ }
+ if (!has_mask && etype == LivePathEffect::POWERMASK) {
+ appliable = false;
+ }
+ if (item_type == "group" && !converter.get_on_group(etype)) {
+ appliable = false;
+ } else if (item_type == "shape" && !converter.get_on_shape(etype)) {
+ appliable = false;
+ } else if (item_type == "path" && !converter.get_on_path(etype)) {
+ appliable = false;
+ }
+ return appliable;
+}
+
+void align(Gtk::Widget* top, gint spinbutton_width_chars) {
+ auto box = dynamic_cast<Gtk::Box*>(top);
+ if (!box) return;
+ box->set_spacing(2);
+
+ // traverse container, locate n-th child in each row
+ auto for_child_n = [=](int child_index, const std::function<void (Gtk::Widget*)>& action) {
+ for (auto child : box->get_children()) {
+ auto container = dynamic_cast<Gtk::Box*>(child);
+ if (!container) continue;
+ container->set_spacing(2);
+ const auto& children = container->get_children();
+ if (children.size() > child_index) {
+ action(children[child_index]);
+ }
+ }
+ };
+
+ // column 0 - labels
+ int max_width = 0;
+ for_child_n(0, [&](Gtk::Widget* child){
+ if (auto label = dynamic_cast<Gtk::Label*>(child)) {
+ label->set_xalign(0); // left-align
+ int label_width = 0, dummy = 0;
+ label->get_preferred_width(dummy, label_width);
+ if (label_width > max_width) {
+ max_width = label_width;
+ }
+ }
+ });
+ // align
+ for_child_n(0, [=](Gtk::Widget* child) {
+ if (auto label = dynamic_cast<Gtk::Label*>(child)) {
+ label->set_size_request(max_width);
+ }
+ });
+
+ // column 1 - align spin buttons, if any
+ int button_width = 0;
+ for_child_n(1, [&](Gtk::Widget* child) {
+ if (auto spin = dynamic_cast<Gtk::SpinButton*>(child)) {
+ // selected spinbutton size by each LPE default 7
+ spin->set_width_chars(spinbutton_width_chars);
+ int dummy = 0;
+ spin->get_preferred_width(dummy, button_width);
+ }
+ });
+ // set min size for comboboxes, if any
+ int combo_size = button_width > 0 ? button_width : 50; // match with spinbuttons, or just min of 50px
+ for_child_n(1, [=](Gtk::Widget* child) {
+ if (auto combo = dynamic_cast<Gtk::ComboBox*>(child)) {
+ combo->set_size_request(combo_size);
+ }
+ });
+}
+
+void
+LivePathEffectEditor::clearMenu()
+{
+ sp_clear_custom_tooltip();
+ _reload_menu = true;
+}
+
+void
+LivePathEffectEditor::toggleVisible(Inkscape::LivePathEffect::Effect *lpe , Gtk::EventBox *visbutton) {
+ auto *visimage = dynamic_cast<Gtk::Image *>(dynamic_cast<Gtk::Button *>(visbutton->get_children()[0])->get_image());
+ bool hide = false;
+ if (!g_strcmp0(lpe->getRepr()->attribute("is_visible"),"true")) {
+ visimage->set_from_icon_name("object-hidden-symbolic", Gtk::IconSize(Gtk::ICON_SIZE_SMALL_TOOLBAR));
+ lpe->getRepr()->setAttribute("is_visible", "false");
+ hide = true;
+ } else {
+ visimage->set_from_icon_name("object-visible-symbolic", Gtk::IconSize(Gtk::ICON_SIZE_SMALL_TOOLBAR));
+ lpe->getRepr()->setAttribute("is_visible", "true");
+ }
+ lpe->doOnVisibilityToggled(current_lpeitem);
+ DocumentUndo::done(getDocument(), hide ? _("Deactivate path effect") : _("Activate path effect"), INKSCAPE_ICON("dialog-path-effects"));
+}
+
+const Glib::ustring& get_category_name(Inkscape::LivePathEffect::LPECategory category) {
+ static const std::map<Inkscape::LivePathEffect::LPECategory, Glib::ustring> category_names = {
+ { Inkscape::LivePathEffect::LPECategory::Favorites, _("Favorites") },
+ { Inkscape::LivePathEffect::LPECategory::EditTools, _("Edit/Tools") },
+ { Inkscape::LivePathEffect::LPECategory::Distort, _("Distort") },
+ { Inkscape::LivePathEffect::LPECategory::Generate, _("Generate") },
+ { Inkscape::LivePathEffect::LPECategory::Convert, _("Convert") },
+ { Inkscape::LivePathEffect::LPECategory::Experimental, _("Experimental") },
+ };
+ return category_names.at(category);
+}
+
+struct LPEMetadata {
+ Inkscape::LivePathEffect::LPECategory category;
+ Glib::ustring icon_name;
+ Glib::ustring tooltip;
+ bool sensitive;
+};
+
+static std::map<Inkscape::LivePathEffect::EffectType, LPEMetadata> g_lpes;
+// populate popup with lpes and completion list for a search box
+void LivePathEffectEditor::add_lpes(Inkscape::UI::Widget::CompletionPopup& popup, bool symbolic) {
+ auto& menu = popup.get_menu();
+ struct LPE {
+ Inkscape::LivePathEffect::EffectType type;
+ Glib::ustring label;
+ Inkscape::LivePathEffect::LPECategory category;
+ Glib::ustring icon_name;
+ Glib::ustring tooltip;
+ bool sensitive;
+ };
+ std::vector<LPE> lpes;
+ lpes.reserve(g_lpes.size());
+ for (auto&& lpe : g_lpes) {
+ lpes.push_back({
+ lpe.first,
+ g_dpgettext2(0, "path effect", converter.get_label(lpe.first).c_str()),
+ lpe.second.category,
+ lpe.second.icon_name,
+ lpe.second.tooltip,
+ lpe.second.sensitive
+ });
+ }
+ std::sort(begin(lpes), end(lpes), [=](auto&& a, auto&& b) {
+ if (a.category != b.category) {
+ return a.category < b.category;
+ }
+ return a.label < b.label;
+ });
+
+ popup.clear_completion_list();
+
+ // 2-column menu
+ for (auto w:menu.get_children()) {
+ menu.remove(*w);
+ }
+ Inkscape::UI::ColumnMenuBuilder<Inkscape::LivePathEffect::LPECategory> builder(menu, 3, Gtk::ICON_SIZE_LARGE_TOOLBAR);
+
+ for (auto& lpe : lpes) {
+ // build popup menu
+ auto type = lpe.type;
+ auto *menuitem = builder.add_item(lpe.label, lpe.category, lpe.tooltip, lpe.icon_name, lpe.sensitive, true, [=](){ onAdd((LivePathEffect::EffectType)type); });
+ gint id = (gint)type;
+ menuitem->property_has_tooltip() = true;
+ menuitem->signal_query_tooltip().connect([=](int x, int y, bool kbd, const Glib::RefPtr<Gtk::Tooltip>& tooltipw){
+ return sp_query_custom_tooltip(x, y, kbd, tooltipw, id, lpe.tooltip, lpe.icon_name);
+ });
+ if (builder.new_section()) {
+ builder.set_section(get_category_name(lpe.category));
+ }
+
+ // build completion list
+ if (lpe.sensitive) {
+ popup.add_to_completion_list(static_cast<int>(lpe.type), lpe.label, lpe.icon_name + (symbolic ? "-symbolic" : ""));
+ }
+ }
+
+ if (symbolic) {
+ menu.get_style_context()->add_class("symbolic");
+ }
+}
+
+
+void
+LivePathEffectEditor::setMenu()
+{
+ if (!_reload_menu) {
+ return;
+ }
+ auto shape = cast<SPShape>(current_lpeitem);
+ auto path = cast<SPPath>(current_lpeitem);
+ auto group = cast<SPGroup>(current_lpeitem);
+ bool has_clip = current_lpeitem && (current_lpeitem->getClipObject() != nullptr);
+ bool has_mask = current_lpeitem && (current_lpeitem->getMaskObject() != nullptr);
+ Glib::ustring item_type = "";
+ if (group) {
+ item_type = "group";
+ } else if (path) {
+ item_type = "path";
+ } else if (shape) {
+ item_type = "shape";
+ }
+ if (_item_type != item_type || has_clip != _has_clip || has_mask != _has_mask) {
+ _item_type = item_type;
+ _has_clip = has_clip;
+ _has_mask = has_mask;
+ g_lpes.clear();
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool experimental = prefs->getBool("/dialogs/livepatheffect/showexperimental", false);
+ std::map<Inkscape::LivePathEffect::LPECategory, std::map< Glib::ustring, const LivePathEffect::EnumEffectData<LivePathEffect::EffectType> *> > lpesorted;
+ for (int i = 0; i < static_cast<int>(converter._length); ++i) {
+ const LivePathEffect::EnumEffectData<LivePathEffect::EffectType> *data = &converter.data(i);
+ const Glib::ustring label = _(converter.get_label(data->id).c_str());
+ const Glib::ustring untranslated_label = converter.get_label(data->id);
+ Glib::ustring name = label;
+ if (untranslated_label != label) {
+ name =+ "\n<span size='x-small'>" + untranslated_label + "</span>";
+ }
+ Inkscape::LivePathEffect::LPECategory category = converter.get_category(data->id);
+ if (sp_has_fav(untranslated_label)) {
+ //category = 0;
+ category = Inkscape::LivePathEffect::LPECategory::Favorites;
+ }
+ if (!experimental && category == Inkscape::LivePathEffect::LPECategory::Experimental) {
+ continue;
+ }
+ lpesorted[category][name] = data;
+ }
+ for (auto e : lpesorted) {
+ for (auto e2 : e.second) {
+ const Glib::ustring label = _(converter.get_label(e2.second->id).c_str());
+ const Glib::ustring untranslated_label = converter.get_label(e2.second->id);
+ Glib::ustring tooltip = _(converter.get_description(e2.second->id).c_str());
+ if (untranslated_label != label) {
+ tooltip = "[" + untranslated_label + "] " + _(converter.get_description(e2.second->id).c_str());
+ }
+ Glib::ustring name = label;
+ Glib::ustring icon = converter.get_icon(e2.second->id);
+ LPEMetadata mdata;
+ mdata.category = e.first;
+ mdata.icon_name = icon;
+ mdata.tooltip = tooltip;
+ mdata.sensitive = is_appliable(e2.second->id, item_type, has_clip, has_mask);
+ g_lpes[e2.second->id] = mdata;
+ }
+ }
+ auto symbolic = Inkscape::Preferences::get()->getBool("/theme/symbolicIcons", true);
+ add_lpes(_lpes_popup, symbolic);
+ }
+}
+
+void LivePathEffectEditor::onAdd(LivePathEffect::EffectType etype)
+{
+ selection_changed_lock = true;
+ Glib::ustring key = converter.get_key(etype);
+ SPLPEItem *fromclone = clonetolpeitem();
+ if (fromclone) {
+ current_lpeitem = fromclone;
+ if (key == "clone_original") {
+ current_lpeitem->getCurrentLPE()->refresh_widgets = true;
+ selection_changed_lock = false;
+ DocumentUndo::done(getDocument(), _("Create and apply path effect"), INKSCAPE_ICON("dialog-path-effects"));
+ return;
+ }
+ }
+ selection_changed_lock = false;
+ if (current_lpeitem) {
+ LivePathEffect::Effect::createAndApply(key.c_str(), getDocument(), current_lpeitem);
+ current_lpeitem->getCurrentLPE()->refresh_widgets = true;
+ DocumentUndo::done(getDocument(), _("Create and apply path effect"), INKSCAPE_ICON("dialog-path-effects"));
+ }
+}
+
+void
+LivePathEffectEditor::map_handler()
+{
+ ensure_size();
+}
+
+void
+LivePathEffectEditor::selection_info()
+{
+ auto selection = getSelection();
+ SPItem * selected = nullptr;
+ _LPESelectionInfo.hide();
+ if (selection && (selected = selection->singleItem()) ) {
+ if (is<SPText>(selected) || is<SPFlowtext>(selected)) {
+ _LPESelectionInfo.set_text(_("Text objects do not support Live Path Effects"));
+ _LPESelectionInfo.show();
+ Glib::ustring labeltext = _("Convert text to paths");
+ Gtk::Button *selectbutton = Gtk::manage(new Gtk::Button());
+ Gtk::Box *boxc = Gtk::manage(new Gtk::Box());
+ Gtk::Label *lbl = Gtk::manage(new Gtk::Label(labeltext));
+ std::string shape_type = "group";
+ std::string highlight = SPColor(selected->highlight_color()).toString();
+ Gtk::Image *type = Gtk::manage(new Gtk::Image(sp_get_shape_icon(shape_type, Gdk::RGBA(highlight),20, 1)));
+ boxc->pack_start(*type, false, false);
+ boxc->pack_start(*lbl, false, false);
+ type->set_margin_start(4);
+ type->set_margin_end(4);
+ selectbutton->add(*boxc);
+ selectbutton->signal_clicked().connect([=](){
+ selection->toCurves();
+ });
+ _LPEParentBox.add(*selectbutton);
+ Glib::ustring labeltext2 = _("Clone");
+ Gtk::Button *selectbutton2 = Gtk::manage(new Gtk::Button());
+ Gtk::Box *boxc2 = Gtk::manage(new Gtk::Box());
+ Gtk::Label *lbl2 = Gtk::manage(new Gtk::Label(labeltext2));
+ std::string shape_type2 = "clone";
+ std::string highlight2 = SPColor(selected->highlight_color()).toString();
+ Gtk::Image *type2 = Gtk::manage(new Gtk::Image(sp_get_shape_icon(shape_type2, Gdk::RGBA(highlight2),20, 1)));
+ boxc2->pack_start(*type2, false, false);
+ boxc2->pack_start(*lbl2, false, false);
+ type2->set_margin_start(4);
+ type2->set_margin_end(4);
+ selectbutton2->add(*boxc2);
+ selectbutton2->signal_clicked().connect([=](){
+ selection->clone();;
+ });
+ _LPEParentBox.add(*selectbutton2);
+ _LPEParentBox.show_all();
+ } else if (!is<SPLPEItem>(selected) && !is<SPUse>(selected)) {
+ _LPESelectionInfo.set_text(_("Select a path, shape, clone or group"));
+ _LPESelectionInfo.show();
+ } else {
+ if (selected->getId()) {
+ Glib::ustring labeltext = selected->label() ? selected->label() : selected->getId();
+ Gtk::Box *boxc = Gtk::manage(new Gtk::Box());
+ Gtk::Label *lbl = Gtk::manage(new Gtk::Label(labeltext));
+ lbl->set_ellipsize(Pango::ELLIPSIZE_END);
+ std::string shape_type = selected->typeName();
+ std::string highlight = SPColor(selected->highlight_color()).toString();
+ Gtk::Image *type = Gtk::manage(new Gtk::Image(sp_get_shape_icon(shape_type, Gdk::RGBA(highlight),20, 1)));
+ boxc->pack_start(*type, false, false);
+ boxc->pack_start(*lbl, false, false);
+ _LPECurrentItem.add(*boxc);
+ _LPECurrentItem.get_children()[0]->set_halign(Gtk::ALIGN_CENTER);
+ _LPESelectionInfo.hide();
+ }
+ std::vector<std::pair <Glib::ustring, Glib::ustring> > newrootsatellites;
+ for (auto root : selected->rootsatellites) {
+ auto lpeobj = cast<LivePathEffectObject>(selected->document->getObjectById(root.second));
+ Inkscape::LivePathEffect::Effect *lpe = nullptr;
+ if (lpeobj) {
+ lpe = lpeobj->get_lpe();
+ }
+ if (lpe) {
+ const Glib::ustring label = _(converter.get_label(lpe->effectType()).c_str());
+ Glib::ustring labeltext = Glib::ustring::compose(_("Select %1 with %2 LPE"), root.first, label);
+ auto lpeitem = cast<SPLPEItem>(selected->document->getObjectById(root.first));
+ if (lpeitem && lpeitem->getLPEIndex(lpe) != Glib::ustring::npos) {
+ newrootsatellites.emplace_back(root.first, root.second);
+ Gtk::Button *selectbutton = Gtk::manage(new Gtk::Button());
+ Gtk::Box *boxc = Gtk::manage(new Gtk::Box());
+ Gtk::Label *lbl = Gtk::manage(new Gtk::Label(labeltext));
+ std::string shape_type = selected->typeName();
+ std::string highlight = SPColor(selected->highlight_color()).toString();
+ Gtk::Image *type = Gtk::manage(new Gtk::Image(sp_get_shape_icon(shape_type, Gdk::RGBA(highlight),20, 1)));
+ boxc->pack_start(*type, false, false);
+ boxc->pack_start(*lbl, false, false);
+ type->set_margin_start(4);
+ type->set_margin_end(4);
+ selectbutton->add(*boxc);
+ selectbutton->signal_clicked().connect([=](){
+ selection->set(lpeitem);
+ });
+ _LPEParentBox.add(*selectbutton);
+ }
+ }
+ }
+ selected->rootsatellites = newrootsatellites;
+ _LPEParentBox.show_all();
+ _LPEParentBox.drag_dest_unset();
+ _LPECurrentItem.show_all();
+ }
+ } else if (!selection || selection->isEmpty()) {
+ _LPESelectionInfo.set_text(_("Select a path, shape, clone or group"));
+ _LPESelectionInfo.show();
+ } else if (selection->size() > 1) {
+ _LPESelectionInfo.set_text(_("Select only one path, shape, clone or group"));
+ _LPESelectionInfo.show();
+ }
+}
+
+void
+LivePathEffectEditor::onSelectionChanged(Inkscape::Selection *sel)
+{
+ SPUse *use = nullptr;
+ _reload_menu = true;
+ if ( sel && !sel->isEmpty() ) {
+ SPItem *item = sel->singleItem();
+ if ( item ) {
+ auto lpeitem = cast<SPLPEItem>(item);
+ use = cast<SPUse>(item);
+ if (lpeitem) {
+ lpeitem->update_satellites();
+ current_lpeitem = lpeitem;
+ _LPEAddContainer.set_sensitive(true);
+ effect_list_reload(lpeitem);
+ return;
+ }
+ }
+ }
+ current_lpeitem = nullptr;
+ _LPEAddContainer.set_sensitive(use != nullptr);
+ clear_lpe_list();
+ selection_info();
+}
+
+void
+LivePathEffectEditor::move_list(gint origin, gint dest)
+{
+ Inkscape::Selection *sel = getDesktop()->getSelection();
+
+ if ( sel && !sel->isEmpty() ) {
+ SPItem *item = sel->singleItem();
+ if ( item ) {
+ auto lpeitem = cast<SPLPEItem>(item);
+ if ( lpeitem ) {
+ lpeitem->movePathEffect(origin, dest);
+ }
+ }
+ }
+}
+
+static const std::vector<Gtk::TargetEntry> entries = {Gtk::TargetEntry("GTK_LIST_BOX_ROW", Gtk::TARGET_SAME_APP, 0 )};
+
+void
+LivePathEffectEditor::showParams(std::pair<Gtk::Expander *, std::shared_ptr<Inkscape::LivePathEffect::LPEObjectReference> > expanderdata, bool changed)
+{
+ LivePathEffectObject *lpeobj = expanderdata.second->lpeobject;
+
+ if (lpeobj) {
+ Inkscape::LivePathEffect::Effect *lpe = lpeobj->get_lpe();
+ if (lpe) {
+ if (effectwidget && !lpe->refresh_widgets && expanderdata == current_lperef && !changed) {
+ return;
+ }
+ if (effectwidget) {
+ effectwidget->get_parent()->remove(*effectwidget);
+ delete effectwidget;
+ effectwidget = nullptr;
+ }
+ effectwidget = lpe->newWidget();
+ if (!dynamic_cast<Gtk::Container *>(effectwidget)->get_children().size()) {
+ auto * label = new Gtk::Label("", Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
+ label->set_markup(_("<small>Without parameters</small>"));
+ label->set_margin_top(5);
+ label->set_margin_bottom(5);
+ label->set_margin_start(5);
+ effectwidget = label;
+ }
+ expanderdata.first->add(*effectwidget);
+ expanderdata.first->show_all_children();
+ align(effectwidget, lpe->spinbutton_width_chars);
+ // fixme: add resizing of dialog
+ lpe->refresh_widgets = false;
+ ensure_size();
+ } else {
+ current_lperef = std::make_pair(nullptr, nullptr);
+ }
+ } else {
+ current_lperef = std::make_pair(nullptr, nullptr);
+ }
+
+ // effectwidget = effect.newWidget();
+ // effectcontrol_frame.set_label(effect.getName());
+ // effectcontrol_vbox.pack_start(*effectwidget, true, true);
+
+ // button_remove.show();
+ // status_label.hide();
+ // effectcontrol_vbox.show_all_children();
+ // align(effectwidget);
+ // effectcontrol_frame.show();
+ // // fixme: add resizing of dialog
+ // effect.refresh_widgets = false;
+}
+
+bool
+LivePathEffectEditor::closeExpander(GdkEventButton * evt) {
+ current_lperef.first->set_expanded(false);
+ return false;
+}
+
+/*
+ * First clears the effectlist_store, then appends all effects from the effectlist.
+ */
+void
+LivePathEffectEditor::effect_list_reload(SPLPEItem *lpeitem)
+{
+ clear_lpe_list();
+ _LPEExpanders.clear();
+ auto gladefile = get_filename_string(Inkscape::IO::Resource::UIS, "dialog-livepatheffect-item.glade");
+ gint counter = -1;
+ Gtk::Expander *LPEExpanderCurrent = nullptr;
+ effectlist = lpeitem->getEffectList();
+ gint total = effectlist.size();
+ if (total > 1) {
+ _LPECurrentItem.drag_dest_unset();
+ _lpes_popup.drag_dest_unset();
+ _lpes_popup.get_entry().drag_dest_unset();
+ _LPEAddContainer.drag_dest_unset();
+ _LPEContainer.drag_dest_set(entries, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_MOVE);
+ _LPEContainer.signal_drag_data_received().connect([=](const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& selection_data, guint info, guint time)
+ {
+ if (dnd) {
+ unsigned int pos_target, pos_source;
+ Gtk::Widget *target = &_LPEContainer;
+ pos_source = atoi(reinterpret_cast<char const*>(selection_data.get_data()));
+ pos_target = LPEListBox.get_children().size()-1;
+ if (y < 90) {
+ pos_target = 0;
+ }
+ if (pos_target == pos_source) {
+ gtk_drag_finish(context->gobj(), FALSE, FALSE, time);
+ dnd = false;
+ return;
+ }
+ Glib::RefPtr<Gtk::StyleContext> stylec = target->get_style_context();
+ if (pos_source > pos_target) {
+ if (stylec->has_class("after")) {
+ pos_target ++;
+ }
+ } else if (pos_source < pos_target) {
+ if (stylec->has_class("before")) {
+ pos_target --;
+ }
+ }
+ Gtk::Widget *source = LPEListBox.get_row_at_index(pos_source);
+ g_object_ref(source->gobj());
+ LPEListBox.remove(*source);
+ LPEListBox.insert(*source, pos_target);
+ g_object_unref(source->gobj());
+ move_list(pos_source,pos_target);
+ gtk_drag_finish(context->gobj(), TRUE, TRUE, time);
+ dnd = false;
+ }
+ });
+ _LPEContainer.signal_drag_motion().connect([=](const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time)
+ {
+ Glib::RefPtr<Gtk::StyleContext> stylec = _LPEContainer.get_style_context();
+ if (y < 90) {
+ stylec->add_class("before");
+ stylec->remove_class("after");
+ } else {
+ stylec->remove_class("before");
+ stylec->add_class("after");
+ }
+ return true;
+ }, true);
+ }
+ PathEffectList::iterator it;
+ Gtk::MenuItem *LPEMoveUpExtrem = nullptr;
+ Gtk::MenuItem *LPEMoveDownExtrem = nullptr;
+ Gtk::EventBox *LPEDrag = nullptr;
+ for( it = effectlist.begin() ; it!=effectlist.end(); ++it)
+ {
+ if ( !(*it)->lpeobject ) {
+ continue;
+ }
+ auto lpe = (*it)->lpeobject->get_lpe();
+ bool current = lpeitem->getCurrentLPE() == lpe;
+ counter++;
+ Glib::RefPtr<Gtk::Builder> builder;
+ if (lpe) {
+ try {
+ builder = Gtk::Builder::create_from_file(gladefile);
+ } catch (const Glib::Error &ex) {
+ g_warning("Glade file loading failed for path effect dialog");
+ return;
+ }
+ Gtk::Box *LPEEffect;
+ Gtk::Box *LPEExpanderBox;
+ Gtk::Box *LPEActionButtons;
+ Gtk::EventBox *LPEOpenExpander;
+ Gtk::Expander *LPEExpander;
+ Gtk::Image *LPEIconImage;
+ Gtk::EventBox *LPEErase;
+ Gtk::EventBox *LPEHide;
+ Gtk::MenuItem *LPEtoggleFavorite;
+ Gtk::Label *LPENameLabel;
+ Gtk::Menu *LPEEffectMenu;
+ Gtk::MenuItem *LPEMoveUp;
+ Gtk::MenuItem *LPEMoveDown;
+ Gtk::MenuItem *LPEResetDefault;
+ Gtk::MenuItem *LPESetDefault;
+ builder->get_widget("LPEMoveUp", LPEMoveUp);
+ builder->get_widget("LPEMoveDown", LPEMoveDown);
+ builder->get_widget("LPEResetDefault", LPEResetDefault);
+ builder->get_widget("LPESetDefault", LPESetDefault);
+ builder->get_widget("LPENameLabel", LPENameLabel);
+ builder->get_widget("LPEEffectMenu", LPEEffectMenu);
+ builder->get_widget("LPEHide", LPEHide);
+ builder->get_widget("LPEIconImage", LPEIconImage);
+ builder->get_widget("LPEExpanderBox", LPEExpanderBox);
+ builder->get_widget("LPEEffect", LPEEffect);
+ builder->get_widget("LPEExpander", LPEExpander);
+ builder->get_widget("LPEOpenExpander", LPEOpenExpander);
+ builder->get_widget("LPEErase", LPEErase);
+ builder->get_widget("LPEDrag", LPEDrag);
+ builder->get_widget("LPEActionButtons", LPEActionButtons);
+ builder->get_widget("LPEtoggleFavorite", LPEtoggleFavorite);
+ LPEExpander->drag_dest_unset();
+ LPEActionButtons->drag_dest_unset();
+ LPEMoveUp->show();
+ LPEMoveDown->show();
+ LPEDrag->get_children()[0]->show();
+ LPEDrag->set_tooltip_text(_("Drag to change position in path effects stack"));
+ if (current) {
+ LPEExpanderCurrent = LPEExpander;
+ }
+ if (counter == 0) {
+ LPEMoveUpExtrem = LPEMoveUp;
+ }
+ LPEMoveDownExtrem = LPEMoveDown;
+ auto effectype = lpe->effectType();
+ const Glib::ustring label = _(converter.get_label(effectype).c_str());
+ const Glib::ustring untranslated_label = converter.get_label(effectype);
+ const Glib::ustring icon = converter.get_icon(effectype);
+ LPEIconImage->set_from_icon_name(icon, Gtk::IconSize(Gtk::ICON_SIZE_SMALL_TOOLBAR));
+ Glib::ustring lpename = "";
+ if (untranslated_label == label) {
+ lpename = label;
+ } else {
+ lpename = (label + "\n<span size='x-small'>" + untranslated_label + "</span>");
+ }
+ auto *visimage = dynamic_cast<Gtk::Image *>(dynamic_cast<Gtk::Button *>(LPEHide->get_children()[0])->get_image());
+ if (!g_strcmp0(lpe->getRepr()->attribute("is_visible"),"true")) {
+ visimage->set_from_icon_name("object-visible-symbolic", Gtk::IconSize(Gtk::ICON_SIZE_SMALL_TOOLBAR));
+ } else {
+ visimage->set_from_icon_name("object-hidden-symbolic", Gtk::IconSize(Gtk::ICON_SIZE_SMALL_TOOLBAR));
+ }
+ _LPEExpanders.emplace_back(LPEExpander, (*it));
+ LPEListBox.add(*LPEEffect);
+
+ Glib::ustring name = "drag_";
+ name += Glib::ustring::format(counter);
+ LPEDrag->set_name(name);
+ if (total > 1) {
+ //DnD
+ LPEDrag->drag_source_set(entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE);
+ }
+ Glib::ustring tooltip = _(converter.get_description(effectype).c_str());
+ if (untranslated_label != label) {
+ tooltip = "[" + untranslated_label + "] " + _(converter.get_description(effectype).c_str());
+ }
+ gint id = (gint)effectype;
+ LPEExpanderBox->property_has_tooltip() = true;
+ LPEExpanderBox->signal_query_tooltip().connect([=](int x, int y, bool kbd, const Glib::RefPtr<Gtk::Tooltip>& tooltipw){
+ return sp_query_custom_tooltip(x, y, kbd, tooltipw, id, tooltip, icon);
+ });
+ size_t pos = 0;
+ std::shared_ptr<Inkscape::LivePathEffect::LPEObjectReference> lperef = (*it);
+ for (auto w : LPEEffectMenu->get_children()) {
+ auto * mitem = dynamic_cast<Gtk::MenuItem *>(w);
+ if (mitem) {
+ mitem->signal_activate().connect([=](){
+ if (pos == 0) {
+ current_lpeitem->setCurrentPathEffect(lperef);
+ current_lpeitem->duplicateCurrentPathEffect();
+ effect_list_reload(current_lpeitem);
+ DocumentUndo::done(getDocument(), _("Duplicate path effect"), INKSCAPE_ICON("dialog-path-effects"));
+ } else if (pos == 1) {
+ current_lpeitem->setCurrentPathEffect(lperef);
+ current_lpeitem->upCurrentPathEffect();
+ effect_list_reload(current_lpeitem);
+ DocumentUndo::done(getDocument(), _("Move path effect up"), INKSCAPE_ICON("dialog-path-effects"));
+ } else if (pos == 2) {
+ current_lpeitem->setCurrentPathEffect(lperef);
+ current_lpeitem->downCurrentPathEffect();
+ effect_list_reload(current_lpeitem);
+ DocumentUndo::done(getDocument(), _("Move path effect down"), INKSCAPE_ICON("dialog-path-effects"));
+ } else if (pos == 3) {
+ lpeFlatten(lperef);
+ } else if (pos == 4) {
+ lpe->setDefaultParameters();
+ effect_list_reload(current_lpeitem);
+ } else if (pos == 5) {
+ lpe->resetDefaultParameters();
+ effect_list_reload(current_lpeitem);
+ } else if (pos == 6) {
+ sp_toggle_fav(untranslated_label, LPEtoggleFavorite);
+ _reload_menu = true;
+ _item_type = ""; // here we force reload even with the same tipe item selected
+ }
+
+ });
+ if (pos == 6) {
+ if (sp_has_fav(untranslated_label)) {
+ LPEtoggleFavorite->set_label(_("Unset Favorite"));
+ } else {
+ LPEtoggleFavorite->set_label(_("Set Favorite"));
+ }
+ }
+ }
+ pos ++;
+ }
+ if (total > 1) {
+ LPEDrag->signal_drag_begin().connect([=](const Glib::RefPtr<Gdk::DragContext> context){
+ cairo_surface_t *surface;
+ cairo_t *cr;
+ int x, y;
+ double sx = 1;
+ double sy = 1;
+ dnd = true;
+ Gtk::Allocation alloc = LPEEffect->get_allocation ();
+ auto device_scale = get_scale_factor();
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, alloc.get_width() * device_scale, alloc.get_height() * device_scale);
+ cairo_surface_set_device_scale(surface, device_scale, device_scale);
+ cr = cairo_create (surface);
+ LPEEffect->get_style_context()->add_class("drag-icon");
+ gtk_widget_draw (GTK_WIDGET(LPEEffect->gobj()), cr);
+ LPEEffect->get_style_context()->remove_class("drag-icon");
+ LPEDrag->translate_coordinates(*LPEEffect, dndx, dndy, x, y);
+ #ifndef __APPLE__
+ cairo_surface_get_device_scale (surface, &sx, &sy);
+ #endif
+ cairo_surface_set_device_offset (surface, -x * sx, -y * sy);
+ gtk_drag_set_icon_surface (context->gobj(), surface);
+ cairo_destroy (cr);
+ cairo_surface_destroy (surface);
+ });
+ auto row = dynamic_cast<Gtk::ListBoxRow *>(LPEEffect->get_parent());
+ LPEDrag->signal_drag_data_get().connect([=](const Glib::RefPtr<Gdk::DragContext>& context, Gtk::SelectionData& selection_data, guint info, guint time)
+ {
+ selection_data.set("GTK_LIST_BOX_ROW", Glib::ustring::format(row->get_index()));
+ });
+ LPEDrag->signal_drag_end().connect([=](const Glib::RefPtr<Gdk::DragContext>& context)
+ {
+ dnd = false;
+ });
+ row->signal_drag_data_received().connect([=](const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& selection_data, guint info, guint time)
+ {
+ if (dnd) {
+ unsigned int pos_target, pos_source;
+ Gtk::Widget *target = row;
+ pos_target = row->get_index();
+ pos_source = atoi(reinterpret_cast<char const*>(selection_data.get_data()));
+ Glib::RefPtr<Gtk::StyleContext> stylec = target->get_style_context();
+ if (pos_source > pos_target) {
+ if (stylec->has_class("after")) {
+ pos_target ++;
+ }
+ } else if (pos_source < pos_target) {
+ if (stylec->has_class("before")) {
+ pos_target --;
+ }
+ }
+ Gtk::Widget *source = LPEListBox.get_row_at_index(pos_source);
+ if (source == target) {
+ gtk_drag_finish(context->gobj(), FALSE, FALSE, time);
+ dnd = false;
+ return;
+ }
+ g_object_ref(source->gobj());
+ LPEListBox.remove(*source);
+ LPEListBox.insert(*source, pos_target);
+ g_object_unref(source->gobj());
+ move_list(pos_source,pos_target);
+ gtk_drag_finish(context->gobj(), TRUE, TRUE, time);
+ dnd = false;
+ }
+ });
+ row->drag_dest_set(entries, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_MOVE);
+ row->signal_drag_motion().connect([=](const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time)
+ {
+ gint half = row->get_allocated_height()/2;
+ Glib::RefPtr<Gtk::StyleContext> stylec = row->get_style_context();
+ if (y < half) {
+ stylec->add_class("before");
+ stylec->remove_class("after");
+ } else {
+ stylec->remove_class("before");
+ stylec->add_class("after");
+ }
+ return true;
+ }, true);
+ }
+ // other
+ LPEEffect->set_name("LPEEffectItem");
+ LPENameLabel->set_label(g_dpgettext2(nullptr, "path effect", (*it)->lpeobject->get_lpe()->getName().c_str()));
+ LPEExpander->property_expanded().signal_changed().connect(sigc::bind(sigc::mem_fun(*this, &LivePathEffectEditor::expanded_notify),LPEExpander));
+ LPEOpenExpander->signal_button_press_event().connect([=](GdkEventButton* const evt){
+ LPEExpander->set_expanded(!LPEExpander->property_expanded());
+ return false;
+ }, false);
+ dynamic_cast<Gtk::Button *>(LPEHide->get_children()[0])->signal_clicked().connect(sigc::bind<Inkscape::LivePathEffect::Effect *, Gtk::EventBox *>(sigc::mem_fun(*this, &LivePathEffectEditor::toggleVisible), lpe, LPEHide));
+ LPEDrag->signal_button_press_event().connect([=](GdkEventButton* const evt){dndx = evt->x; dndy = evt->y; return false; }, false);
+ dynamic_cast<Gtk::Button *>(LPEErase->get_children()[0])->signal_clicked().connect([=](){ removeEffect(LPEExpander);});
+ if (total > 1) {
+ LPEDrag->signal_enter_notify_event().connect([=](GdkEventCrossing*){
+ auto window = get_window();
+ auto display = get_display();
+ auto cursor = Gdk::Cursor::create(display, "grab");
+ window->set_cursor(cursor);
+ return false;
+ }, false);
+ LPEDrag->signal_leave_notify_event().connect([=](GdkEventCrossing*){
+ auto window = get_window();
+ auto display = get_display();
+ auto cursor = Gdk::Cursor::create(display, "default");
+ window->set_cursor(cursor);
+ return false;
+ }, false);
+ }
+ if (lpe->hasDefaultParameters()) {
+ LPEResetDefault->show();
+ LPESetDefault->hide();
+
+ } else {
+ LPEResetDefault->hide();
+ LPESetDefault->show();
+ }
+ }
+ }
+ if (counter == 0 && LPEDrag) {
+ LPEDrag->get_children()[0]->hide();
+ LPEDrag->set_tooltip_text("");
+ }
+ if (LPEMoveUpExtrem) {
+ LPEMoveUpExtrem->hide();
+ LPEMoveDownExtrem->hide();
+ }
+ if (LPEExpanderCurrent) {
+ _LPESelectionInfo.hide();
+ LPEExpanderCurrent->set_expanded(true);
+ Gtk::Window *current_window = dynamic_cast<Gtk::Window *>(LPEExpanderCurrent->get_toplevel());
+ if (current_window) {
+ current_window->set_focus(*LPEExpanderCurrent);
+ }
+ }
+ selection_info();
+ LPEListBox.show_all_children();
+ ensure_size();
+}
+
+void LivePathEffectEditor::expanded_notify(Gtk::Expander *expander) {
+
+ if (updating) {
+ return;
+ }
+ if (!dnd) {
+ _freezeexpander = false;
+ }
+ if (_freezeexpander) {
+ _freezeexpander = false;
+ return;
+ }
+ if (dnd) {
+ _freezeexpander = true;
+ expander->set_expanded(!expander->get_expanded());
+ return;
+ };
+ updating = true;
+ if (expander->get_expanded()) {
+ for (auto &w : _LPEExpanders){
+ if (w.first == expander) {
+ w.first->set_expanded(true);
+ w.first->get_parent()->get_parent()->get_parent()->set_name("currentlpe");
+ current_lperef = w;
+ current_lpeitem->setCurrentPathEffect(w.second);
+ showParams(w, true);
+ } else {
+ w.first->set_expanded(false);
+ w.first->get_parent()->get_parent()->get_parent()->set_name("unactive_lpe");
+ }
+ }
+ }
+ auto selection = SP_ACTIVE_DESKTOP->getSelection();
+ if (selection && current_lpeitem && !selection->isEmpty()) {
+ selection_changed_lock = true;
+ selection->clear();
+ selection->add(current_lpeitem);
+ Inkscape::UI::Tools::sp_update_helperpath(getDesktop());
+ selection_changed_lock = false;
+ }
+ updating = false;
+}
+
+bool
+LivePathEffectEditor::lpeFlatten(std::shared_ptr<Inkscape::LivePathEffect::LPEObjectReference> lperef)
+{
+ current_lpeitem->setCurrentPathEffect(lperef);
+ current_lpeitem = current_lpeitem->flattenCurrentPathEffect();
+ auto selection = getSelection();
+ if (selection && selection->isEmpty() ) {
+ selection->add(current_lpeitem);
+ }
+ DocumentUndo::done(getDocument(), _("Flatten path effect(s)"), INKSCAPE_ICON("dialog-path-effects"));
+ return false;
+}
+
+void
+LivePathEffectEditor::removeEffect(Gtk::Expander * expander) {
+ bool reload = current_lperef.first != expander;
+ auto current_lperef_tmp = current_lperef;
+ for (auto &w : _LPEExpanders){
+ if (w.first == expander) {
+ current_lpeitem->setCurrentPathEffect(w.second);
+ current_lpeitem = current_lpeitem->removeCurrentPathEffect(false);
+ }
+ }
+ if (reload) {
+ current_lpeitem->setCurrentPathEffect(current_lperef_tmp.second);
+ }
+ effect_list_reload(current_lpeitem);
+ DocumentUndo::done(getDocument(), _("Remove path effect"), INKSCAPE_ICON("dialog-path-effects"));
+}
+
+bool
+LivePathEffectEditor::toggleFavInLpe(GdkEventButton * evt, Glib::ustring name, Gtk::Button *favbutton) {
+ auto *favimage = dynamic_cast<Gtk::Image *>(favbutton->get_image());
+ if (favimage->get_icon_name() == "draw-star") {
+ favbutton->set_image_from_icon_name("draw-star-outline", Gtk::IconSize(Gtk::ICON_SIZE_SMALL_TOOLBAR));
+ sp_remove_fav(name);
+ } else {
+ favbutton->set_image_from_icon_name("draw-star", Gtk::IconSize(Gtk::ICON_SIZE_SMALL_TOOLBAR));
+ sp_add_fav(name);
+ }
+ clearMenu();
+ return false;
+}
+
+
+
+
+
+/*
+ * Clears the effectlist
+ */
+void
+LivePathEffectEditor::clear_lpe_list()
+{
+ for (auto &w : LPEListBox.get_children()) {
+ LPEListBox.remove(*w);
+ }
+ for (auto &w : _LPEParentBox.get_children()) {
+ _LPEParentBox.remove(*w);
+ }
+ for (auto &w : _LPECurrentItem.get_children()) {
+ _LPECurrentItem.remove(*w);
+ }
+}
+
+SPLPEItem * LivePathEffectEditor::clonetolpeitem()
+{
+ auto selection = getSelection();
+ if (selection && !selection->isEmpty() ) {
+ auto use = cast<SPUse>(selection->singleItem());
+ if ( use ) {
+ DocumentUndo::ScopedInsensitive tmp(getDocument());
+ // item is a clone. do not show effectlist dialog.
+ // convert to path, apply CLONE_ORIGINAL LPE, link it to the cloned path
+
+ // test whether linked object is supported by the CLONE_ORIGINAL LPE
+ SPItem *orig = use->get_original();
+ if ( is<SPShape>(orig) || is<SPGroup>(orig) || is<SPText>(orig) ) {
+ // select original
+ selection->set(orig);
+
+ // delete clone but remember its id and transform
+ auto id_copy = Util::to_opt(use->getAttribute("id"));
+ auto transform_copy = Util::to_opt(use->getAttribute("transform"));
+ use->deleteObject(false);
+ use = nullptr;
+
+ // run sp_selection_clone_original_path_lpe
+ selection->cloneOriginalPathLPE(true, true, true);
+
+ SPItem *new_item = selection->singleItem();
+ // Check that the cloning was successful. We don't want to change the ID of the original referenced path!
+ if (new_item && (new_item != orig)) {
+ new_item->setAttribute("id", Util::to_cstr(id_copy));
+ if (Util::to_cstr(transform_copy)) {
+ Geom::Affine item_t(Geom::identity());
+ sp_svg_transform_read(Util::to_cstr(transform_copy), &item_t);
+ new_item->transform *= item_t;
+ new_item->doWriteTransform(new_item->transform);
+ new_item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ }
+ new_item->setAttribute("class", "fromclone");
+ }
+
+ auto *lpeitem = cast<SPLPEItem>(new_item);
+ if (lpeitem) {
+ sp_lpe_item_update_patheffect(lpeitem, true, true);
+ return lpeitem;
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
+void LivePathEffectEditor::onAddGallery()
+{
+ // show effectlist dialog
+ using Inkscape::UI::Dialog::LivePathEffectAdd;
+ LivePathEffectAdd::show(getDesktop());
+ clearMenu();
+ if ( !LivePathEffectAdd::isApplied()) {
+ return;
+ }
+
+ const LivePathEffect::EnumEffectData<LivePathEffect::EffectType> *data = LivePathEffectAdd::getActiveData();;
+ if (!data) {
+ return;
+ }
+ selection_changed_lock = true;
+ SPLPEItem *fromclone = clonetolpeitem();
+ if (fromclone) {
+ current_lpeitem = fromclone;
+ if (data->key == "clone_original") {
+ current_lpeitem->getCurrentLPE()->refresh_widgets = true;
+ selection_changed_lock = false;
+ DocumentUndo::done(getDocument(), _("Create and apply path effect"), INKSCAPE_ICON("dialog-path-effects"));
+ return;
+ }
+
+ }
+ selection_changed_lock = false;
+ if (current_lpeitem) {
+ LivePathEffect::Effect::createAndApply(data->key.c_str(), getDocument(), current_lpeitem);
+ current_lpeitem->getCurrentLPE()->refresh_widgets = true;
+ DocumentUndo::done(getDocument(), _("Create and apply path effect"), INKSCAPE_ICON("dialog-path-effects"));
+ }
+}
+
+void LivePathEffectEditor::on_showgallery_notify(Preferences::Entry const &new_val)
+{
+ _LPEGallery.set_visible(new_val.getBool());
+}
+
+} // namespace Dialog
+} // 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:fileencoding=utf-8:textwidth=99 :