Download src/Gui/Selection/Selection.cpp from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 96.7 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Gui/Selection/Selection.cpp
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Gui/Selection/Selection.cpp
-
curl -L -o Selection.cpp https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Gui/Selection/Selection.cpp
96.7 kB
| // SPDX-License-Identifier: LGPL-2.1-or-later | |
| /*************************************************************************** | |
| * Copyright (c) 2011 Jürgen Riegel <juergen.riegel@web.de> * | |
| * Copyright (c) 2011 Werner Mayer <wmayer[at]users.sourceforge.net> * | |
| * * | |
| * 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 * | |
| * * | |
| ***************************************************************************/ | |
| 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::SelObj> SelectionSingleton::getCompleteSelection(ResolveMode resolve) const | |
| { | |
| return getSelection("*", resolve); | |
| } | |
| std::vector<SelectionSingleton::SelObj> SelectionSingleton::getSelection( | |
| const char* pDocName, | |
| ResolveMode resolve, | |
| bool single | |
| ) const | |
| { | |
| std::vector<SelObj> 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<App::DocumentObject*, std::set<std::string>> 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::SelObj> SelectionSingleton::getPickedList(const char* pDocName) const | |
| { | |
| std::vector<SelObj> 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<Gui::SelectionObject> SelectionSingleton::getSelectionIn( | |
| App::DocumentObject* container, | |
| Base::Type typeId, | |
| bool single | |
| ) const | |
| { | |
| if (!container) { | |
| return getSelectionEx(nullptr, typeId, ResolveMode::NoResolve, single); | |
| } | |
| std::vector<SelectionObject> sels | |
| = getSelectionEx(nullptr, App::DocumentObject::getClassTypeId(), ResolveMode::NoResolve, single); | |
| std::vector<SelectionObject> ret; | |
| std::map<App::DocumentObject*, size_t> SortMap; | |
| for (auto& sel : sels) { | |
| auto* rootObj = sel.getObject(); | |
| App::Document* doc = rootObj->getDocument(); | |
| std::vector<std::string> 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<std::string> 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<SelectionObject> SelectionSingleton::getSelectionEx( | |
| const char* pDocName, | |
| Base::Type typeId, | |
| ResolveMode resolve, | |
| bool single | |
| ) const | |
| { | |
| return getObjectList(pDocName, typeId, _SelList, resolve, single); | |
| } | |
| std::vector<SelectionObject> SelectionSingleton::getPickedListEx( | |
| const char* pDocName, | |
| Base::Type typeId | |
| ) const | |
| { | |
| return getObjectList(pDocName, typeId, _PickedList, ResolveMode::NoResolve); | |
| } | |
| std::vector<SelectionObject> SelectionSingleton::getObjectList( | |
| const char* pDocName, | |
| Base::Type typeId, | |
| std::list<_SelObj>& objList, | |
| ResolveMode resolve, | |
| bool single | |
| ) const | |
| { | |
| std::vector<SelectionObject> temp; | |
| if (single) { | |
| temp.reserve(1); | |
| } | |
| std::map<App::DocumentObject*, size_t> 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<bool> 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<Gui::SelectionObject> sel = this->getSelectionEx(); | |
| std::vector<App::DocumentObject*> objs; | |
| objs.reserve(sel.size() * 2); | |
| std::vector<std::string> subs; | |
| subs.reserve(sel.size() * 2); | |
| for (auto& selitem : sel) { | |
| App::DocumentObject* obj = selitem.getObject(); | |
| const std::vector<std::string>& 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<App::DocumentObject*> SelectionSingleton::getObjectsOfType( | |
| const Base::Type& typeId, | |
| const char* pDocName, | |
| ResolveMode resolve | |
| ) const | |
| { | |
| std::vector<App::DocumentObject*> temp; | |
| App::Document* pcDoc = nullptr; | |
| if (!pDocName || strcmp(pDocName, "*") != 0) { | |
| pcDoc = getDocument(pDocName); | |
| if (!pcDoc) { | |
| return temp; | |
| } | |
| } | |
| std::set<App::DocumentObject*> 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<App::DocumentObject*> 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<std::pair<double, std::string>, 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<std::pair<double, std::string>, 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<int>(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<SelObj>* 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<SelStackItem> 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<SelStackItem> 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<SelectionObject> 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<std::string>& 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<std::string>& subNames = obj.getSubNames(); | |
| const std::vector<Base::Vector3d> 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<float>(pnt.x), | |
| static_cast<float>(pnt.y), | |
| static_cast<float>(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<SelObj>* 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<SelectionChanges> 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<std::pair<App::DocumentObject*, App::DocumentObject*>> 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<SelInfo> 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<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<App::DocumentObject*>& 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<ViewProvider*> 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<SelectionChanges> 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<PyCFunction>(reinterpret_cast<void (*)()>(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<App::DocumentObjectPy*>(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<App::DocumentObjectPy*>(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<std::string>(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<App::DocumentObjectPy*>(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<App::DocumentObjectPy*>(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<App::DocumentObjectPy*>(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<SelectionSingleton::SelObj> sel; | |
| sel = Selection().getSelection(documentName, toEnum(resolve), Base::asBoolean(single)); | |
| std::set<App::DocumentObject*> noduplicates; | |
| std::vector<App::DocumentObject*> 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<const char*, 7> 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<App::DocumentObjectPy*>(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<SelectionChanges::MsgSource>(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<SelectionSingleton::SelObj> 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<SelectionObject> 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<SelectionObject> 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; | |
| } | |