/*************************************************************************** * 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 "PreCompiled.h" #ifndef _PreComp_ # include # include #endif #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 "Tree.h" #include "ViewProviderDocumentObject.h" FC_LOG_LEVEL_INIT("Selection",false,true,true) using namespace Gui; using namespace std; namespace bp = boost::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.size() && doc->getName()!=DocName) notAllowedReason = "Cannot select external object"; else if(ObjName.size() && 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 ; connectSelection = signal.connect(boost::bind (&SelectionObserver::_onSelectionChanged, this, bp::_1)); if (filterDocName.size()) { 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.size()) Selection().rmvSelectionGate(); } } // ------------------------------------------- std::vector SelectionObserverPython::_instances; SelectionObserverPython::SelectionObserverPython(const Py::Object& obj, ResolveMode resolve) : SelectionObserver(true, resolve), inst(obj) { #undef FC_PY_ELEMENT #define FC_PY_ELEMENT(_name) FC_PY_GetCallable(obj.ptr(),#_name,py_##_name); FC_PY_SEL_OBSERVER } SelectionObserverPython::~SelectionObserverPython() { } void SelectionObserverPython::addObserver(const Py::Object& obj, ResolveMode resolve) { _instances.push_back(new SelectionObserverPython(obj, resolve)); } void SelectionObserverPython::removeObserver(const Py::Object& obj) { SelectionObserverPython* obs=nullptr; for (std::vector::iterator it = _instances.begin(); it != _instances.end(); ++it) { if ((*it)->inst == obj) { obs = *it; _instances.erase(it); break; } } delete obs; } void SelectionObserverPython::onSelectionChanged(const SelectionChanges& msg) { switch (msg.Type) { case SelectionChanges::AddSelection: addSelection(msg); break; case SelectionChanges::RmvSelection: removeSelection(msg); break; case SelectionChanges::SetSelection: setSelection(msg); break; case SelectionChanges::ClrSelection: clearSelection(msg); break; case SelectionChanges::SetPreselect: setPreselection(msg); break; case SelectionChanges::RmvPreselect: removePreselection(msg); break; case SelectionChanges::PickedListChanged: pickedListChanged(); break; default: break; } } void SelectionObserverPython::pickedListChanged() { if(py_pickedListChanged.isNone()) return; Base::PyGILStateLocker lock; try { Py::Callable(py_pickedListChanged).apply(Py::Tuple()); } catch (Py::Exception&) { Base::PyException e; // extract the Python error text e.ReportException(); } } void SelectionObserverPython::addSelection(const SelectionChanges& msg) { if(py_addSelection.isNone()) return; Base::PyGILStateLocker lock; try { Py::Tuple args(4); args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); args.setItem(1, Py::String(msg.pObjectName ? msg.pObjectName : "")); args.setItem(2, Py::String(msg.pSubName ? msg.pSubName : "")); Py::Tuple tuple(3); tuple[0] = Py::Float(msg.x); tuple[1] = Py::Float(msg.y); tuple[2] = Py::Float(msg.z); args.setItem(3, tuple); Base::pyCall(py_addSelection.ptr(),args.ptr()); } catch (Py::Exception&) { Base::PyException e; // extract the Python error text e.ReportException(); } } void SelectionObserverPython::removeSelection(const SelectionChanges& msg) { if(py_removeSelection.isNone()) return; Base::PyGILStateLocker lock; try { Py::Tuple args(3); args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); args.setItem(1, Py::String(msg.pObjectName ? msg.pObjectName : "")); args.setItem(2, Py::String(msg.pSubName ? msg.pSubName : "")); Base::pyCall(py_removeSelection.ptr(),args.ptr()); } catch (Py::Exception&) { Base::PyException e; // extract the Python error text e.ReportException(); } } void SelectionObserverPython::setSelection(const SelectionChanges& msg) { if(py_setSelection.isNone()) return; Base::PyGILStateLocker lock; try { Py::Tuple args(1); args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); Base::pyCall(py_setSelection.ptr(),args.ptr()); } catch (Py::Exception&) { Base::PyException e; // extract the Python error text e.ReportException(); } } void SelectionObserverPython::clearSelection(const SelectionChanges& msg) { if(py_clearSelection.isNone()) return; Base::PyGILStateLocker lock; try { Py::Tuple args(1); args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); Base::pyCall(py_clearSelection.ptr(),args.ptr()); } catch (Py::Exception&) { Base::PyException e; // extract the Python error text e.ReportException(); } } void SelectionObserverPython::setPreselection(const SelectionChanges& msg) { if(py_setPreselection.isNone()) return; Base::PyGILStateLocker lock; try { Py::Tuple args(3); args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); args.setItem(1, Py::String(msg.pObjectName ? msg.pObjectName : "")); args.setItem(2, Py::String(msg.pSubName ? msg.pSubName : "")); Base::pyCall(py_setPreselection.ptr(),args.ptr()); } catch (Py::Exception&) { Base::PyException e; // extract the Python error text e.ReportException(); } } void SelectionObserverPython::removePreselection(const SelectionChanges& msg) { if(py_removePreselection.isNone()) return; Base::PyGILStateLocker lock; try { Py::Tuple args(3); args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); args.setItem(1, Py::String(msg.pObjectName ? msg.pObjectName : "")); args.setItem(2, Py::String(msg.pSubName ? msg.pSubName : "")); Base::pyCall(py_removePreselection.ptr(),args.ptr()); } catch (Py::Exception&) { Base::PyException e; // extract the Python error text e.ReportException(); } } // ------------------------------------------- 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.size()) { 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::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 == Base::Type::badType()) 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.push_back(subelement); temp[it->second].SelPoses.emplace_back(sel.x,sel.y,sel.z); } } else { if (single && temp.size()) { temp.clear(); break; } // create a new entry temp.emplace_back(obj); if (subelement && *subelement) { temp.back().SubNames.push_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)); } } 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.size()) { const auto &msg = NotificationQueue.front(); bool notify; 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(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.size(); } 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 (std::size_t iobj = 0; iobj < sel.size(); iobj++) { Gui::SelectionObject &selitem = sel[iobj]; App::DocumentObject* obj = selitem.getObject(); const std::vector &subnames = selitem.getSubNames(); //whole object is selected if (subnames.size() == 0){ objs.push_back(obj); subs.emplace_back(); } else { for (std::size_t isub = 0; isub < subnames.size(); isub++) { objs.push_back(obj); subs.push_back(subnames[isub]); } } } 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->getNameInDocument()) return nullptr; const char *subname = sel.SubName.c_str(); if (resolve != ResolveMode::NoResolve) { obj = sel.pResolvedObject; if (resolve == ResolveMode::NewStyleElement && sel.elementName.first.size()) subname = sel.elementName.first.c_str(); else subname = sel.elementName.second.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 == Base::Type::badType()) return std::vector(); return getObjectsOfType(typeId, pDocName, resolve); } unsigned int SelectionSingleton::countObjectsOfType(const Base::Type& typeId, const char* pDocName, ResolveMode resolve) const { unsigned int iNbr=0; App::Document *pcDoc = nullptr; if(!pDocName || strcmp(pDocName,"*") != 0) { pcDoc = getDocument(pDocName); if (!pcDoc) return 0; } for (auto &sel : _SelList) { if((!pcDoc||pcDoc==sel.pDoc) && getObjectOfType(sel, typeId, resolve)) iNbr++; } return iNbr; } unsigned int SelectionSingleton::countObjectsOfType(const char* typeName, const char* pDocName, ResolveMode resolve) const { Base::Type typeId = Base::Type::fromName(typeName); if (typeId == Base::Type::badType()) 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().size()) { auto pParent = msg.Object.getObject(); if(!pParent) return; std::pair elementName; auto &newElementName = elementName.first; auto &oldElementName = elementName.second; auto pObject = App::GeoFeature::resolveElement(pParent,msg.pSubName,elementName); if (!pObject) return; SelectionChanges msg2(msg.Type,pObject->getDocument()->getName(), pObject->getNameInDocument(), newElementName.size()?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; std::pair elementName; auto pObject = pDoc->getObject(pObjectName); if(!pObject) return 0; const char *subelement = pSubName; if (gateResolve != ResolveMode::NoResolve) { auto &newElementName = elementName.first; auto &oldElementName = elementName.second; pObject = App::GeoFeature::resolveElement(pObject,pSubName,elementName); if (!pObject) return 0; if (gateResolve > ResolveMode::OldStyleElement) subelement = newElementName.size() ? 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(QString::fromLatin1(" %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 "<, 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; QString 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.toUtf8().constBegin()), std::make_pair(yuser, yunit.toUtf8().constBegin()), std::make_pair(zuser, zunit.toUtf8().constBegin())}; return ret; } } void SelectionSingleton::setPreselectCoord( float x, float y, float z) { static char buf[513]; // if nothing is in preselect ignore if(CurrentPreselection.Object.getObjectName().empty()) return; CurrentPreselection.x = x; CurrentPreselection.y = y; CurrentPreselection.z = z; auto pts = schemaTranslatePoint(x, y, z, 0.0); snprintf(buf,512,"Preselected: %s.%s.%s (%f %s,%f %s,%f %s)" ,CurrentPreselection.pDocName ,CurrentPreselection.pObjectName ,CurrentPreselection.pSubName ,pts[0].first, pts[0].second.c_str() ,pts[1].first, pts[1].second.c_str() ,pts[2].first, pts[2].second.c_str()); if (getMainWindow()) getMainWindow()->showMessage(QString::fromUtf8(buf)); } void SelectionSingleton::rmvPreselect(bool signal) { if (DocName == "") 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(void) 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(void) { 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 (pDocName && pDocName[0]) 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.size()) { if(elementName.second.size() && elementName.first.size()) ss << ",'" << SubName.substr(0,SubName.size()-elementName.first.size()) << elementName.second << "'"; 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 "<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.size() && _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() tmpStack; _SelStackForward.swap(tmpStack); while(_SelStackBack.size()) { 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() tmpStack; _SelStackForward.swap(tmpStack); while(1) { 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.size()) { _PickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } bool update = false; for(std::vector::const_iterator it = pSubNames.begin(); it != pSubNames.end(); ++it) { _SelObj temp; int ret = checkSelection(pDocName, pObjectName, it->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 "<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 "<& 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(pnt.x), static_cast(pnt.y), static_cast(pnt.z),nullptr,clearPreselect); } return ok; } else if (!subNames.empty()) { bool ok = true; for (std::size_t i=0; i *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.size()) { // 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.size()) { for(auto &Chng : changes) { FC_LOG("Rmv Selection "<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->getNameInDocument() || (parent && !parent->getNameInDocument())) 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,(App::DocumentObject*)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.size()) { _PickedList.clear(); notify(SelectionChanges(SelectionChanges::PickedListChanged)); } bool touched = false; for(auto obj : sel) { if(!obj || !obj->getNameInDocument()) 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.size()) { _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.size()) { _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)"); _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->getNameInDocument() || !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.size()?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.first.size()) { // make sure the selected sub name is a new style if available subname = prefix + sel.elementName.first; 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.first.size()) { if (s.elementName.first == sel.elementName.first) return 1; } else if(s.SubName == sel.elementName.second) 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.getNameInDocument()) 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.size()) { for(auto &Chng : changes) { FC_LOG("Rmv Selection "<updateActions(); } if(_PickedList.size()) { 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)); } } //************************************************************************** // Construction/Destruction /** * A constructor. * A more elaborate description of the constructor. */ SelectionSingleton::SelectionSingleton() :CurrentPreselection(SelectionChanges::ClrSelection) ,_needPickedList(false) { hx = 0; hy = 0; hz = 0; ActiveGate = nullptr; gateResolve = ResolveMode::OldStyleElement; App::GetApplication().signalDeletedObject.connect(boost::bind(&Gui::SelectionSingleton::slotDeletedObject, this, bp::_1)); signalSelectionChanged.connect(boost::bind(&Gui::SelectionSingleton::slotSelectionChanged, this, bp::_1)); } /** * A destructor. * A more elaborate description of the destructor. */ SelectionSingleton::~SelectionSingleton() { } SelectionSingleton* SelectionSingleton::_pcSingleton = nullptr; SelectionSingleton& SelectionSingleton::instance(void) { if (_pcSingleton == nullptr) _pcSingleton = new SelectionSingleton; return *_pcSingleton; } void SelectionSingleton::destruct (void) { 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."}, {"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)) { App::DocumentObjectPy* docObjPy = static_cast(object); App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); if (!docObj || !docObj->getNameInDocument()) { 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)) { App::DocumentObjectPy* docObjPy = static_cast(object); App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); if (!docObj || !docObj->getNameInDocument()) { 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; App::DocumentObjectPy* docObjPy = static_cast(object); App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); if (!docObj || !docObj->getNameInDocument()) { 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; App::DocumentObjectPy* docObjPy = static_cast(object); App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); if (!docObj || !docObj->getNameInDocument()) { 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"); } } } 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 { App::DocumentObjectPy* 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 (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) { if (noduplicates.insert(it->pObject).second) { selectedObjects.push_back(it->pObject); } } for (std::vector::iterator it = selectedObjects.begin(); it != selectedObjects.end(); ++it) { 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::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; char* subname = nullptr; float x = 0, y = 0, z = 0; int type = 1; static char *kwlist[] = {"obj","subname","x","y","z","tp",nullptr}; if (PyArg_ParseTupleAndKeywords(args, kwd, "O!|sfffi", kwlist, &(App::DocumentObjectPy::Type),&object,&subname,&x,&y,&z,&type)) { App::DocumentObjectPy* docObjPy = static_cast(object); App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); if (!docObj || !docObj->getNameInDocument()) { 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 (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) { list.append(Py::asObject(it->pObject->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 (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) { 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 (std::vector::iterator it = sel.begin(); it != sel.end(); ++it) { 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::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( static_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; if(visible == Py_None) { vis = VisToggle; } else if (PyBool_Check(visible)) { vis = Base::asBoolean(visible) ? VisShow : VisHide; } else { PyErr_SetString(PyExc_ValueError, "Argument is neither None nor Bool"); return nullptr; } 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, "|sO!", &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; }