diff options
Diffstat (limited to '')
-rw-r--r-- | src/selection.cpp | 367 |
1 files changed, 367 insertions, 0 deletions
diff --git a/src/selection.cpp b/src/selection.cpp new file mode 100644 index 0000000..520a2cc --- /dev/null +++ b/src/selection.cpp @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Per-desktop selection container + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * MenTaLguY <mental@rydia.net> + * bulia byak <buliabyak@users.sf.net> + * Andrius R. <knutux@gmail.com> + * Abhishek Sharma + * Adrian Boguszewski + * + * Copyright (C) 2016 Adrian Boguszewski + * Copyright (C) 2006 Andrius R. + * Copyright (C) 2004-2005 MenTaLguY + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#ifdef HAVE_CONFIG_H +#endif + +#include <cmath> + +#include "inkscape.h" +#include "preferences.h" +#include "desktop.h" +#include "document.h" +#include "ui/tools/node-tool.h" +#include "ui/tool/multi-path-manipulator.h" +#include "ui/tool/path-manipulator.h" +#include "ui/tool/control-point-selection.h" +#include "layer-manager.h" +#include "page-manager.h" +#include "object/sp-path.h" +#include "object/sp-defs.h" +#include "object/sp-shape.h" +#include "xml/repr.h" + +#define SP_SELECTION_UPDATE_PRIORITY (G_PRIORITY_HIGH_IDLE + 1) + +namespace Inkscape { + +Selection::Selection(SPDesktop *desktop): + ObjectSet(desktop), + _selection_context(nullptr), + _flags(0), + _idle(0), + anchor_x(0.0), + anchor_y(0.0) +{ +} + +Selection::Selection(SPDocument *document): + ObjectSet(document), + _selection_context(nullptr), + _flags(0), + _idle(0), + anchor_x(0.0), + anchor_y(0.0) +{ +} + +Selection::~Selection() { + if (_idle) { + g_source_remove(_idle); + _idle = 0; + } + + for (auto &c : _modified_connections) { + c.second.disconnect(); + } +} + +/* Handler for selected objects "modified" signal */ + +void Selection::_schedule_modified(SPObject */*obj*/, guint flags) { + if (!this->_idle) { + /* Request handling to be run in _idle loop */ + this->_idle = g_idle_add_full(SP_SELECTION_UPDATE_PRIORITY, GSourceFunc(&Selection::_emit_modified), this, nullptr); + } + + /* Collect all flags */ + this->_flags |= flags; +} + +gboolean Selection::_emit_modified(Selection *selection) +{ + /* force new handler to be created if requested before we return */ + selection->_idle = 0; + guint flags = selection->_flags; + selection->_flags = 0; + + selection->_emitModified(flags); + + /* drop this handler */ + return FALSE; +} + +void Selection::_emitModified(guint flags) { + _modified_signal.emit(this, flags); + + if (_desktop) { + if (auto item = singleItem()) { + // If the selected items have been moved to a new page... + _desktop->getDocument()->getPageManager().selectPage(item, false); + } + } +} + +void Selection::_emitChanged(bool persist_selection_context/* = false */) { + ObjectSet::_emitChanged(); + if (persist_selection_context) { + if (nullptr == _selection_context) { + _selection_context = _desktop->layerManager().currentLayer(); + sp_object_ref(_selection_context, nullptr); + _context_release_connection = _selection_context->connectRelease(sigc::mem_fun(*this, &Selection::_releaseContext)); + } + } else { + _releaseContext(_selection_context); + } + + /** Change the layer selection to the item selection + * TODO: Should it only change if there's a single object? + */ + if (_desktop) { + if (auto item = singleItem()) { + auto layer = _desktop->layerManager().layerForObject(item); + if (layer && layer != _selection_context) { + _desktop->layerManager().setCurrentLayer(layer); + } + // This could be more complex if we want to be smarter. + _desktop->getDocument()->getPageManager().selectPage(item, false); + } + } + + _changed_signal.emit(this); +} + +void Selection::_releaseContext(SPObject *obj) +{ + if (nullptr == _selection_context || _selection_context != obj) + return; + + _context_release_connection.disconnect(); + + sp_object_unref(_selection_context, nullptr); + _selection_context = nullptr; +} + +SPObject *Selection::activeContext() { + if (nullptr != _selection_context) + return _selection_context; + return _desktop->layerManager().currentLayer(); +} + +std::vector<Inkscape::SnapCandidatePoint> Selection::getSnapPoints(SnapPreferences const *snapprefs) const { + std::vector<Inkscape::SnapCandidatePoint> p; + + if (snapprefs != nullptr){ + SnapPreferences snapprefs_dummy = *snapprefs; // create a local copy of the snapping prefs + snapprefs_dummy.setTargetSnappable(Inkscape::SNAPTARGET_ROTATION_CENTER, false); // locally disable snapping to the item center + auto items = const_cast<Selection *>(this)->items(); + for (auto iter = items.begin(); iter != items.end(); ++iter) { + SPItem *this_item = *iter; + this_item->getSnappoints(p, &snapprefs_dummy); + + //Include the transformation origin for snapping + //For a selection or group only the overall center is considered, not for each item individually + if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_ROTATION_CENTER)) { + p.emplace_back(this_item->getCenter(), SNAPSOURCE_ROTATION_CENTER); + } + } + } + + return p; +} + +void Selection::setAnchor(double x, double y, bool set) +{ + double const epsilon = 1e-12; + if (std::fabs(anchor_x - x) > epsilon || std::fabs(anchor_y - y) > epsilon || set != has_anchor) { + anchor_x = x; + anchor_y = y; + has_anchor = set; + this->_emitModified(SP_OBJECT_MODIFIED_FLAG); + } +} + +SPObject *Selection::_objectForXMLNode(Inkscape::XML::Node *repr) const { + g_return_val_if_fail(repr != nullptr, NULL); + SPObject *object = _desktop->getDocument()->getObjectByRepr(repr); + assert(object == _desktop->getDocument()->getObjectById(repr->attribute("id"))); + return object; +} + +size_t Selection::numberOfLayers() { + auto items = this->items(); + std::set<SPObject*> layers; + for (auto iter = items.begin(); iter != items.end(); ++iter) { + SPObject *layer = _desktop->layerManager().layerForObject(*iter); + layers.insert(layer); + } + + return layers.size(); +} + +size_t Selection::numberOfParents() { + auto items = this->items(); + std::set<SPObject*> parents; + for (auto iter = items.begin(); iter != items.end(); ++iter) { + SPObject *parent = (*iter)->parent; + parents.insert(parent); + } + return parents.size(); +} + +void Selection::_connectSignals(SPObject *object) { + _modified_connections[object] = object->connectModified(sigc::mem_fun(*this, &Selection::_schedule_modified)); +} + +void Selection::_releaseSignals(SPObject *object) { + _modified_connections[object].disconnect(); + _modified_connections.erase(object); +} + +void +Selection::emptyBackup(){ + _selected_ids.clear(); + _seldata.clear(); + params.clear(); +} + +void +Selection::setBackup () +{ + SPDesktop *desktop = this->desktop(); + Inkscape::UI::Tools::NodeTool *tool = nullptr; + if (desktop) { + Inkscape::UI::Tools::ToolBase *ec = desktop->event_context; + if (INK_IS_NODE_TOOL(ec)) { + tool = static_cast<Inkscape::UI::Tools::NodeTool*>(ec); + } + } + _selected_ids.clear(); + _seldata.clear(); + params.clear(); + auto items = const_cast<Selection *>(this)->items(); + for (auto iter = items.begin(); iter != items.end(); ++iter) { + SPItem *item = *iter; + if(!item->getId()) { + continue; + } + std::string selected_id; + selected_id += "--id="; + selected_id += item->getId(); + params.push_back(selected_id); + _selected_ids.emplace_back(item->getId()); + } + if(tool){ + Inkscape::UI::ControlPointSelection *cps = tool->_selected_nodes; + std::list<Inkscape::UI::SelectableControlPoint *> points_list = cps->_points_list; + for (auto & i : points_list) { + Inkscape::UI::Node *node = dynamic_cast<Inkscape::UI::Node*>(i); + if (node) { + std::string id = node->nodeList().subpathList().pm().item()->getId(); + + int sp = 0; + bool found_sp = false; + for(Inkscape::UI::SubpathList::iterator i = node->nodeList().subpathList().begin(); i != node->nodeList().subpathList().end(); ++i,++sp){ + if(&**i == &(node->nodeList())){ + found_sp = true; + break; + } + } + int nl=0; + bool found_nl = false; + for (Inkscape::UI::NodeList::iterator j = node->nodeList().begin(); j != node->nodeList().end(); ++j, ++nl){ + if(&*j==node){ + found_nl = true; + break; + } + } + std::ostringstream ss; + ss<< "--selected-nodes=" << id << ":" << sp << ":" << nl; + Glib::ustring selected_nodes = ss.str(); + + if(found_nl && found_sp) { + _seldata.emplace_back(id,std::make_pair(sp,nl)); + params.push_back(selected_nodes); + } else { + g_warning("Something went wrong while trying to pass selected nodes to extension. Please report a bug."); + } + } + } + }//end add selected nodes +} + +void +Selection::restoreBackup() +{ + SPDesktop *desktop = this->desktop(); + SPDocument *document = SP_ACTIVE_DOCUMENT; + Inkscape::UI::Tools::NodeTool *tool = nullptr; + if (desktop) { + Inkscape::UI::Tools::ToolBase *ec = desktop->event_context; + if (INK_IS_NODE_TOOL(ec)) { + tool = static_cast<Inkscape::UI::Tools::NodeTool*>(ec); + } + } + + // update selection + std::vector<std::string>::iterator it = _selected_ids.begin(); + std::vector<SPItem*> new_selection; + for (; it!= _selected_ids.end(); ++it){ + SPItem * item = dynamic_cast<SPItem *>(document->getObjectById(it->c_str())); + SPDefs * defs = document->getDefs(); + if (item && !defs->isAncestorOf(item)) { + new_selection.push_back(item); + } + } + clear(); + add(new_selection.begin(), new_selection.end()); + + if (tool) { + Inkscape::UI::ControlPointSelection *cps = tool->_selected_nodes; + cps->selectAll(); + std::list<Inkscape::UI::SelectableControlPoint *> points_list = cps->_points_list; + cps->clear(); + Inkscape::UI::Node * node = dynamic_cast<Inkscape::UI::Node*>(*points_list.begin()); + if (node) { + Inkscape::UI::SubpathList sp = node->nodeList().subpathList(); + for (auto & l : _seldata) { + gint sp_count = 0; + for (Inkscape::UI::SubpathList::iterator j = sp.begin(); j != sp.end(); ++j, ++sp_count) { + if(sp_count == l.second.first) { + gint nt_count = 0; + for (Inkscape::UI::NodeList::iterator k = (*j)->begin(); k != (*j)->end(); ++k, ++nt_count) { + if(nt_count == l.second.second) { + cps->insert(k.ptr()); + break; + } + } + break; + } + } + } + } + points_list.clear(); + } +} + + +} + +/* + 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 : |