// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2011 Jürgen Riegel * * Copyright (c) 2011 Werner Mayer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Selection.h" #include "SelectionObject.h" #include "Application.h" #include "Document.h" #include "Macro.h" #include "MainWindow.h" #include "MDIView.h" #include "SelectionFilter.h" #include "SelectionFilterPy.h" #include "SelectionObserverPython.h" #include "Tree.h" #include "ViewProvider.h" #include "ViewProviderDocumentObject.h" FC_LOG_LEVEL_INIT("Selection", false, true, true) using namespace Gui; using namespace std; namespace sp = std::placeholders; SelectionGateFilterExternal::SelectionGateFilterExternal(const char* docName, const char* objName) { if (docName) { DocName = docName; if (objName) { ObjName = objName; } } } bool SelectionGateFilterExternal::allow(App::Document* doc, App::DocumentObject* obj, const char*) { if (!doc || !obj) { return true; } if (!DocName.empty() && doc->getName() != DocName) { notAllowedReason = "Cannot select external object"; } else if (!ObjName.empty() && ObjName == obj->getNameInDocument()) { notAllowedReason = "Cannot select self"; } else { return true; } return false; } ////////////////////////////////////////////////////////////////////////////////////////// SelectionObserver::SelectionObserver(bool attach, ResolveMode resolve) : resolve(resolve) , blockedSelection(false) { if (attach) { attachSelection(); } } SelectionObserver::SelectionObserver(const ViewProviderDocumentObject* vp, bool attach, ResolveMode resolve) : resolve(resolve) , blockedSelection(false) { if (vp && vp->getObject() && vp->getObject()->getDocument()) { filterDocName = vp->getObject()->getDocument()->getName(); filterObjName = vp->getObject()->getNameInDocument(); } if (attach) { attachSelection(); } } SelectionObserver::~SelectionObserver() { detachSelection(); } bool SelectionObserver::blockSelection(bool block) { bool ok = blockedSelection; blockedSelection = block; return ok; } bool SelectionObserver::isSelectionBlocked() const { return blockedSelection; } bool SelectionObserver::isSelectionAttached() const { return connectSelection.connected(); } void SelectionObserver::attachSelection() { if (!connectSelection.connected()) { bool newStyle = (resolve >= ResolveMode::NewStyleElement); bool oldStyle = (resolve == ResolveMode::OldStyleElement); auto& signal = newStyle ? Selection().signalSelectionChanged3 : oldStyle ? Selection().signalSelectionChanged2 : Selection().signalSelectionChanged; // NOLINTBEGIN connectSelection = signal.connect( std::bind(&SelectionObserver::_onSelectionChanged, this, sp::_1) ); // NOLINTEND if (!filterDocName.empty()) { Selection().addSelectionGate( new SelectionGateFilterExternal(filterDocName.c_str(), filterObjName.c_str()) ); } } } void SelectionObserver::_onSelectionChanged(const SelectionChanges& msg) { try { if (blockedSelection) { return; } onSelectionChanged(msg); } catch (Base::Exception& e) { e.reportException(); FC_ERR("Unhandled Base::Exception caught in selection observer: "); } catch (std::exception& e) { FC_ERR("Unhandled std::exception caught in selection observer: " << e.what()); } catch (...) { FC_ERR("Unhandled unknown exception caught in selection observer"); } } void SelectionObserver::detachSelection() { if (connectSelection.connected()) { connectSelection.disconnect(); if (!filterDocName.empty()) { Selection().rmvSelectionGate(); } } } // ------------------------------------------- bool SelectionSingleton::hasSelection() const { return !_SelList.empty(); } bool SelectionSingleton::hasPreselection() const { return !CurrentPreselection.Object.getObjectName().empty(); } std::vector SelectionSingleton::getCompleteSelection(ResolveMode resolve) const { return getSelection("*", resolve); } std::vector SelectionSingleton::getSelection( const char* pDocName, ResolveMode resolve, bool single ) const { std::vector temp; if (single) { temp.reserve(1); } SelObj tempSelObj; App::Document* pcDoc = nullptr; if (!pDocName || strcmp(pDocName, "*") != 0) { pcDoc = getDocument(pDocName); if (!pcDoc) { return temp; } } std::map> objMap; for (auto& sel : _SelList) { if (!sel.pDoc) { continue; } const char* subelement = nullptr; auto obj = getObjectOfType(sel, App::DocumentObject::getClassTypeId(), resolve, &subelement); if (!obj || (pcDoc && sel.pObject->getDocument() != pcDoc)) { continue; } // In case we are resolving objects, make sure no duplicates if (resolve != ResolveMode::NoResolve && !objMap[obj].insert(std::string(subelement ? subelement : "")).second) { continue; } if (single && !temp.empty()) { temp.clear(); break; } tempSelObj.DocName = obj->getDocument()->getName(); tempSelObj.FeatName = obj->getNameInDocument(); tempSelObj.SubName = subelement; tempSelObj.TypeName = obj->getTypeId().getName(); tempSelObj.pObject = obj; tempSelObj.pResolvedObject = sel.pResolvedObject; tempSelObj.pDoc = obj->getDocument(); tempSelObj.x = sel.x; tempSelObj.y = sel.y; tempSelObj.z = sel.z; temp.push_back(tempSelObj); } return temp; } bool SelectionSingleton::hasSelection(const char* doc, ResolveMode resolve) const { App::Document* pcDoc = nullptr; if (!doc || strcmp(doc, "*") != 0) { pcDoc = getDocument(doc); if (!pcDoc) { return false; } } for (auto& sel : _SelList) { if (!sel.pDoc) { continue; } auto obj = getObjectOfType(sel, App::DocumentObject::getClassTypeId(), resolve); if (obj && (!pcDoc || sel.pObject->getDocument() == pcDoc)) { return true; } } return false; } bool SelectionSingleton::hasSubSelection(const char* doc, bool subElement) const { App::Document* pcDoc = nullptr; if (!doc || strcmp(doc, "*") != 0) { pcDoc = getDocument(doc); if (!pcDoc) { return false; } } for (auto& sel : _SelList) { if (pcDoc && pcDoc != sel.pDoc) { continue; } if (sel.SubName.empty()) { continue; } if (subElement && sel.SubName.back() != '.') { return true; } if (sel.pObject != sel.pResolvedObject) { return true; } } return false; } std::vector SelectionSingleton::getPickedList(const char* pDocName) const { std::vector temp; SelObj tempSelObj; App::Document* pcDoc = nullptr; if (!pDocName || strcmp(pDocName, "*") != 0) { pcDoc = getDocument(pDocName); if (!pcDoc) { return temp; } } for (std::list<_SelObj>::const_iterator It = _PickedList.begin(); It != _PickedList.end(); ++It) { if (!pcDoc || It->pDoc == pcDoc) { tempSelObj.DocName = It->DocName.c_str(); tempSelObj.FeatName = It->FeatName.c_str(); tempSelObj.SubName = It->SubName.c_str(); tempSelObj.TypeName = It->TypeName.c_str(); tempSelObj.pObject = It->pObject; tempSelObj.pResolvedObject = It->pResolvedObject; tempSelObj.pDoc = It->pDoc; tempSelObj.x = It->x; tempSelObj.y = It->y; tempSelObj.z = It->z; temp.push_back(tempSelObj); } } return temp; } std::vector SelectionSingleton::getSelectionIn( App::DocumentObject* container, Base::Type typeId, bool single ) const { if (!container) { return getSelectionEx(nullptr, typeId, ResolveMode::NoResolve, single); } std::vector sels = getSelectionEx(nullptr, App::DocumentObject::getClassTypeId(), ResolveMode::NoResolve, single); std::vector ret; std::map SortMap; for (auto& sel : sels) { auto* rootObj = sel.getObject(); App::Document* doc = rootObj->getDocument(); std::vector subs = sel.getSubNames(); bool objPassed = false; for (size_t i = 0; i < subs.size(); ++i) { auto& sub = subs[i]; App::DocumentObject* newRootObj = nullptr; std::string newSub = ""; std::vector names = Base::Tools::splitSubName(sub); if (container == rootObj) { objPassed = true; } if (rootObj->isLink()) { // Update doc in case its an external link. doc = rootObj->getLinkedObject()->getDocument(); } for (auto& name : names) { App::DocumentObject* obj = doc->getObject(name.c_str()); if (!obj) { // We reached the element name (for example 'edge1') newSub += name; break; } if (objPassed) { if (!newRootObj) { // We are the first object after the container is passed. newRootObj = obj; } else { newSub += name + "."; } } if (obj == container) { objPassed = true; } if (obj->isLink()) { // Update doc in case its an external link. doc = obj->getLinkedObject()->getDocument(); } } if (newRootObj) { // Make sure selected object is of correct type auto* lastObj = newRootObj->resolve(newSub.c_str()); if (!lastObj || !lastObj->isDerivedFrom(typeId)) { continue; } auto it = SortMap.find(newRootObj); if (it != SortMap.end()) { // only add sub-element if (newSub != "") { ret[it->second].SubNames.emplace_back(newSub); ret[it->second].SelPoses.emplace_back(sel.SelPoses[i]); } } else { if (single && !ret.empty()) { ret.clear(); break; } // create a new entry ret.emplace_back(newRootObj); if (newSub != "") { ret.back().SubNames.emplace_back(newSub); ret.back().SelPoses.emplace_back(sel.SelPoses[i]); } SortMap.insert(std::make_pair(newRootObj, ret.size() - 1)); } } } } return ret; } std::vector SelectionSingleton::getSelectionEx( const char* pDocName, Base::Type typeId, ResolveMode resolve, bool single ) const { return getObjectList(pDocName, typeId, _SelList, resolve, single); } std::vector SelectionSingleton::getPickedListEx( const char* pDocName, Base::Type typeId ) const { return getObjectList(pDocName, typeId, _PickedList, ResolveMode::NoResolve); } std::vector SelectionSingleton::getObjectList( const char* pDocName, Base::Type typeId, std::list<_SelObj>& objList, ResolveMode resolve, bool single ) const { std::vector temp; if (single) { temp.reserve(1); } std::map SortMap; // check the type if (typeId.isBad()) { return temp; } App::Document* pcDoc = nullptr; if (!pDocName || strcmp(pDocName, "*") != 0) { pcDoc = getDocument(pDocName); if (!pcDoc) { return temp; } } for (auto& sel : objList) { if (!sel.pDoc) { continue; } const char* subelement = nullptr; auto obj = getObjectOfType(sel, typeId, resolve, &subelement); if (!obj || (pcDoc && sel.pObject->getDocument() != pcDoc)) { continue; } auto it = SortMap.find(obj); if (it != SortMap.end()) { // only add sub-element if (subelement && *subelement) { if (resolve != ResolveMode::NoResolve && !temp[it->second]._SubNameSet.insert(subelement).second) { continue; } temp[it->second].SubNames.emplace_back(subelement); temp[it->second].SelPoses.emplace_back(sel.x, sel.y, sel.z); } } else { if (single && !temp.empty()) { temp.clear(); break; } // create a new entry temp.emplace_back(obj); if (subelement && *subelement) { temp.back().SubNames.emplace_back(subelement); temp.back().SelPoses.emplace_back(sel.x, sel.y, sel.z); if (resolve != ResolveMode::NoResolve) { temp.back()._SubNameSet.insert(subelement); } } SortMap.insert(std::make_pair(obj, temp.size() - 1)); } } return temp; } bool SelectionSingleton::needPickedList() const { return _needPickedList; } void SelectionSingleton::enablePickedList(bool enable) { if (enable != _needPickedList) { _needPickedList = enable; _PickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } } static void notifyDocumentObjectViewProvider(const SelectionChanges& changes) { const auto* doc = App::GetApplication().getDocument(changes.pDocName); if (!doc) { return; } const auto* obj = doc->getObject(changes.pObjectName); if (!obj) { return; } auto* vp = Application::Instance->getViewProvider(obj); if (!vp) { return; } vp->onSelectionChanged(changes); } void SelectionSingleton::notify(SelectionChanges&& Chng) { if (Notifying) { NotificationQueue.push_back(std::move(Chng)); return; } Base::FlagToggler flag(Notifying); NotificationQueue.push_back(std::move(Chng)); while (!NotificationQueue.empty()) { const auto& msg = NotificationQueue.front(); bool notify = false; switch (msg.Type) { case SelectionChanges::AddSelection: notify = isSelected(msg.pDocName, msg.pObjectName, msg.pSubName, ResolveMode::NoResolve); break; case SelectionChanges::RmvSelection: notify = !isSelected(msg.pDocName, msg.pObjectName, msg.pSubName, ResolveMode::NoResolve); break; case SelectionChanges::SetPreselect: notify = CurrentPreselection.Type == SelectionChanges::SetPreselect && CurrentPreselection.Object == msg.Object; break; case SelectionChanges::RmvPreselect: notify = CurrentPreselection.Type == SelectionChanges::ClrSelection; break; default: notify = true; } if (notify) { // Notify the view provider of the object. notifyDocumentObjectViewProvider(msg); Notify(msg); try { signalSelectionChanged(msg); } catch (const boost::exception&) { // reported by code analyzers Base::Console().warning("notify: Unexpected boost exception\n"); } } NotificationQueue.pop_front(); } } bool SelectionSingleton::hasPickedList() const { return !_PickedList.empty(); } int SelectionSingleton::getAsPropertyLinkSubList(App::PropertyLinkSubList& prop) const { std::vector sel = this->getSelectionEx(); std::vector objs; objs.reserve(sel.size() * 2); std::vector subs; subs.reserve(sel.size() * 2); for (auto& selitem : sel) { App::DocumentObject* obj = selitem.getObject(); const std::vector& subnames = selitem.getSubNames(); // whole object is selected if (subnames.empty()) { objs.push_back(obj); subs.emplace_back(); } else { for (const auto& subname : subnames) { objs.push_back(obj); subs.push_back(subname); } } } assert(objs.size() == subs.size()); prop.setValues(objs, subs); return objs.size(); } App::DocumentObject* SelectionSingleton::getObjectOfType( _SelObj& sel, Base::Type typeId, ResolveMode resolve, const char** subelement ) { auto obj = sel.pObject; if (!obj || !obj->isAttachedToDocument()) { return nullptr; } const char* subname = sel.SubName.c_str(); if (resolve != ResolveMode::NoResolve) { obj = sel.pResolvedObject; if (resolve == ResolveMode::NewStyleElement && !sel.elementName.newName.empty()) { subname = sel.elementName.newName.c_str(); } else { subname = sel.elementName.oldName.c_str(); } } if (!obj) { return nullptr; } if (!obj->isDerivedFrom(typeId) && (resolve != ResolveMode::FollowLink || !obj->getLinkedObject(true)->isDerivedFrom(typeId))) { return nullptr; } if (subelement) { *subelement = subname; } return obj; } vector SelectionSingleton::getObjectsOfType( const Base::Type& typeId, const char* pDocName, ResolveMode resolve ) const { std::vector temp; App::Document* pcDoc = nullptr; if (!pDocName || strcmp(pDocName, "*") != 0) { pcDoc = getDocument(pDocName); if (!pcDoc) { return temp; } } std::set objs; for (auto& sel : _SelList) { if (pcDoc && pcDoc != sel.pDoc) { continue; } App::DocumentObject* pObject = getObjectOfType(sel, typeId, resolve); if (pObject) { auto ret = objs.insert(pObject); if (ret.second) { temp.push_back(pObject); } } } return temp; } std::vector SelectionSingleton::getObjectsOfType( const char* typeName, const char* pDocName, ResolveMode resolve ) const { Base::Type typeId = Base::Type::fromName(typeName); if (typeId.isBad()) { return {}; } return getObjectsOfType(typeId, pDocName, resolve); } unsigned int SelectionSingleton::countObjectsOfType( const Base::Type& typeId, const char* pDocName, ResolveMode resolve ) const { App::Document* pcDoc = nullptr; if (!pDocName || strcmp(pDocName, "*") != 0) { pcDoc = getDocument(pDocName); if (!pcDoc) { return 0; } } return std::count_if(_SelList.begin(), _SelList.end(), [&](auto& sel) { return (!pcDoc || pcDoc == sel.pDoc) && getObjectOfType(sel, typeId, resolve); }); } unsigned int SelectionSingleton::countObjectsOfType( const char* typeName, const char* pDocName, ResolveMode resolve ) const { Base::Type typeId = Base::Type::fromName(typeName); if (typeId.isBad()) { return 0; } return countObjectsOfType(typeId, pDocName, resolve); } void SelectionSingleton::slotSelectionChanged(const SelectionChanges& msg) { if (msg.Type == SelectionChanges::SetPreselectSignal || msg.Type == SelectionChanges::ShowSelection || msg.Type == SelectionChanges::HideSelection) { return; } if (!msg.Object.getSubName().empty()) { auto pParent = msg.Object.getObject(); if (!pParent) { return; } App::ElementNamePair elementName; auto& newElementName = elementName.newName; auto& oldElementName = elementName.oldName; auto pObject = App::GeoFeature::resolveElement(pParent, msg.pSubName, elementName); if (!pObject) { return; } SelectionChanges msg2( msg.Type, pObject->getDocument()->getName(), pObject->getNameInDocument(), !newElementName.empty() ? newElementName.c_str() : oldElementName.c_str(), pObject->getTypeId().getName(), msg.x, msg.y, msg.z ); try { msg2.pOriginalMsg = &msg; signalSelectionChanged3(msg2); msg2.Object.setSubName(oldElementName.c_str()); msg2.pSubName = msg2.Object.getSubName().c_str(); signalSelectionChanged2(msg2); } catch (const boost::exception&) { // reported by code analyzers Base::Console().warning("slotSelectionChanged: Unexpected boost exception\n"); } } else { try { signalSelectionChanged3(msg); signalSelectionChanged2(msg); } catch (const boost::exception&) { // reported by code analyzers Base::Console().warning("slotSelectionChanged: Unexpected boost exception\n"); } } } int SelectionSingleton::setPreselect( const char* pDocName, const char* pObjectName, const char* pSubName, float x, float y, float z, SelectionChanges::MsgSource signal ) { if (!pDocName || !pObjectName) { rmvPreselect(); return 0; } if (!pSubName) { pSubName = ""; } if (DocName == pDocName && FeatName == pObjectName && SubName == pSubName) { return -1; } rmvPreselect(); if (ActiveGate && signal != SelectionChanges::MsgSource::Internal) { App::Document* pDoc = getDocument(pDocName); if (!pDoc || !pObjectName) { return 0; } App::ElementNamePair elementName; auto pObject = pDoc->getObject(pObjectName); if (!pObject) { return 0; } const char* subelement = pSubName; if (gateResolve != ResolveMode::NoResolve) { auto& newElementName = elementName.newName; auto& oldElementName = elementName.oldName; pObject = App::GeoFeature::resolveElement(pObject, pSubName, elementName); if (!pObject) { return 0; } if (gateResolve > ResolveMode::OldStyleElement) { subelement = !newElementName.empty() ? newElementName.c_str() : oldElementName.c_str(); } else { subelement = oldElementName.c_str(); } } if (!ActiveGate->allow(pObject->getDocument(), pObject, subelement)) { QString msg; if (ActiveGate->notAllowedReason.length() > 0) { msg = QObject::tr(ActiveGate->notAllowedReason.c_str()); } else { msg = QCoreApplication::translate("SelectionFilter", "Not allowed:"); } msg.append(QStringLiteral(" %1.%2.%3 ") .arg( QString::fromLatin1(pDocName), QString::fromLatin1(pObjectName), QString::fromLatin1(pSubName) )); if (getMainWindow()) { getMainWindow()->showMessage(msg); Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->setOverrideCursor(QCursor(Qt::ForbiddenCursor)); } return 0; } Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->restoreOverrideCursor(); } DocName = pDocName; FeatName = pObjectName; SubName = pSubName; hx = x; hy = y; hz = z; // set up the change object SelectionChanges Chng( signal == SelectionChanges::MsgSource::Internal ? SelectionChanges::SetPreselectSignal : SelectionChanges::SetPreselect, DocName, FeatName, SubName, std::string(), x, y, z, signal ); if (Chng.Type == SelectionChanges::SetPreselect) { CurrentPreselection = Chng; FC_TRACE("preselect " << DocName << '#' << FeatName << '.' << SubName); } else { FC_TRACE("preselect signal " << DocName << '#' << FeatName << '.' << SubName); } notify(Chng); if (signal == SelectionChanges::MsgSource::Internal && !DocName.empty()) { FC_TRACE("preselect " << DocName << '#' << FeatName << '.' << SubName); Chng.Type = SelectionChanges::SetPreselect; CurrentPreselection = Chng; notify(std::move(Chng)); } // It is possible the preselect is removed during notification return DocName.empty() ? 0 : 1; } namespace Gui { std::array, 3> schemaTranslatePoint( double x, double y, double z, double precision ) { Base::Quantity mmx(Base::Quantity::MilliMetre); mmx.setValue(fabs(x) > precision ? x : 0.0); Base::Quantity mmy(Base::Quantity::MilliMetre); mmy.setValue(fabs(y) > precision ? y : 0.0); Base::Quantity mmz(Base::Quantity::MilliMetre); mmz.setValue(fabs(z) > precision ? z : 0.0); double xfactor, yfactor, zfactor; std::string xunit, yunit, zunit; Base::UnitsApi::schemaTranslate(mmx, xfactor, xunit); Base::UnitsApi::schemaTranslate(mmy, yfactor, yunit); Base::UnitsApi::schemaTranslate(mmz, zfactor, zunit); double xuser = fabs(x) > precision ? x / xfactor : 0.0; double yuser = fabs(y) > precision ? y / yfactor : 0.0; double zuser = fabs(z) > precision ? z / zfactor : 0.0; std::array, 3> ret = {std::make_pair(xuser, xunit), std::make_pair(yuser, yunit), std::make_pair(zuser, zunit)}; return ret; } QString getPreselectionInfo( const char* documentName, const char* objectName, const char* subElementName, float x, float y, float z, double precision ) { auto pts = schemaTranslatePoint(x, y, z, precision); int numberDecimals = std::min(6, static_cast(Base::UnitsApi::getDecimals())); QString message = QStringLiteral("Preselected: %1.%2.%3 (%4 %5, %6 %7, %8 %9)") .arg(QString::fromUtf8(documentName)) .arg(QString::fromUtf8(objectName)) .arg(QString::fromUtf8(subElementName)) .arg(QString::number(pts[0].first, 'f', numberDecimals)) .arg(QString::fromStdString(pts[0].second)) .arg(QString::number(pts[1].first, 'f', numberDecimals)) .arg(QString::fromStdString(pts[1].second)) .arg(QString::number(pts[2].first, 'f', numberDecimals)) .arg(QString::fromStdString(pts[2].second)); return message; } void printPreselectionInfo( const char* documentName, const char* objectName, const char* subElementName, float x, float y, float z, double precision ) { if (getMainWindow()) { QString message = getPreselectionInfo(documentName, objectName, subElementName, x, y, z, precision); getMainWindow()->showMessage(message); } } } // namespace Gui void SelectionSingleton::setPreselectCoord(float x, float y, float z) { // if nothing is in preselect ignore if (CurrentPreselection.Object.getObjectName().empty()) { return; } CurrentPreselection.x = x; CurrentPreselection.y = y; CurrentPreselection.z = z; printPreselectionInfo( CurrentPreselection.pDocName, CurrentPreselection.pObjectName, CurrentPreselection.pSubName, x, y, z, 0.0 ); } void SelectionSingleton::rmvPreselect(bool signal) { if (DocName.empty()) { return; } if (signal) { SelectionChanges Chng(SelectionChanges::RmvPreselectSignal, DocName, FeatName, SubName); notify(std::move(Chng)); return; } SelectionChanges Chng(SelectionChanges::RmvPreselect, DocName, FeatName, SubName); // reset the current preselection CurrentPreselection = SelectionChanges(); DocName = ""; FeatName = ""; SubName = ""; hx = 0; hy = 0; hz = 0; if (ActiveGate && getMainWindow()) { Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->restoreOverrideCursor(); } FC_TRACE("rmv preselect"); // notify observing objects notify(std::move(Chng)); } const SelectionChanges& SelectionSingleton::getPreselection() const { return CurrentPreselection; } // add a SelectionGate to control what is selectable void SelectionSingleton::addSelectionGate(Gui::SelectionGate* gate, ResolveMode resolve) { if (ActiveGate) { rmvSelectionGate(); } ActiveGate = gate; gateResolve = resolve; } // remove the active SelectionGate void SelectionSingleton::rmvSelectionGate() { if (ActiveGate) { delete ActiveGate; ActiveGate = nullptr; Gui::Document* doc = Gui::Application::Instance->activeDocument(); if (doc) { // if a document is about to be closed it has no MDI view any more Gui::MDIView* mdi = doc->getActiveView(); if (mdi) { mdi->restoreOverrideCursor(); } } } } App::Document* SelectionSingleton::getDocument(const char* pDocName) const { if (!Base::Tools::isNullOrEmpty(pDocName)) { return App::GetApplication().getDocument(pDocName); } else { return App::GetApplication().getActiveDocument(); } } int SelectionSingleton::disableCommandLog() { if (!logDisabled) { logHasSelection = hasSelection(); } return ++logDisabled; } int SelectionSingleton::enableCommandLog(bool silent) { --logDisabled; if (!logDisabled && !silent) { auto manager = Application::Instance->macroManager(); if (!hasSelection()) { if (logHasSelection) { manager->addLine(MacroManager::Cmt, "Gui.Selection.clearSelection()"); } } else { for (auto& sel : _SelList) { sel.log(); } } } return logDisabled; } void SelectionSingleton::_SelObj::log(bool remove, bool clearPreselect) { if (logged && !remove) { return; } logged = true; std::ostringstream ss; ss << "Gui.Selection." << (remove ? "removeSelection" : "addSelection") << "('" << DocName << "','" << FeatName << "'"; if (!SubName.empty()) { if (!elementName.oldName.empty() && !elementName.newName.empty()) { ss << ",'" << SubName.substr(0, SubName.size() - elementName.newName.size()) << elementName.oldName << "'"; } else { ss << ",'" << SubName << "'"; } } if (!remove && (x || y || z || !clearPreselect)) { if (SubName.empty()) { ss << ",''"; } ss << ',' << x << ',' << y << ',' << z; if (!clearPreselect) { ss << ",False"; } } ss << ')'; Application::Instance->macroManager()->addLine(MacroManager::Cmt, ss.str().c_str()); } bool SelectionSingleton::addSelection( const char* pDocName, const char* pObjectName, const char* pSubName, float x, float y, float z, const std::vector* pickedList, bool clearPreselect ) { if (pickedList) { _PickedList.clear(); for (const auto& sel : *pickedList) { _PickedList.emplace_back(); auto& s = _PickedList.back(); s.DocName = sel.DocName; s.FeatName = sel.FeatName; s.SubName = sel.SubName; s.TypeName = sel.TypeName; s.pObject = sel.pObject; s.pDoc = sel.pDoc; s.x = sel.x; s.y = sel.y; s.z = sel.z; } notify(SelectionChanges(SelectionChanges::PickedListChanged)); } _SelObj temp; int ret = checkSelection(pDocName, pObjectName, pSubName, ResolveMode::NoResolve, temp); if (ret != 0) { return false; } temp.x = x; temp.y = y; temp.z = z; // check for a Selection Gate if (ActiveGate) { const char* subelement = nullptr; auto pObject = getObjectOfType(temp, App::DocumentObject::getClassTypeId(), gateResolve, &subelement); if (!ActiveGate->allow(pObject ? pObject->getDocument() : temp.pDoc, pObject, subelement)) { if (getMainWindow()) { QString msg; if (ActiveGate->notAllowedReason.length() > 0) { msg = QObject::tr(ActiveGate->notAllowedReason.c_str()); } else { msg = QCoreApplication::translate( "SelectionFilter", "Selection not allowed by filter" ); } getMainWindow()->showMessage(msg); Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->setOverrideCursor(Qt::ForbiddenCursor); } ActiveGate->notAllowedReason.clear(); QApplication::beep(); return false; } } if (!logDisabled) { temp.log(false, clearPreselect); } _SelList.push_back(temp); _SelStackForward.clear(); if (clearPreselect) { rmvPreselect(); } SelectionChanges Chng( SelectionChanges::AddSelection, temp.DocName, temp.FeatName, temp.SubName, temp.TypeName, x, y, z ); FC_LOG( "Add Selection " << Chng.pDocName << '#' << Chng.pObjectName << '.' << Chng.pSubName << " (" << x << ", " << y << ", " << z << ')' ); notify(std::move(Chng)); getMainWindow()->updateActions(); rmvPreselect(true); // There is a possibility that some observer removes or clears selection // inside signal handler, hence the check here return isSelected(temp.DocName.c_str(), temp.FeatName.c_str(), temp.SubName.c_str()); } void SelectionSingleton::selStackPush(bool clearForward, bool overwrite) { static int stackSize; if (!stackSize) { stackSize = App::GetApplication() .GetParameterGroupByPath("User parameter:BaseApp/Preferences/View") ->GetInt("SelectionStackSize", 100); } if (clearForward) { _SelStackForward.clear(); } if (_SelList.empty()) { return; } if ((int)_SelStackBack.size() >= stackSize) { _SelStackBack.pop_front(); } SelStackItem item; for (auto& sel : _SelList) { item.emplace(sel.DocName.c_str(), sel.FeatName.c_str(), sel.SubName.c_str()); } if (!_SelStackBack.empty() && _SelStackBack.back() == item) { return; } if (!overwrite || _SelStackBack.empty()) { _SelStackBack.emplace_back(); } _SelStackBack.back() = std::move(item); } void SelectionSingleton::selStackGoBack(int count) { if ((int)_SelStackBack.size() < count) { count = _SelStackBack.size(); } if (count <= 0) { return; } if (!_SelList.empty()) { selStackPush(false, true); clearCompleteSelection(); } else { --count; } for (int i = 0; i < count; ++i) { _SelStackForward.push_front(std::move(_SelStackBack.back())); _SelStackBack.pop_back(); } std::deque tmpStack; _SelStackForward.swap(tmpStack); while (!_SelStackBack.empty()) { bool found = false; for (auto& sobjT : _SelStackBack.back()) { if (sobjT.getSubObject()) { addSelection( sobjT.getDocumentName().c_str(), sobjT.getObjectName().c_str(), sobjT.getSubName().c_str() ); found = true; } } if (found) { break; } tmpStack.push_front(std::move(_SelStackBack.back())); _SelStackBack.pop_back(); } _SelStackForward = std::move(tmpStack); getMainWindow()->updateActions(); } void SelectionSingleton::selStackGoForward(int count) { if ((int)_SelStackForward.size() < count) { count = _SelStackForward.size(); } if (count <= 0) { return; } if (!_SelList.empty()) { selStackPush(false, true); clearCompleteSelection(); } for (int i = 0; i < count; ++i) { _SelStackBack.push_back(_SelStackForward.front()); _SelStackForward.pop_front(); } std::deque tmpStack; _SelStackForward.swap(tmpStack); while (true) { bool found = false; for (auto& sobjT : _SelStackBack.back()) { if (sobjT.getSubObject()) { addSelection( sobjT.getDocumentName().c_str(), sobjT.getObjectName().c_str(), sobjT.getSubName().c_str() ); found = true; } } if (found || tmpStack.empty()) { break; } _SelStackBack.push_back(tmpStack.front()); tmpStack.pop_front(); } _SelStackForward = std::move(tmpStack); getMainWindow()->updateActions(); } std::vector SelectionSingleton::selStackGet( const char* pDocName, ResolveMode resolve, int index ) const { const SelStackItem* item = nullptr; if (index >= 0) { if (index >= (int)_SelStackBack.size()) { return {}; } item = &_SelStackBack[_SelStackBack.size() - 1 - index]; } else { index = -index - 1; if (index >= (int)_SelStackForward.size()) { return {}; } item = &_SelStackBack[_SelStackForward.size() - 1 - index]; } std::list<_SelObj> selList; for (auto& sobjT : *item) { _SelObj sel; if (checkSelection( sobjT.getDocumentName().c_str(), sobjT.getObjectName().c_str(), sobjT.getSubName().c_str(), ResolveMode::NoResolve, sel, &selList ) == 0) { selList.push_back(sel); } } return getObjectList(pDocName, App::DocumentObject::getClassTypeId(), selList, resolve); } bool SelectionSingleton::addSelections( const char* pDocName, const char* pObjectName, const std::vector& pSubNames ) { if (!_PickedList.empty()) { _PickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } bool update = false; for (const auto& pSubName : pSubNames) { _SelObj temp; int ret = checkSelection(pDocName, pObjectName, pSubName.c_str(), ResolveMode::NoResolve, temp); if (ret != 0) { continue; } temp.x = 0; temp.y = 0; temp.z = 0; _SelList.push_back(temp); _SelStackForward.clear(); SelectionChanges Chng( SelectionChanges::AddSelection, temp.DocName, temp.FeatName, temp.SubName, temp.TypeName ); FC_LOG("Add Selection " << Chng.pDocName << '#' << Chng.pObjectName << '.' << Chng.pSubName); notify(std::move(Chng)); update = true; } if (update) { getMainWindow()->updateActions(); } return true; } bool SelectionSingleton::updateSelection( bool show, const char* pDocName, const char* pObjectName, const char* pSubName ) { if (!pDocName || !pObjectName) { return false; } if (!pSubName) { pSubName = ""; } if (DocName == pDocName && FeatName == pObjectName && SubName == pSubName) { if (show) { FC_TRACE("preselect signal"); notify(SelectionChanges(SelectionChanges::SetPreselectSignal, DocName, FeatName, SubName)); } else { rmvPreselect(); } } auto pDoc = getDocument(pDocName); if (!pDoc) { return false; } auto pObject = pDoc->getObject(pObjectName); if (!pObject) { return false; } if (!isSelected(pObject, pSubName, ResolveMode::NoResolve)) { return false; } SelectionChanges Chng( show ? SelectionChanges::ShowSelection : SelectionChanges::HideSelection, pDocName, pObjectName, pSubName, pObject->getTypeId().getName() ); FC_LOG("Update Selection " << Chng.pDocName << '#' << Chng.pObjectName << '.' << Chng.pSubName); notify(std::move(Chng)); return true; } bool SelectionSingleton::addSelection(const SelectionObject& obj, bool clearPreselect) { const std::vector& subNames = obj.getSubNames(); const std::vector points = obj.getPickedPoints(); if (!subNames.empty() && subNames.size() == points.size()) { bool ok = true; for (std::size_t i = 0; i < subNames.size(); i++) { const std::string& name = subNames[i]; const Base::Vector3d& pnt = points[i]; ok &= addSelection( obj.getDocName(), obj.getFeatName(), name.c_str(), static_cast(pnt.x), static_cast(pnt.y), static_cast(pnt.z), nullptr, clearPreselect ); } return ok; } else if (!subNames.empty()) { bool ok = true; for (const std::string& name : subNames) { ok &= addSelection(obj.getDocName(), obj.getFeatName(), name.c_str()); } return ok; } else { return addSelection(obj.getDocName(), obj.getFeatName()); } } void SelectionSingleton::rmvSelection( const char* pDocName, const char* pObjectName, const char* pSubName, const std::vector* pickedList ) { if (pickedList) { _PickedList.clear(); for (const auto& sel : *pickedList) { _PickedList.emplace_back(); auto& s = _PickedList.back(); s.DocName = sel.DocName; s.FeatName = sel.FeatName; s.SubName = sel.SubName; s.TypeName = sel.TypeName; s.pObject = sel.pObject; s.pDoc = sel.pDoc; s.x = sel.x; s.y = sel.y; s.z = sel.z; } notify(SelectionChanges(SelectionChanges::PickedListChanged)); } if (!pDocName) { return; } _SelObj temp; int ret = checkSelection(pDocName, pObjectName, pSubName, ResolveMode::NoResolve, temp); if (ret < 0) { return; } std::vector changes; for (auto It = _SelList.begin(), ItNext = It; It != _SelList.end(); It = ItNext) { ++ItNext; if (It->DocName != temp.DocName || It->FeatName != temp.FeatName) { continue; } // if no subname is specified, remove all subobjects of the matching object if (!temp.SubName.empty()) { // otherwise, match subojects with common prefix, separated by '.' if (!boost::starts_with(It->SubName, temp.SubName) || (It->SubName.length() != temp.SubName.length() && It->SubName[temp.SubName.length() - 1] != '.')) { continue; } } It->log(true); changes.emplace_back( SelectionChanges::RmvSelection, It->DocName, It->FeatName, It->SubName, It->TypeName ); // destroy the _SelObj item _SelList.erase(It); } // NOTE: It can happen that there are nested calls of rmvSelection() // so that it's not safe to invoke the notifications inside the loop // as this can invalidate the iterators and thus leads to undefined // behaviour. // So, the notification is done after the loop, see also #0003469 if (!changes.empty()) { for (auto& Chng : changes) { FC_LOG( "Rmv Selection " << Chng.pDocName << '#' << Chng.pObjectName << '.' << Chng.pSubName ); notify(std::move(Chng)); } getMainWindow()->updateActions(); } } struct SelInfo { std::string DocName; std::string FeatName; std::string SubName; SelInfo(const std::string& docName, const std::string& featName, const std::string& subName) : DocName(docName) , FeatName(featName) , SubName(subName) {} }; void SelectionSingleton::setVisible(VisibleState vis) { std::set> filter; int visible; switch (vis) { case VisShow: visible = 1; break; case VisToggle: visible = -1; break; default: visible = 0; } // Copy the selection in case it changes during this function std::vector sels; sels.reserve(_SelList.size()); for (auto& sel : _SelList) { if (sel.DocName.empty() || sel.FeatName.empty() || !sel.pObject) { continue; } sels.emplace_back(sel.DocName, sel.FeatName, sel.SubName); } for (auto& sel : sels) { App::Document* doc = App::GetApplication().getDocument(sel.DocName.c_str()); if (!doc) { continue; } App::DocumentObject* obj = doc->getObject(sel.FeatName.c_str()); if (!obj) { continue; } // get parent object App::DocumentObject* parent = nullptr; std::string elementName; obj = obj->resolve(sel.SubName.c_str(), &parent, &elementName); if (!obj || !obj->isAttachedToDocument() || (parent && !parent->isAttachedToDocument())) { continue; } // try call parent object's setElementVisible if (parent) { // prevent setting the same object visibility more than once if (!filter.insert(std::make_pair(obj, parent)).second) { continue; } int visElement = parent->isElementVisible(elementName.c_str()); if (visElement >= 0) { if (visElement > 0) { visElement = 1; } if (visible >= 0) { if (visElement == visible) { continue; } visElement = visible; } else { visElement = !visElement; } if (!visElement) { updateSelection( false, sel.DocName.c_str(), sel.FeatName.c_str(), sel.SubName.c_str() ); } parent->setElementVisible(elementName.c_str(), visElement ? true : false); if (visElement) { updateSelection(true, sel.DocName.c_str(), sel.FeatName.c_str(), sel.SubName.c_str()); } continue; } // Fall back to direct object visibility setting } if (!filter.insert(std::make_pair(obj, static_cast(nullptr))).second) { continue; } auto vp = Application::Instance->getViewProvider(obj); if (vp) { bool visObject; if (visible >= 0) { visObject = visible ? true : false; } else { visObject = !vp->isShow(); } if (visObject) { vp->show(); updateSelection( visObject, sel.DocName.c_str(), sel.FeatName.c_str(), sel.SubName.c_str() ); } else { updateSelection( visObject, sel.DocName.c_str(), sel.FeatName.c_str(), sel.SubName.c_str() ); vp->hide(); } } } } void SelectionSingleton::setSelection(const char* pDocName, const std::vector& sel) { App::Document* pcDoc; pcDoc = getDocument(pDocName); if (!pcDoc) { return; } if (!_PickedList.empty()) { _PickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } bool touched = false; for (auto obj : sel) { if (!obj || !obj->isAttachedToDocument()) { continue; } _SelObj temp; int ret = checkSelection(pDocName, obj->getNameInDocument(), nullptr, ResolveMode::NoResolve, temp); if (ret != 0) { continue; } touched = true; _SelList.push_back(temp); } if (touched) { _SelStackForward.clear(); notify(SelectionChanges(SelectionChanges::SetSelection, pDocName)); getMainWindow()->updateActions(); } } void SelectionSingleton::clearSelection(const char* pDocName, bool clearPreSelect) { // Because the introduction of external editing, it is best to make // clearSelection(0) behave as clearCompleteSelection(), which is the same // behavior of python Selection.clearSelection(None) if (!pDocName || !pDocName[0] || strcmp(pDocName, "*") == 0) { clearCompleteSelection(clearPreSelect); return; } if (!_PickedList.empty()) { _PickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } App::Document* pDoc; pDoc = getDocument(pDocName); if (pDoc) { std::string docName = pDocName; if (clearPreSelect && DocName == docName) { rmvPreselect(); } bool touched = false; for (auto it = _SelList.begin(); it != _SelList.end();) { if (it->DocName == docName) { touched = true; it = _SelList.erase(it); } else { ++it; } } if (!touched) { return; } if (!logDisabled) { std::ostringstream ss; ss << "Gui.Selection.clearSelection('" << docName << "'"; if (!clearPreSelect) { ss << ", False"; } ss << ')'; Application::Instance->macroManager()->addLine(MacroManager::Cmt, ss.str().c_str()); } notify(SelectionChanges(SelectionChanges::ClrSelection, docName.c_str())); getMainWindow()->updateActions(); } } void SelectionSingleton::clearCompleteSelection(bool clearPreSelect) { if (!_PickedList.empty()) { _PickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } if (clearPreSelect) { rmvPreselect(); } if (_SelList.empty()) { return; } if (!logDisabled) { Application::Instance->macroManager()->addLine( MacroManager::Cmt, clearPreSelect ? "Gui.Selection.clearSelection()" : "Gui.Selection.clearSelection(False)" ); } // Send the clear selection notification to all view providers associated with the // objects being deselected. std::set viewProviders; for (_SelObj& sel : _SelList) { if (auto vp = Application::Instance->getViewProvider(sel.pObject)) { viewProviders.insert(vp); } } for (auto& vp : viewProviders) { SelectionChanges Chng(SelectionChanges::ClrSelection); vp->onSelectionChanged(Chng); } _SelList.clear(); SelectionChanges Chng(SelectionChanges::ClrSelection); FC_LOG("Clear selection"); notify(std::move(Chng)); getMainWindow()->updateActions(); } bool SelectionSingleton::isSelected( const char* pDocName, const char* pObjectName, const char* pSubName, ResolveMode resolve ) const { _SelObj sel; return checkSelection(pDocName, pObjectName, pSubName, resolve, sel, &_SelList) > 0; } bool SelectionSingleton::isSelected( App::DocumentObject* pObject, const char* pSubName, ResolveMode resolve ) const { if (!pObject || !pObject->isAttachedToDocument() || !pObject->getDocument()) { return false; } _SelObj sel; return checkSelection( pObject->getDocument()->getName(), pObject->getNameInDocument(), pSubName, resolve, sel, &_SelList ) > 0; } int SelectionSingleton::checkSelection( const char* pDocName, const char* pObjectName, const char* pSubName, ResolveMode resolve, _SelObj& sel, const std::list<_SelObj>* selList ) const { sel.pDoc = getDocument(pDocName); if (!sel.pDoc) { if (!selList) { FC_ERR("Cannot find document"); } return -1; } pDocName = sel.pDoc->getName(); sel.DocName = pDocName; if (pObjectName) { sel.pObject = sel.pDoc->getObject(pObjectName); } else { sel.pObject = nullptr; } if (!sel.pObject) { if (!selList) { FC_ERR("Object not found"); } return -1; } if (sel.pObject->testStatus(App::ObjectStatus::Remove)) { return -1; } if (pSubName) { sel.SubName = pSubName; } if (resolve == ResolveMode::NoResolve) { TreeWidget::checkTopParent(sel.pObject, sel.SubName); } pSubName = !sel.SubName.empty() ? sel.SubName.c_str() : nullptr; sel.FeatName = sel.pObject->getNameInDocument(); sel.TypeName = sel.pObject->getTypeId().getName(); const char* element = nullptr; sel.pResolvedObject = App::GeoFeature::resolveElement( sel.pObject, pSubName, sel.elementName, false, App::GeoFeature::Normal, nullptr, &element ); if (!sel.pResolvedObject) { if (!selList) { FC_ERR( "Sub-object " << sel.DocName << '#' << sel.FeatName << '.' << sel.SubName << " not found" ); } return -1; } if (sel.pResolvedObject->testStatus(App::ObjectStatus::Remove)) { return -1; } std::string subname; std::string prefix; if (pSubName && element) { prefix = std::string(pSubName, element - pSubName); if (!sel.elementName.newName.empty()) { // make sure the selected sub name is a new style if available subname = prefix + sel.elementName.newName; pSubName = subname.c_str(); sel.SubName = subname; } } if (!selList) { selList = &_SelList; } if (!pSubName) { pSubName = ""; } for (auto& s : *selList) { if (s.DocName == pDocName && s.FeatName == sel.FeatName) { if (s.SubName == pSubName) { return 1; } if (resolve > ResolveMode::OldStyleElement && boost::starts_with(s.SubName, prefix)) { return 1; } } } if (resolve == ResolveMode::OldStyleElement) { for (auto& s : *selList) { if (s.pResolvedObject != sel.pResolvedObject) { continue; } if (!pSubName[0]) { return 1; } if (!s.elementName.newName.empty()) { if (s.elementName.newName == sel.elementName.newName) { return 1; } } else if (s.SubName == sel.elementName.oldName) { return 1; } } } return 0; } const char* SelectionSingleton::getSelectedElement(App::DocumentObject* obj, const char* pSubName) const { if (!obj) { return nullptr; } for (list<_SelObj>::const_iterator It = _SelList.begin(); It != _SelList.end(); ++It) { if (It->pObject == obj) { auto len = It->SubName.length(); if (!len) { return ""; } if (pSubName && strncmp(pSubName, It->SubName.c_str(), It->SubName.length()) == 0) { if (pSubName[len] == 0 || pSubName[len - 1] == '.') { return It->SubName.c_str(); } } } } return nullptr; } void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) { if (!Obj.isAttachedToDocument()) { return; } // For safety reason, don't bother checking rmvPreselect(); // Remove also from the selection, if selected // We don't walk down the hierarchy for each selection, so there may be stray selection std::vector changes; for (auto it = _SelList.begin(), itNext = it; it != _SelList.end(); it = itNext) { ++itNext; if (it->pResolvedObject == &Obj || it->pObject == &Obj) { changes.emplace_back( SelectionChanges::RmvSelection, it->DocName, it->FeatName, it->SubName, it->TypeName ); _SelList.erase(it); } } if (!changes.empty()) { for (auto& Chng : changes) { FC_LOG( "Rmv Selection " << Chng.pDocName << '#' << Chng.pObjectName << '.' << Chng.pSubName ); notify(std::move(Chng)); } getMainWindow()->updateActions(); } if (!_PickedList.empty()) { bool changed = false; for (auto it = _PickedList.begin(), itNext = it; it != _PickedList.end(); it = itNext) { ++itNext; auto& sel = *it; if (sel.DocName == Obj.getDocument()->getName() && sel.FeatName == Obj.getNameInDocument()) { changed = true; _PickedList.erase(it); } } if (changed) { notify(SelectionChanges(SelectionChanges::PickedListChanged)); } } } void SelectionSingleton::setSelectionStyle(SelectionStyle selStyle) { selectionStyle = selStyle; } SelectionSingleton::SelectionStyle SelectionSingleton::getSelectionStyle() { return selectionStyle; } //************************************************************************** // Construction/Destruction /** * A constructor. * A more elaborate description of the constructor. */ SelectionSingleton::SelectionSingleton() : CurrentPreselection(SelectionChanges::ClrSelection) , selectionStyle(SelectionStyle::NormalSelection) { hx = 0; hy = 0; hz = 0; ActiveGate = nullptr; gateResolve = ResolveMode::OldStyleElement; // NOLINTBEGIN App::GetApplication().signalDeletedObject.connect( std::bind(&Gui::SelectionSingleton::slotDeletedObject, this, sp::_1) ); signalSelectionChanged.connect( std::bind(&Gui::SelectionSingleton::slotSelectionChanged, this, sp::_1) ); // NOLINTEND } /** * A destructor. * A more elaborate description of the destructor. */ SelectionSingleton::~SelectionSingleton() = default; SelectionSingleton* SelectionSingleton::_pcSingleton = nullptr; SelectionSingleton& SelectionSingleton::instance() { if (!_pcSingleton) { _pcSingleton = new SelectionSingleton; } return *_pcSingleton; } void SelectionSingleton::destruct() { if (_pcSingleton) { delete _pcSingleton; } _pcSingleton = nullptr; } //************************************************************************** // Python stuff // SelectionSingleton Methods // Methods structure PyMethodDef SelectionSingleton::Methods[] = { {"addSelection", (PyCFunction)SelectionSingleton::sAddSelection, METH_VARARGS, "addSelection(docName, objName, subName, x=0, y=0, z=0, clear=True) -> None\n" "addSelection(obj, subName, x=0, y=0, z=0, clear=True) -> None\n" "addSelection(obj, subNames, clear=True) -> None\n" "\n" "Add an object to the selection.\n" "\n" "docName : str\n Name of the `App.Document`.\n" "objName : str\n Name of the `App.DocumentObject` to add.\n" "obj : App.DocumentObject\n Object to add.\n" "subName : str\n Subelement name.\n" "x : float\n Coordinate `x` of the point to pick.\n" "y : float\n Coordinate `y` of the point to pick.\n" "z : float\n Coordinate `z` of the point to pick.\n" "subNames : list of str\n List of subelement names.\n" "clear : bool\n Clear preselection."}, {"updateSelection", (PyCFunction)SelectionSingleton::sUpdateSelection, METH_VARARGS, "updateSelection(show, obj, subName) -> None\n" "\n" "Update an object in the selection.\n" "\n" "show : bool\n Show or hide the selection.\n" "obj : App.DocumentObject\n Object to update.\n" "subName : str\n Name of the subelement to update."}, {"removeSelection", (PyCFunction)SelectionSingleton::sRemoveSelection, METH_VARARGS, "removeSelection(obj, subName) -> None\n" "removeSelection(docName, objName, subName) -> None\n" "\n" "Remove an object from the selection.\n" "\n" "docName : str\n Name of the `App.Document`.\n" "objName : str\n Name of the `App.DocumentObject` to remove.\n" "obj : App.DocumentObject\n Object to remove.\n" "subName : str\n Name of the subelement to remove."}, {"clearSelection", (PyCFunction)SelectionSingleton::sClearSelection, METH_VARARGS, "clearSelection(docName, clearPreSelect=True) -> None\n" "clearSelection(clearPreSelect=True) -> None\n" "\n" "Clear the selection in the given document. If no document is\n" "given the complete selection is cleared.\n" "\n" "docName : str\n Name of the `App.Document`.\n" "clearPreSelect : bool\n Clear preselection."}, {"isSelected", (PyCFunction)SelectionSingleton::sIsSelected, METH_VARARGS, "isSelected(obj, subName, resolve=ResolveMode.OldStyleElement) -> bool\n" "\n" "Check if a given object is selected.\n" "\n" "obj : App.DocumentObject\n Object to check.\n" "subName : str\n Name of the subelement.\n" "resolve : int\n Resolve subelement reference."}, {"setPreselection", reinterpret_cast(reinterpret_cast(SelectionSingleton::sSetPreselection)), METH_VARARGS | METH_KEYWORDS, "setPreselection(obj, subName, x=0, y=0, z=0, type=1) -> None\n" "\n" "Set preselected object.\n" "\n" "obj : App.DocumentObject\n Object to preselect.\n" "subName : str\n Subelement name.\n" "x : float\n Coordinate `x` of the point to preselect.\n" "y : float\n Coordinate `y` of the point to preselect.\n" "z : float\n Coordinate `z` of the point to preselect.\n" "type : int"}, {"getPreselection", (PyCFunction)SelectionSingleton::sGetPreselection, METH_VARARGS, "getPreselection() -> Gui.SelectionObject\n" "\n" "Get preselected object."}, {"clearPreselection", (PyCFunction)SelectionSingleton::sRemPreselection, METH_VARARGS, "clearPreselection() -> None\n" "\n" "Clear the preselection."}, {"countObjectsOfType", (PyCFunction)SelectionSingleton::sCountObjectsOfType, METH_VARARGS, "countObjectsOfType(type, docName, resolve=ResolveMode.OldStyleElement) -> int\n" "\n" "Get the number of selected objects. If no document name is given the\n" "active document is used and '*' means all documents.\n" "\n" "type : str\n Object type id name.\n" "docName : str\n Name of the `App.Document`.\n" "resolve : int"}, {"getSelection", (PyCFunction)SelectionSingleton::sGetSelection, METH_VARARGS, "getSelection(docName, resolve=ResolveMode.OldStyleElement, single=False) -> list\n" "\n" "Return a list of selected objects. If no document name is given\n" "the active document is used and '*' means all documents.\n" "\n" "docName : str\n Name of the `App.Document`.\n" "resolve : int\n Resolve the subname references.\n" " 0: do not resolve, 1: resolve, 2: resolve with element map.\n" "single : bool\n Only return if there is only one selection."}, {"getPickedList", (PyCFunction)SelectionSingleton::sGetPickedList, 1, "getPickedList(docName) -> list of Gui.SelectionObject\n" "\n" "Return a list of SelectionObjects generated by the last mouse click.\n" "If no document name is given the active document is used and '*'\n" "means all documents.\n" "\n" "docName : str\n Name of the `App.Document`."}, {"enablePickedList", (PyCFunction)SelectionSingleton::sEnablePickedList, METH_VARARGS, "enablePickedList(enable=True) -> None\n" "\n" "Enable/disable pick list.\n" "\n" "enable : bool"}, {"getCompleteSelection", (PyCFunction)SelectionSingleton::sGetCompleteSelection, METH_VARARGS, "getCompleteSelection(resolve=ResolveMode.OldStyleElement) -> list\n" "\n" "Return a list of selected objects across all documents.\n" "\n" "resolve : int"}, {"getSelectionEx", (PyCFunction)SelectionSingleton::sGetSelectionEx, METH_VARARGS, "getSelectionEx(docName, resolve=ResolveMode.OldStyleElement, single=False) -> list of " "Gui.SelectionObject\n" "\n" "Return a list of SelectionObjects. If no document name is given the\n" "active document is used and '*' means all documents.\n" "The SelectionObjects contain a variety of information about the selection,\n" "e.g. subelement names.\n" "\n" "docName : str\n Name of the `App.Document`.\n" "resolve : int\n Resolve the subname references.\n" " 0: do not resolve, 1: resolve, 2: resolve with element map.\n" "single : bool\n Only return if there is only one selection."}, {"getSelectionObject", (PyCFunction)SelectionSingleton::sGetSelectionObject, METH_VARARGS, "getSelectionObject(docName, objName, subName, point) -> Gui.SelectionObject\n" "\n" "Return a SelectionObject.\n" "\n" "docName : str\n Name of the `App.Document`.\n" "objName : str\n Name of the `App.DocumentObject` to select.\n" "subName : str\n Subelement name.\n" "point : tuple\n Coordinates of the point to pick."}, {"setSelectionStyle", (PyCFunction)SelectionSingleton::sSetSelectionStyle, METH_VARARGS, "setSelectionStyle(selectionStyle) -> None\n" "\n" "Change the selection style. 0 for normal selection, 1 for greedy selection\n" "\n" "selectionStyle : int"}, {"addObserver", (PyCFunction)SelectionSingleton::sAddSelObserver, METH_VARARGS, "addObserver(object, resolve=ResolveMode.OldStyleElement) -> None\n" "\n" "Install an observer.\n" "\n" "object : object\n Python object instance.\n" "resolve : int"}, {"removeObserver", (PyCFunction)SelectionSingleton::sRemSelObserver, METH_VARARGS, "removeObserver(object) -> None\n" "\n" "Uninstall an observer.\n" "\n" "object : object\n Python object instance."}, {"addSelectionGate", (PyCFunction)SelectionSingleton::sAddSelectionGate, METH_VARARGS, "addSelectionGate(filter, resolve=ResolveMode.OldStyleElement) -> None\n" "\n" "Activate the selection gate.\n" "The selection gate will prohibit all selections that do not match\n" "the given selection criteria.\n" "\n" "filter : str, SelectionFilter, object\n" "resolve : int\n" "\n" "Examples strings are:\n" "Gui.Selection.addSelectionGate('SELECT Part::Feature SUBELEMENT Edge')\n" "Gui.Selection.addSelectionGate('SELECT Robot::RobotObject')\n" "\n" "An instance of SelectionFilter can also be set:\n" "filter = Gui.Selection.Filter('SELECT Part::Feature SUBELEMENT Edge')\n" "Gui.Selection.addSelectionGate(filter)\n" "\n" "The most flexible approach is to write a selection gate class that\n" "implements the method 'allow':\n" "class Gate:\n" " def allow(self,doc,obj,sub):\n" " return (sub[0:4] == 'Face')\n" "Gui.Selection.addSelectionGate(Gate())"}, {"removeSelectionGate", (PyCFunction)SelectionSingleton::sRemoveSelectionGate, METH_VARARGS, "removeSelectionGate() -> None\n" "\n" "Remove the active selection gate."}, {"setVisible", (PyCFunction)SelectionSingleton::sSetVisible, METH_VARARGS, "setVisible(visible=None) -> None\n" "\n" "Set visibility of all selection items.\n" "\n" "visible : bool, None\n If None, then toggle visibility."}, {"pushSelStack", (PyCFunction)SelectionSingleton::sPushSelStack, METH_VARARGS, "pushSelStack(clearForward=True, overwrite=False) -> None\n" "\n" "Push current selection to stack.\n" "\n" "clearForward : bool\n Clear the forward selection stack.\n" "overwrite : bool\n Overwrite the top back selection stack with current selection."}, {"hasSelection", (PyCFunction)SelectionSingleton::sHasSelection, METH_VARARGS, "hasSelection(docName, resolve=ResolveMode.NoResolve) -> bool\n" "\n" "Check if there is any selection. If no document name is given,\n" "checks selections in all documents.\n" "\n" "docName : str\n Name of the `App.Document`.\n" "resolve : int"}, {"hasSubSelection", (PyCFunction)SelectionSingleton::sHasSubSelection, METH_VARARGS, "hasSubSelection(docName, subElement=False) -> bool\n" "\n" "Check if there is any selection with subname. If no document name\n" "is given the active document is used and '*' means all documents.\n" "\n" "docName : str\n Name of the `App.Document`.\n" "subElement : bool"}, {"getSelectionFromStack", (PyCFunction)SelectionSingleton::sGetSelectionFromStack, METH_VARARGS, "getSelectionFromStack(docName, resolve=ResolveMode.OldStyleElement, index=0) -> list of " "Gui.SelectionObject\n" "\n" "Return SelectionObjects from selection stack. If no document name is given\n" "the active document is used and '*' means all documents.\n" "\n" "docName : str\n Name of the `App.Document`.\n" "resolve : int\n Resolve the subname references.\n" " 0: do not resolve, 1: resolve, 2: resolve with element map.\n" "index : int\n Select stack index.\n" " 0: last pushed selection, > 0: trace back, < 0: trace forward."}, {nullptr, nullptr, 0, nullptr} /* Sentinel */ }; PyObject* SelectionSingleton::sAddSelection(PyObject* /*self*/, PyObject* args) { SelectionLogDisabler disabler(true); PyObject* clearPreselect = Py_True; char* objname; char* docname; char* subname = nullptr; float x = 0, y = 0, z = 0; if (PyArg_ParseTuple(args, "ss|sfffO!", &docname, &objname, &subname, &x, &y, &z, &PyBool_Type, &clearPreselect)) { Selection() .addSelection(docname, objname, subname, x, y, z, nullptr, Base::asBoolean(clearPreselect)); Py_Return; } PyErr_Clear(); PyObject* object; subname = nullptr; x = 0, y = 0, z = 0; if (PyArg_ParseTuple( args, "O!|sfffO!", &(App::DocumentObjectPy::Type), &object, &subname, &x, &y, &z, &PyBool_Type, &clearPreselect )) { auto docObjPy = static_cast(object); App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); if (!docObj || !docObj->isAttachedToDocument()) { PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check invalid object"); return nullptr; } Selection().addSelection( docObj->getDocument()->getName(), docObj->getNameInDocument(), subname, x, y, z, nullptr, Base::asBoolean(clearPreselect) ); Py_Return; } PyErr_Clear(); PyObject* sequence; if (PyArg_ParseTuple( args, "O!O|O!", &(App::DocumentObjectPy::Type), &object, &sequence, &PyBool_Type, &clearPreselect )) { auto docObjPy = static_cast(object); App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); if (!docObj || !docObj->isAttachedToDocument()) { PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check invalid object"); return nullptr; } try { if (PyTuple_Check(sequence) || PyList_Check(sequence)) { Py::Sequence list(sequence); for (Py::Sequence::iterator it = list.begin(); it != list.end(); ++it) { std::string subname = static_cast(Py::String(*it)); Selection().addSelection( docObj->getDocument()->getName(), docObj->getNameInDocument(), subname.c_str(), 0, 0, 0, nullptr, Base::asBoolean(clearPreselect) ); } Py_Return; } } catch (const Py::Exception&) { // do nothing here } } PyErr_SetString(PyExc_ValueError, "type must be 'DocumentObject[,subname[,x,y,z]]' or 'DocumentObject, list or tuple of subnames'"); return nullptr; } PyObject* SelectionSingleton::sUpdateSelection(PyObject* /*self*/, PyObject* args) { PyObject* show; PyObject* object; char* subname = nullptr; if (!PyArg_ParseTuple( args, "O!O!|s", &PyBool_Type, &show, &(App::DocumentObjectPy::Type), &object, &subname )) { return nullptr; } auto docObjPy = static_cast(object); App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); if (!docObj || !docObj->isAttachedToDocument()) { PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check invalid object"); return nullptr; } Selection().updateSelection( Base::asBoolean(show), docObj->getDocument()->getName(), docObj->getNameInDocument(), subname ); Py_Return; } PyObject* SelectionSingleton::sRemoveSelection(PyObject* /*self*/, PyObject* args) { SelectionLogDisabler disabler(true); char *docname, *objname; char* subname = nullptr; if (PyArg_ParseTuple(args, "ss|s", &docname, &objname, &subname)) { Selection().rmvSelection(docname, objname, subname); Py_Return; } PyErr_Clear(); PyObject* object; subname = nullptr; if (!PyArg_ParseTuple(args, "O!|s", &(App::DocumentObjectPy::Type), &object, &subname)) { return nullptr; } auto docObjPy = static_cast(object); App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); if (!docObj || !docObj->isAttachedToDocument()) { PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check invalid object"); return nullptr; } Selection().rmvSelection(docObj->getDocument()->getName(), docObj->getNameInDocument(), subname); Py_Return; } PyObject* SelectionSingleton::sClearSelection(PyObject* /*self*/, PyObject* args) { SelectionLogDisabler disabler(true); PyObject* clearPreSelect = Py_True; char* documentName = nullptr; if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &clearPreSelect)) { PyErr_Clear(); if (!PyArg_ParseTuple(args, "|sO!", &documentName, &PyBool_Type, &clearPreSelect)) { return nullptr; } } Selection().clearSelection(documentName, Base::asBoolean(clearPreSelect)); Py_Return; } namespace { ResolveMode toEnum(int value) { switch (value) { case 0: return ResolveMode::NoResolve; case 1: return ResolveMode::OldStyleElement; case 2: return ResolveMode::NewStyleElement; case 3: return ResolveMode::FollowLink; default: throw Base::ValueError("Wrong enum value"); } } } // namespace PyObject* SelectionSingleton::sIsSelected(PyObject* /*self*/, PyObject* args) { PyObject* object; char* subname = nullptr; int resolve = 1; if (!PyArg_ParseTuple(args, "O!|si", &(App::DocumentObjectPy::Type), &object, &subname, &resolve)) { return nullptr; } try { auto docObj = static_cast(object); bool ok = Selection().isSelected(docObj->getDocumentObjectPtr(), subname, toEnum(resolve)); return Py_BuildValue("O", (ok ? Py_True : Py_False)); } catch (const Base::Exception& e) { e.setPyException(); return nullptr; } } PyObject* SelectionSingleton::sCountObjectsOfType(PyObject* /*self*/, PyObject* args) { char* objecttype; char* document = nullptr; int resolve = 1; if (!PyArg_ParseTuple(args, "s|si", &objecttype, &document, &resolve)) { return nullptr; } try { unsigned int count = Selection().countObjectsOfType(objecttype, document, toEnum(resolve)); return PyLong_FromLong(count); } catch (const Base::Exception& e) { e.setPyException(); return nullptr; } } PyObject* SelectionSingleton::sGetSelection(PyObject* /*self*/, PyObject* args) { char* documentName = nullptr; int resolve = 1; PyObject* single = Py_False; if (!PyArg_ParseTuple(args, "|siO!", &documentName, &resolve, &PyBool_Type, &single)) { return nullptr; } try { std::vector sel; sel = Selection().getSelection(documentName, toEnum(resolve), Base::asBoolean(single)); std::set noduplicates; std::vector selectedObjects; // keep the order of selection Py::List list; for (const auto& it : sel) { if (noduplicates.insert(it.pObject).second) { selectedObjects.push_back(it.pObject); } } for (const auto& selectedObject : selectedObjects) { list.append(Py::asObject(selectedObject->getPyObject())); } return Py::new_reference_to(list); } catch (const Base::Exception& e) { e.setPyException(); return nullptr; } catch (Py::Exception&) { return nullptr; } } PyObject* SelectionSingleton::sEnablePickedList(PyObject* /*self*/, PyObject* args) { PyObject* enable = Py_True; if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &enable)) { return nullptr; } Selection().enablePickedList(Base::asBoolean(enable)); Py_Return; } PyObject* SelectionSingleton::sSetPreselection(PyObject* /*self*/, PyObject* args, PyObject* kwd) { PyObject* object; const char* subname = nullptr; float x = 0, y = 0, z = 0; int type = 1; static const std::array kwlist {"obj", "subname", "x", "y", "z", "tp", nullptr}; if (Base::Wrapped_ParseTupleAndKeywords( args, kwd, "O!|sfffi", kwlist, &(App::DocumentObjectPy::Type), &object, &subname, &x, &y, &z, &type )) { auto docObjPy = static_cast(object); App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); if (!docObj || !docObj->isAttachedToDocument()) { PyErr_SetString(Base::PyExc_FC_GeneralError, "Cannot check invalid object"); return nullptr; } Selection().setPreselect( docObj->getDocument()->getName(), docObj->getNameInDocument(), subname, x, y, z, static_cast(type) ); Py_Return; } PyErr_SetString(PyExc_ValueError, "type must be 'DocumentObject[,subname[,x,y,z]]'"); return nullptr; } PyObject* SelectionSingleton::sGetPreselection(PyObject* /*self*/, PyObject* args) { if (!PyArg_ParseTuple(args, "")) { return nullptr; } const SelectionChanges& sel = Selection().getPreselection(); SelectionObject obj(sel); return obj.getPyObject(); } PyObject* SelectionSingleton::sRemPreselection(PyObject* /*self*/, PyObject* args) { if (!PyArg_ParseTuple(args, "")) { return nullptr; } Selection().rmvPreselect(); Py_Return; } PyObject* SelectionSingleton::sGetCompleteSelection(PyObject* /*self*/, PyObject* args) { int resolve = 1; if (!PyArg_ParseTuple(args, "|i", &resolve)) { return nullptr; } try { std::vector sel; sel = Selection().getCompleteSelection(toEnum(resolve)); Py::List list; for (const auto& it : sel) { SelectionObject obj(SelectionChanges( SelectionChanges::AddSelection, it.DocName, it.FeatName, it.SubName, it.TypeName, it.x, it.y, it.z )); list.append(Py::asObject(obj.getPyObject())); } return Py::new_reference_to(list); } catch (const Base::Exception& e) { e.setPyException(); return nullptr; } catch (Py::Exception&) { return nullptr; } } PyObject* SelectionSingleton::sGetSelectionEx(PyObject* /*self*/, PyObject* args) { char* documentName = nullptr; int resolve = 1; PyObject* single = Py_False; if (!PyArg_ParseTuple(args, "|siO!", &documentName, &resolve, &PyBool_Type, &single)) { return nullptr; } try { std::vector sel; sel = Selection().getSelectionEx( documentName, App::DocumentObject::getClassTypeId(), toEnum(resolve), Base::asBoolean(single) ); Py::List list; for (auto& it : sel) { list.append(Py::asObject(it.getPyObject())); } return Py::new_reference_to(list); } catch (const Base::Exception& e) { e.setPyException(); return nullptr; } catch (Py::Exception&) { return nullptr; } } PyObject* SelectionSingleton::sGetPickedList(PyObject* /*self*/, PyObject* args) { char* documentName = nullptr; if (!PyArg_ParseTuple(args, "|s", &documentName)) { return nullptr; } std::vector sel; sel = Selection().getPickedListEx(documentName); try { Py::List list; for (auto& it : sel) { list.append(Py::asObject(it.getPyObject())); } return Py::new_reference_to(list); } catch (Py::Exception&) { return nullptr; } } PyObject* SelectionSingleton::sGetSelectionObject(PyObject* /*self*/, PyObject* args) { char *docName, *objName, *subName; PyObject* tuple = nullptr; if (!PyArg_ParseTuple(args, "sss|O!", &docName, &objName, &subName, &PyTuple_Type, &tuple)) { return nullptr; } try { SelectionObject selObj; selObj.DocName = docName; selObj.FeatName = objName; std::string sub = subName; if (!sub.empty()) { selObj.SubNames.push_back(sub); if (tuple) { Py::Tuple t(tuple); double x = (double)Py::Float(t.getItem(0)); double y = (double)Py::Float(t.getItem(1)); double z = (double)Py::Float(t.getItem(2)); selObj.SelPoses.emplace_back(x, y, z); } } return selObj.getPyObject(); } catch (const Py::Exception&) { return nullptr; } catch (const Base::Exception& e) { e.setPyException(); return nullptr; } } PyObject* SelectionSingleton::sSetSelectionStyle(PyObject* /*self*/, PyObject* args) { int selStyle = 0; if (!PyArg_ParseTuple(args, "i", &selStyle)) { return nullptr; } PY_TRY { Selection().setSelectionStyle( selStyle == 0 ? SelectionStyle::NormalSelection : SelectionStyle::GreedySelection ); Py_Return; } PY_CATCH; } PyObject* SelectionSingleton::sAddSelObserver(PyObject* /*self*/, PyObject* args) { PyObject* o; int resolve = 1; if (!PyArg_ParseTuple(args, "O|i", &o, &resolve)) { return nullptr; } PY_TRY { SelectionObserverPython::addObserver(Py::Object(o), toEnum(resolve)); Py_Return; } PY_CATCH; } PyObject* SelectionSingleton::sRemSelObserver(PyObject* /*self*/, PyObject* args) { PyObject* o; if (!PyArg_ParseTuple(args, "O", &o)) { return nullptr; } PY_TRY { SelectionObserverPython::removeObserver(Py::Object(o)); Py_Return; } PY_CATCH; } PyObject* SelectionSingleton::sAddSelectionGate(PyObject* /*self*/, PyObject* args) { char* filter; int resolve = 1; if (PyArg_ParseTuple(args, "s|i", &filter, &resolve)) { PY_TRY { Selection().addSelectionGate(new SelectionFilterGate(filter), toEnum(resolve)); Py_Return; } PY_CATCH; } PyErr_Clear(); PyObject* filterPy; if (PyArg_ParseTuple(args, "O!|i", SelectionFilterPy::type_object(), &filterPy, resolve)) { PY_TRY { Selection().addSelectionGate( new SelectionFilterGatePython(SelectionFilterPy::cast(filterPy)), toEnum(resolve) ); Py_Return; } PY_CATCH; } PyErr_Clear(); PyObject* gate; if (PyArg_ParseTuple(args, "O|i", &gate, &resolve)) { PY_TRY { Selection().addSelectionGate( new SelectionGatePython(Py::Object(gate, false)), toEnum(resolve) ); Py_Return; } PY_CATCH; } PyErr_SetString(PyExc_ValueError, "Argument is neither string nor SelectionFiler nor SelectionGate"); return nullptr; } PyObject* SelectionSingleton::sRemoveSelectionGate(PyObject* /*self*/, PyObject* args) { if (!PyArg_ParseTuple(args, "")) { return nullptr; } PY_TRY { Selection().rmvSelectionGate(); Py_Return; } PY_CATCH; } PyObject* SelectionSingleton::sSetVisible(PyObject* /*self*/, PyObject* args) { PyObject* visible = Py_None; if (!PyArg_ParseTuple(args, "|O", &visible)) { return nullptr; } PY_TRY { VisibleState vis = VisToggle; Base::PyTypeCheck(&visible, &PyBool_Type); if (visible) { vis = PyObject_IsTrue(visible) ? VisShow : VisHide; } Selection().setVisible(vis); Py_Return; } PY_CATCH; } PyObject* SelectionSingleton::sPushSelStack(PyObject* /*self*/, PyObject* args) { PyObject* clear = Py_True; PyObject* overwrite = Py_False; if (!PyArg_ParseTuple(args, "|O!O!", &PyBool_Type, &clear, &PyBool_Type, &overwrite)) { return nullptr; } Selection().selStackPush(Base::asBoolean(clear), Base::asBoolean(overwrite)); Py_Return; } PyObject* SelectionSingleton::sHasSelection(PyObject* /*self*/, PyObject* args) { const char* doc = nullptr; int resolve = 0; if (!PyArg_ParseTuple(args, "|si", &doc, &resolve)) { return nullptr; } PY_TRY { bool ret; if (doc || resolve > 0) { ret = Selection().hasSelection(doc, toEnum(resolve)); } else { ret = Selection().hasSelection(); } return Py::new_reference_to(Py::Boolean(ret)); } PY_CATCH; } PyObject* SelectionSingleton::sHasSubSelection(PyObject* /*self*/, PyObject* args) { const char* doc = nullptr; PyObject* subElement = Py_False; if (!PyArg_ParseTuple(args, "|sO!", &doc, &PyBool_Type, &subElement)) { return nullptr; } PY_TRY { return Py::new_reference_to( Py::Boolean(Selection().hasSubSelection(doc, Base::asBoolean(subElement))) ); } PY_CATCH; } PyObject* SelectionSingleton::sGetSelectionFromStack(PyObject* /*self*/, PyObject* args) { char* documentName = nullptr; int resolve = 1; int index = 0; if (!PyArg_ParseTuple(args, "|sii", &documentName, &resolve, &index)) { return nullptr; } PY_TRY { Py::List list; for (auto& sel : Selection().selStackGet(documentName, toEnum(resolve), index)) { list.append(Py::asObject(sel.getPyObject())); } return Py::new_reference_to(list); } PY_CATCH; } bool SelectionSingleton::isClarifySelectionActive() { return clarifySelectionActive; } void SelectionSingleton::setClarifySelectionActive(bool active) { clarifySelectionActive = active; }