diff --git a/src/Gui/Selection.cpp b/src/Gui/Selection.cpp index d11a35d4bb..ac72f2c8be 100644 --- a/src/Gui/Selection.cpp +++ b/src/Gui/Selection.cpp @@ -32,6 +32,8 @@ # include #endif +#include + /// Here the FreeCAD includes sorted by Base,App,Gui...... #include "Application.h" #include "Document.h" @@ -40,24 +42,65 @@ #include "View3DInventor.h" #include #include +#include #include #include #include #include #include +#include #include #include "MainWindow.h" +#include "Tree.h" +#include "ViewProviderDocumentObject.h" +#include "Macro.h" FC_LOG_LEVEL_INIT("Selection",false,true,true) using namespace Gui; using namespace std; -SelectionObserver::SelectionObserver() : blockSelection(false) -{ - attachSelection(); +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,int resolve) + :resolve(resolve),blockSelection(false) +{ + if(attach) + attachSelection(); +} + +SelectionObserver::SelectionObserver(const ViewProviderDocumentObject *vp,bool attach,int resolve) + :resolve(resolve),blockSelection(false) +{ + if(vp && vp->getObject() && vp->getObject()->getDocument()) { + filterDocName = vp->getObject()->getDocument()->getName(); + filterObjName = vp->getObject()->getNameInDocument(); + } + if(attach) + attachSelection(); +} + + SelectionObserver::~SelectionObserver() { detachSelection(); @@ -78,11 +121,22 @@ bool SelectionObserver::isConnectionBlocked() const return blockSelection; } +bool SelectionObserver::isConnectionAttached() const +{ + return connectSelection.connected(); +} + void SelectionObserver::attachSelection() { if (!connectSelection.connected()) { - connectSelection = Selection().signalSelectionChanged.connect(boost::bind + auto &signal = resolve>1?Selection().signalSelectionChanged3:( + resolve?Selection().signalSelectionChanged2: + Selection().signalSelectionChanged); + connectSelection = signal.connect(boost::bind (&SelectionObserver::_onSelectionChanged, this, _1)); + if(filterDocName.size()) + Selection().addSelectionGate( + new SelectionGateFilterExternal(filterDocName.c_str(),filterObjName.c_str())); } } @@ -105,6 +159,8 @@ void SelectionObserver::detachSelection() { if (connectSelection.connected()) { connectSelection.disconnect(); + if(filterDocName.size()) + Selection().rmvSelectionGate(); } } @@ -112,17 +168,21 @@ void SelectionObserver::detachSelection() std::vector SelectionObserverPython::_instances; -SelectionObserverPython::SelectionObserverPython(const Py::Object& obj) : inst(obj) +SelectionObserverPython::SelectionObserverPython(const Py::Object& obj, int 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) +void SelectionObserverPython::addObserver(const Py::Object& obj, int resolve) { - _instances.push_back(new SelectionObserverPython(obj)); + _instances.push_back(new SelectionObserverPython(obj,resolve)); } void SelectionObserverPython::removeObserver(const Py::Object& obj) @@ -162,28 +222,44 @@ void SelectionObserverPython::onSelectionChanged(const SelectionChanges& msg) case SelectionChanges::RmvPreselect: removePreselection(msg); break; + case SelectionChanges::PickedListChanged: + pickedListChanged(); + break; default: break; } } -void SelectionObserverPython::addSelection(const SelectionChanges& msg) +void SelectionObserverPython::pickedListChanged() { + if(py_pickedListChanged.isNone()) + return; Base::PyGILStateLocker lock; try { - if (this->inst.hasAttr(std::string("addSelection"))) { - Py::Callable method(this->inst.getAttr(std::string("addSelection"))); - 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); - method.apply(args); - } + 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 @@ -193,16 +269,15 @@ void SelectionObserverPython::addSelection(const SelectionChanges& msg) void SelectionObserverPython::removeSelection(const SelectionChanges& msg) { + if(py_removeSelection.isNone()) + return; Base::PyGILStateLocker lock; try { - if (this->inst.hasAttr(std::string("removeSelection"))) { - Py::Callable method(this->inst.getAttr(std::string("removeSelection"))); - 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 : "")); - method.apply(args); - } + 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 @@ -212,14 +287,13 @@ void SelectionObserverPython::removeSelection(const SelectionChanges& msg) void SelectionObserverPython::setSelection(const SelectionChanges& msg) { + if(py_setSelection.isNone()) + return; Base::PyGILStateLocker lock; try { - if (this->inst.hasAttr(std::string("setSelection"))) { - Py::Callable method(this->inst.getAttr(std::string("setSelection"))); - Py::Tuple args(1); - args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); - method.apply(args); - } + 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 @@ -229,14 +303,13 @@ void SelectionObserverPython::setSelection(const SelectionChanges& msg) void SelectionObserverPython::clearSelection(const SelectionChanges& msg) { + if(py_clearSelection.isNone()) + return; Base::PyGILStateLocker lock; try { - if (this->inst.hasAttr(std::string("clearSelection"))) { - Py::Callable method(this->inst.getAttr(std::string("clearSelection"))); - Py::Tuple args(1); - args.setItem(0, Py::String(msg.pDocName ? msg.pDocName : "")); - method.apply(args); - } + 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 @@ -246,16 +319,15 @@ void SelectionObserverPython::clearSelection(const SelectionChanges& msg) void SelectionObserverPython::setPreselection(const SelectionChanges& msg) { + if(py_setPreselection.isNone()) + return; Base::PyGILStateLocker lock; try { - if (this->inst.hasAttr(std::string("setPreselection"))) { - Py::Callable method(this->inst.getAttr(std::string("setPreselection"))); - 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 : "")); - method.apply(args); - } + 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 @@ -265,16 +337,15 @@ void SelectionObserverPython::setPreselection(const SelectionChanges& msg) void SelectionObserverPython::removePreselection(const SelectionChanges& msg) { + if(py_removePreselection.isNone()) + return; Base::PyGILStateLocker lock; try { - if (this->inst.hasAttr(std::string("removePreselection"))) { - Py::Callable method(this->inst.getAttr(std::string("removePreselection"))); - 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 : "")); - method.apply(args); - } + 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 @@ -289,42 +360,122 @@ bool SelectionSingleton::hasSelection() const return !_SelList.empty(); } -std::vector SelectionSingleton::getCompleteSelection() const +bool SelectionSingleton::hasPreselection() const { + return !CurrentPreselection.ObjName.empty(); +} + +std::vector SelectionSingleton::getCompleteSelection(int resolve) const +{ + return getSelection("*",resolve); +} + +std::vector SelectionSingleton::getSelection(const char* pDocName, + int resolve, bool single) const { std::vector temp; + if(single) temp.reserve(1); SelObj tempSelObj; - for(std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - 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.pDoc = It->pDoc; + App::Document *pcDoc = 0; + 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 = 0; + 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 && !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; } -std::vector SelectionSingleton::getSelection(const char* pDocName) const +bool SelectionSingleton::hasSelection(const char* doc, bool resolve) const +{ + App::Document *pcDoc = 0; + 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) const +{ + App::Document *pcDoc = 0; + if(!doc || strcmp(doc,"*")!=0) { + pcDoc = getDocument(doc); + if (!pcDoc) + return false; + } + for(auto &sel : _SelList) { + if((!pcDoc || pcDoc == sel.pDoc) + && sel.pObject != sel.pResolvedObject) + { + return true; + } + } + + return false; +} + +std::vector SelectionSingleton::getPickedList(const char* pDocName) const { std::vector temp; SelObj tempSelObj; - App::Document *pcDoc; - pcDoc = getDocument(pDocName); + App::Document *pcDoc = 0; + if(!pDocName || strcmp(pDocName,"*")!=0) { + pcDoc = getDocument(pDocName); + if (!pcDoc) + return temp; + } - if (!pcDoc) - return temp; - - for(std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - if (It->pDoc == pcDoc) { + 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; @@ -336,77 +487,122 @@ std::vector SelectionSingleton::getSelection(const c return temp; } -bool SelectionSingleton::hasSelection(const char* doc) const -{ - App::Document *pcDoc; - pcDoc = getDocument(doc); - if (!pcDoc) - return false; - for(std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - if (It->pDoc == pcDoc) { - return true; - } - } - - return false; +std::vector SelectionSingleton::getSelectionEx( + const char* pDocName, Base::Type typeId, int resolve, bool single) const { + return getObjectList(pDocName,typeId,_SelList,resolve,single); } -std::vector SelectionSingleton::getSelectionEx(const char* pDocName, Base::Type typeId) const +std::vector SelectionSingleton::getPickedListEx(const char* pDocName, Base::Type typeId) const { + return getObjectList(pDocName,typeId,_PickedList,false); +} + +std::vector SelectionSingleton::getObjectList(const char* pDocName, Base::Type typeId, + std::list<_SelObj> &objList, int resolve, bool single) const { std::vector temp; - std::map SortMap; + if(single) temp.reserve(1); + std::map SortMap; // check the type if (typeId == Base::Type::badType()) return temp; - App::Document *pcDoc; - - pcDoc = getDocument(pDocName); - - if (!pcDoc) - return temp; - - for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - if (It->pDoc == pcDoc) { - // right type? - if (It->pObject->getTypeId().isDerivedFrom(typeId)){ - // if the object already has an entry - if (SortMap.find(It->pObject) != SortMap.end()){ - // only add sub-element - if (!It->SubName.empty()) { - SortMap[It->pObject].SubNames.push_back(It->SubName); - SortMap[It->pObject].SelPoses.push_back(Base::Vector3d(It->x,It->y,It->z)); - } - } - else { - // create a new entry - SelectionObject tempSelObj; - tempSelObj.DocName = It->DocName; - tempSelObj.FeatName = It->FeatName; - tempSelObj.TypeName = It->TypeName.c_str(); - if (!It->SubName.empty()) { - tempSelObj.SubNames.push_back(It->SubName); - tempSelObj.SelPoses.push_back(Base::Vector3d(It->x,It->y,It->z)); - } - SortMap.insert(std::pair(It->pObject,tempSelObj)); - } - } - } + App::Document *pcDoc = 0; + if(!pDocName || strcmp(pDocName,"*")!=0) { + pcDoc = getDocument(pDocName); + if (!pcDoc) + return temp; } - // The map loses the order thus we have to go again through the list and pick up the SelectionObject from the map - for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - std::map::iterator Jt = SortMap.find(It->pObject); - if (Jt != SortMap.end()) { - temp.push_back(Jt->second); - SortMap.erase(Jt); + for (auto &sel : objList) { + if(!sel.pDoc) continue; + const char *subelement = 0; + 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 && !temp[it->second]._SubNameSet.insert(subelement).second) + continue; + temp[it->second].SubNames.push_back(subelement); + temp[it->second].SelPoses.push_back(Base::Vector3d(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.push_back(Base::Vector3d(sel.x,sel.y,sel.z)); + if(resolve) + 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,0); + break; + case SelectionChanges::RmvSelection: + notify = !isSelected(msg.pDocName,msg.pObjectName,msg.pSubName,0); + break; + case SelectionChanges::SetPreselect: + notify = CurrentPreselection.Type==SelectionChanges::SetPreselect + && CurrentPreselection.DocName == msg.DocName + && CurrentPreselection.ObjName == msg.ObjName + && CurrentPreselection.SubName == msg.SubName; + break; + case SelectionChanges::RmvPreselect: + notify = CurrentPreselection.Type==SelectionChanges::ClrSelection; + break; + default: + notify = true; + } + if(notify) { + Notify(msg); + signalSelectionChanged(msg); + } + NotificationQueue.pop_front(); + } +} + +bool SelectionSingleton::hasPickedList() const { + return _PickedList.size(); +} + int SelectionSingleton::getAsPropertyLinkSubList(App::PropertyLinkSubList &prop) const { std::vector sel = this->getSelectionEx(); @@ -431,75 +627,173 @@ int SelectionSingleton::getAsPropertyLinkSubList(App::PropertyLinkSubList &prop) return objs.size(); } -vector SelectionSingleton::getObjectsOfType(const Base::Type& typeId, const char* pDocName) const +App::DocumentObject *SelectionSingleton::getObjectOfType(_SelObj &sel, + Base::Type typeId, int resolve, const char **subelement) +{ + auto obj = sel.pObject; + if(!obj || !obj->getNameInDocument()) + return 0; + const char *subname = sel.SubName.c_str(); + if(resolve) { + obj = sel.pResolvedObject; + if(resolve==2 && sel.elementName.first.size()) + subname = sel.elementName.first.c_str(); + else + subname = sel.elementName.second.c_str(); + } + if(!obj) + return 0; + if(!obj->isDerivedFrom(typeId) && + (resolve!=3 || !obj->getLinkedObject(true)->isDerivedFrom(typeId))) + return 0; + if(subelement) *subelement = subname; + return obj; +} + +vector SelectionSingleton::getObjectsOfType(const Base::Type& typeId, const char* pDocName, int resolve) const { std::vector temp; - App::Document *pcDoc; - pcDoc = getDocument(pDocName); + App::Document *pcDoc = 0; + if(!pDocName || strcmp(pDocName,"*")!=0) { + pcDoc = getDocument(pDocName); + if (!pcDoc) + return temp; + } - if (!pcDoc) - return temp; - - for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - if (It->pDoc == pcDoc && It->pObject && It->pObject->getTypeId().isDerivedFrom(typeId)) { - temp.push_back(It->pObject); + 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) const +std::vector SelectionSingleton::getObjectsOfType(const char* typeName, const char* pDocName, int resolve) const { Base::Type typeId = Base::Type::fromName(typeName); if (typeId == Base::Type::badType()) return std::vector(); - return getObjectsOfType(typeId, pDocName); + return getObjectsOfType(typeId, pDocName, resolve); } -unsigned int SelectionSingleton::countObjectsOfType(const Base::Type& typeId, const char* pDocName) const +unsigned int SelectionSingleton::countObjectsOfType(const Base::Type& typeId, const char* pDocName, int resolve) const { unsigned int iNbr=0; - App::Document *pcDoc; + App::Document *pcDoc = 0; + if(!pDocName || strcmp(pDocName,"*")!=0) { + pcDoc = getDocument(pDocName); + if (!pcDoc) + return 0; + } - pcDoc = getDocument(pDocName); - - if (!pcDoc) - return 0; - - for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { - if (It->pDoc == pcDoc && It->pObject && It->pObject->getTypeId().isDerivedFrom(typeId)) { + 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) const +unsigned int SelectionSingleton::countObjectsOfType(const char* typeName, const char* pDocName, int resolve) const { Base::Type typeId = Base::Type::fromName(typeName); if (typeId == Base::Type::badType()) return 0; - return countObjectsOfType(typeId, pDocName); + return countObjectsOfType(typeId, pDocName, resolve); } -bool SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectName, const char* pSubName, float x, float y, float z) + +void SelectionSingleton::slotSelectionChanged(const SelectionChanges& msg) { + if(msg.Type == SelectionChanges::SetPreselectSignal || + msg.Type == SelectionChanges::ShowSelection || + msg.Type == SelectionChanges::HideSelection) + return; + + if(msg.DocName.size() && msg.ObjName.size() && msg.SubName.size()) { + App::Document* pDoc = getDocument(msg.pDocName); + if(!pDoc) return; + std::pair elementName; + auto &newElementName = elementName.first; + auto &oldElementName = elementName.second; + auto pParent = pDoc->getObject(msg.pObjectName); + if(!pParent) return; + 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); + + msg2.pOriginalMsg = &msg; + msg2.pParentObject = pParent; + msg2.pSubObject = pObject; + signalSelectionChanged3(msg2); + + msg2.SubName = oldElementName; + msg2.pSubName = msg2.SubName.c_str(); + signalSelectionChanged2(msg2); + + }else { + signalSelectionChanged3(msg); + signalSelectionChanged2(msg); + } +} + +int SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectName, const char* pSubName, float x, float y, float z, int signal) { - if (DocName != "") + if(!pDocName || !pObjectName) { rmvPreselect(); + return 0; + } + if(!pSubName) pSubName = ""; - if (ActiveGate) { - // if document of object doesn't exist then return with false + if(DocName==pDocName && FeatName==pObjectName && SubName==pSubName) { + // MovePreselect is likely going to slow down large scene rendering. + // Disable it fow now. +#if 0 + if(hx!=x || hy!=y || hz!=z) { + hx = x; + hy = y; + hz = z; + SelectionChanges Chng(SelectionChanges::MovePreselect, + DocName,FeatName,SubName,std::string(),x,y,z); + notify(Chng); + } +#endif + return -1; + } + + rmvPreselect(); + + if (ActiveGate && signal!=1) { App::Document* pDoc = getDocument(pDocName); - if (!pDoc || !pObjectName) - return false; - App::DocumentObject* pObject = pDoc->getObject(pObjectName); - if (!pObject) - return false; + if (!pDoc || !pObjectName) + return 0; + std::pair elementName; + auto pObject = pDoc->getObject(pObjectName); + if(!pObject) + return 0; - if (!ActiveGate->allow(pDoc,pObject,pSubName)) { + const char *subelement = pSubName; + if(gateResolve) { + auto &newElementName = elementName.first; + auto &oldElementName = elementName.second; + pObject = App::GeoFeature::resolveElement(pObject,pSubName,elementName); + if (!pObject) + return 0; + if(gateResolve > 1) + 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()); @@ -516,8 +810,10 @@ bool SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectN Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); mdi->setOverrideCursor(QCursor(Qt::ForbiddenCursor)); } - return false; + return 0; } + Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); + mdi->restoreOverrideCursor(); } DocName = pDocName; @@ -528,24 +824,26 @@ bool SelectionSingleton::setPreselect(const char* pDocName, const char* pObjectN hz = z; // set up the change object - SelectionChanges Chng; - Chng.pDocName = DocName.c_str(); - Chng.pObjectName = FeatName.c_str(); - Chng.pSubName = SubName.c_str(); - Chng.pTypeName = ""; - Chng.x = x; - Chng.y = y; - Chng.z = z; - Chng.Type = SelectionChanges::SetPreselect; + SelectionChanges Chng(signal==1?SelectionChanges::SetPreselectSignal:SelectionChanges::SetPreselect, + DocName,FeatName,SubName,std::string(),x,y,z,signal); - // set the current preselection - CurrentPreselection = Chng; + if(Chng.Type==SelectionChanges::SetPreselect) { + CurrentPreselection = Chng; + FC_TRACE("preselect "<showMessage(QString::fromLatin1(buf)); } -void SelectionSingleton::rmvPreselect() +void SelectionSingleton::rmvPreselect(bool signal) { if (DocName == "") return; - SelectionChanges Chng; - Chng.pDocName = DocName.c_str(); - Chng.pObjectName = FeatName.c_str(); - Chng.pSubName = SubName.c_str(); - Chng.Type = SelectionChanges::RmvPreselect; + 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.pDocName =0; - CurrentPreselection.pObjectName = 0; - CurrentPreselection.pSubName = 0; - CurrentPreselection.x = 0.0; - CurrentPreselection.y = 0.0; - CurrentPreselection.z = 0.0; - - // notify observing objects - Notify(Chng); - signalSelectionChanged(Chng); + CurrentPreselection = SelectionChanges(); DocName = ""; FeatName= ""; @@ -603,7 +894,11 @@ void SelectionSingleton::rmvPreselect() mdi->restoreOverrideCursor(); } - //Base::Console().Log("Sel : Rmv preselect \n"); + FC_TRACE("rmv preselect"); + + // notify observing objects + notify(std::move(Chng)); + } const SelectionChanges &SelectionSingleton::getPreselection(void) const @@ -612,12 +907,13 @@ const SelectionChanges &SelectionSingleton::getPreselection(void) const } // add a SelectionGate to control what is selectable -void SelectionSingleton::addSelectionGate(Gui::SelectionGate *gate) +void SelectionSingleton::addSelectionGate(Gui::SelectionGate *gate, int resolve) { if (ActiveGate) rmvSelectionGate(); - + ActiveGate = gate; + gateResolve = resolve; } // remove the active SelectionGate @@ -637,151 +933,310 @@ void SelectionSingleton::rmvSelectionGate(void) App::Document* SelectionSingleton::getDocument(const char* pDocName) const { - if (pDocName) + if (pDocName && pDocName[0]) return App::GetApplication().getDocument(pDocName); else return App::GetApplication().getActiveDocument(); } -bool SelectionSingleton::addSelection(const char* pDocName, const char* pObjectName, const char* pSubName, float x, float y, float z) +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) { - // already in ? - if (isSelected(pDocName, pObjectName, pSubName)) - return true; + if(pickedList) { + _PickedList.clear(); + for(const auto &sel : *pickedList) { + _PickedList.push_back(_SelObj()); + 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,0,temp); + if(ret!=0) + return false; - temp.pDoc = getDocument(pDocName); + temp.x = x; + temp.y = y; + temp.z = z; - if (temp.pDoc) { - if(pObjectName) - temp.pObject = temp.pDoc->getObject(pObjectName); - else - temp.pObject = 0; - - // check for a Selection Gate - if (ActiveGate) { - if (!ActiveGate->allow(temp.pDoc,temp.pObject,pSubName)) { - 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); + // check for a Selection Gate + if (ActiveGate) { + const char *subelement = 0; + 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"); } - ActiveGate->notAllowedReason.clear(); - QApplication::beep(); - return false; + getMainWindow()->showMessage(msg); + Gui::MDIView* mdi = Gui::Application::Instance->activeDocument()->getActiveView(); + mdi->setOverrideCursor(Qt::ForbiddenCursor); } + ActiveGate->notAllowedReason.clear(); + QApplication::beep(); + return false; } + } - temp.DocName = pDocName; - temp.FeatName = pObjectName ? pObjectName : ""; - temp.SubName = pSubName ? pSubName : ""; - temp.x = x; - temp.y = y; - temp.z = z; + if(!logDisabled) + temp.log(false,clearPreselect); - if (temp.pObject) - temp.TypeName = temp.pObject->getTypeId().getName(); + _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.insert({sel.DocName,sel.FeatName,sel.SubName}); + if(_SelStackBack.size() && _SelStackBack.back()==item) + return; + if(!overwrite || _SelStackBack.empty()) + _SelStackBack.emplace_back(); + _SelStackBack.back().swap(item); +} + +void SelectionSingleton::selStackGoBack(int count) { + if((int)_SelStackBack.size() tmpStack; + _SelStackForward.swap(tmpStack); + while(_SelStackBack.size()) { + bool found = false; + for(auto &n : _SelStackBack.back()) { + if(addSelection(n[0].c_str(), n[1].c_str(), n[2].c_str())) + found = true; + } + if(found) + break; + tmpStack.push_front(_SelStackBack.back()); + _SelStackBack.pop_back(); + } + _SelStackForward.swap(tmpStack); + getMainWindow()->updateActions(); +} + +void SelectionSingleton::selStackGoForward(int count) { + if((int)_SelStackForward.size() tmpStack; + _SelStackForward.swap(tmpStack); + while(1) { + bool found = false; + for(auto &n : _SelStackBack.back()) { + if(addSelection(n[0].c_str(), n[1].c_str(), n[2].c_str())) + found = true; + } + if(found || tmpStack.empty()) + break; + _SelStackBack.push_back(tmpStack.front()); + tmpStack.pop_front(); + } + _SelStackForward.swap(tmpStack); + getMainWindow()->updateActions(); +} + +std::vector SelectionSingleton::selStackGet( + const char* pDocName, int resolve, int index) const +{ + const SelStackItem *item = 0; + 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 &s : *item) { + _SelObj sel; + if(checkSelection(s[0].c_str(),s[1].c_str(),s[2].c_str(),0,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(),0,temp); + if(ret!=0) + continue; + + temp.x = 0; + temp.y = 0; + temp.z = 0; _SelList.push_back(temp); + _SelStackForward.clear(); - SelectionChanges Chng; + SelectionChanges Chng(SelectionChanges::AddSelection, + temp.DocName,temp.FeatName,temp.SubName,temp.TypeName); - Chng.pDocName = pDocName; - Chng.pObjectName = pObjectName ? pObjectName : ""; - Chng.pSubName = pSubName ? pSubName : ""; - Chng.pTypeName = temp.TypeName.c_str(); - Chng.x = x; - Chng.y = y; - Chng.z = z; - Chng.Type = SelectionChanges::AddSelection; + FC_LOG("Add Selection "<updateActions(); + return true; } -bool SelectionSingleton::addSelection(const char* pDocName, const char* pObjectName, const std::vector& pSubNames) +bool SelectionSingleton::updateSelection(bool show, const char* pDocName, + const char* pObjectName, const char* pSubName) { - _SelObj temp; - - temp.pDoc = getDocument(pDocName); - - if (temp.pDoc) { - if(pObjectName) - temp.pObject = temp.pDoc->getObject(pObjectName); - else - temp.pObject = 0; - - if (temp.pObject) - temp.TypeName = temp.pObject->getTypeId().getName(); - - temp.DocName = pDocName; - temp.FeatName = pObjectName ? pObjectName : ""; - if (!pSubNames.empty()) { - for (std::vector::const_iterator it = pSubNames.begin(); it != pSubNames.end(); ++it) { - temp.SubName = it->c_str(); - temp.x = 0; - temp.y = 0; - temp.z = 0; - - _SelList.push_back(temp); - } - } - else { - temp.SubName = ""; - temp.x = 0; - temp.y = 0; - temp.z = 0; - _SelList.push_back(temp); - } - - SelectionChanges Chng; - - Chng.pDocName = pDocName; - Chng.pObjectName = pObjectName ? pObjectName : ""; - Chng.pSubName = ""; - Chng.pTypeName = temp.TypeName.c_str(); - Chng.x = 0; - Chng.y = 0; - Chng.z = 0; - Chng.Type = SelectionChanges::AddSelection; - - Notify(Chng); - signalSelectionChanged(Chng); - - // allow selection - return true; - } - else { - // neither an existing nor active document available - // this can often happen when importing .iv files - Base::Console().Error("Cannot add to selection: no document '%s' found.\n", pDocName); + 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,0)) + 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(); @@ -793,7 +1248,7 @@ bool SelectionSingleton::addSelection(const SelectionObject& obj) ok &= addSelection(obj.getDocName(), obj.getFeatName(), name.c_str(), static_cast(pnt.x), static_cast(pnt.y), - static_cast(pnt.z)); + static_cast(pnt.z),0,clearPreselect); } return ok; } @@ -810,37 +1265,55 @@ bool SelectionSingleton::addSelection(const SelectionObject& obj) } } -void SelectionSingleton::rmvSelection(const char* pDocName, const char* pObjectName, const char* pSubName) + +void SelectionSingleton::rmvSelection(const char* pDocName, const char* pObjectName, const char* pSubName, + const std::vector *pickedList) { - bool foundSelection = false; - std::string tmpDocName; - std::string tmpFeaName; - std::string tmpSubName; - std::string tmpTypName; - - for (std::list<_SelObj>::iterator It = _SelList.begin();It != _SelList.end();) { - if ((It->DocName == pDocName && !pObjectName) || - (It->DocName == pDocName && pObjectName && It->FeatName == pObjectName && !pSubName) || - (It->DocName == pDocName && pObjectName && It->FeatName == pObjectName && pSubName && It->SubName == pSubName)) - { - // save in tmp. string vars - tmpDocName = It->DocName; - tmpFeaName = It->FeatName; - tmpSubName = It->SubName; - tmpTypName = It->TypeName; - - // destroy the _SelObj item - It = _SelList.erase(It); - - foundSelection = true; - -#ifdef FC_DEBUG - Base::Console().Log("Sel : Rmv Selection \"%s.%s.%s\"\n",pDocName,pObjectName,pSubName); -#endif + if(pickedList) { + _PickedList.clear(); + for(const auto &sel : *pickedList) { + _PickedList.push_back(_SelObj()); + 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; } - else { - ++It; + notify(SelectionChanges(SelectionChanges::PickedListChanged)); + } + + if(!pDocName) return; + + _SelObj temp; + int ret = checkSelection(pDocName,pObjectName,pSubName,0,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() @@ -848,15 +1321,76 @@ void SelectionSingleton::rmvSelection(const char* pDocName, const char* pObjectN // as this can invalidate the iterators and thus leads to undefined // behaviour. // So, the notification is done after the loop, see also #0003469 - if (foundSelection) { - SelectionChanges Chng; - Chng.pDocName = tmpDocName.c_str(); - Chng.pObjectName = tmpFeaName.c_str(); - Chng.pSubName = tmpSubName.c_str(); - Chng.pTypeName = tmpTypName.c_str(); - Chng.Type = SelectionChanges::RmvSelection; - Notify(Chng); - signalSelectionChanged(Chng); + if(changes.size()) { + for(auto &Chng : changes) { + FC_LOG("Rmv Selection "<updateActions(); + } +} + +void SelectionSingleton::setVisible(int visible) { + std::set > filter; + if(visible<0) + visible = -1; + else if(visible>0) + visible = 1; + for(auto &sel : _SelList) { + if(sel.DocName.empty() || sel.FeatName.empty() || !sel.pObject) + continue; + // get parent object + App::DocumentObject *parent = 0; + std::string elementName; + auto obj = sel.pObject->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 vis = parent->isElementVisible(elementName.c_str()); + if(vis>=0) { + if(vis>0) vis = 1; + if(visible>=0) { + if(vis == visible) + continue; + vis = visible; + }else + vis = !vis; + + if(!vis) + updateSelection(false,sel.DocName.c_str(),sel.FeatName.c_str(), sel.SubName.c_str()); + parent->setElementVisible(elementName.c_str(),vis?true:false); + if(vis) + 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*)0)).second) + continue; + + auto vp = Application::Instance->getViewProvider(obj); + if(vp) { + int vis; + if(visible>=0) + vis = visible; + else + vis = !vp->isShow(); + + if(vis) { + vp->show(); + updateSelection(vis,sel.DocName.c_str(),sel.FeatName.c_str(), sel.SubName.c_str()); + } else { + updateSelection(vis,sel.DocName.c_str(),sel.FeatName.c_str(), sel.SubName.c_str()); + vp->hide(); + } + } } } @@ -867,149 +1401,259 @@ void SelectionSingleton::setSelection(const char* pDocName, const std::vector cur_sel, new_sel; - new_sel.insert(sel.begin(), sel.end()); - - // Make sure to keep the order of the currently selected objects - std::list<_SelObj> temp; - for (std::list<_SelObj>::const_iterator it = _SelList.begin(); it != _SelList.end(); ++it) { - if (it->pDoc != pcDoc) - temp.push_back(*it); - else { - cur_sel.insert(it->pObject); - if (new_sel.find(it->pObject) != new_sel.end()) - temp.push_back(*it); - } + if(_PickedList.size()) { + _PickedList.clear(); + notify(SelectionChanges(SelectionChanges::PickedListChanged)); } - // Get the objects we must add to the selection - std::vector diff_new_cur; - std::back_insert_iterator< std::vector > biit(diff_new_cur); - std::set_difference(new_sel.begin(), new_sel.end(), cur_sel.begin(), cur_sel.end(), biit); - - _SelObj obj; - for (std::vector::const_iterator it = diff_new_cur.begin(); it != diff_new_cur.end(); ++it) { - obj.pDoc = pcDoc; - obj.pObject = *it; - obj.DocName = pDocName; - obj.FeatName = (*it)->getNameInDocument(); - obj.SubName = ""; - obj.TypeName = (*it)->getTypeId().getName(); - obj.x = 0.0f; - obj.y = 0.0f; - obj.z = 0.0f; - temp.push_back(obj); + bool touched = false; + for(auto obj : sel) { + if(!obj || !obj->getNameInDocument()) + continue; + _SelObj temp; + int ret = checkSelection(pDocName,obj->getNameInDocument(),0,0,temp); + if(ret!=0) + continue; + touched = true; + _SelList.push_back(temp); } - if (cur_sel == new_sel) // nothing has changed - return; - - _SelList = temp; - - SelectionChanges Chng; - Chng.Type = SelectionChanges::SetSelection; - Chng.pDocName = pDocName; - Chng.pObjectName = ""; - Chng.pSubName = ""; - Chng.pTypeName = ""; - - Notify(Chng); - signalSelectionChanged(Chng); + if(touched) { + _SelStackForward.clear(); + notify(SelectionChanges(SelectionChanges::SetSelection,pDocName)); + getMainWindow()->updateActions(); + } } -void SelectionSingleton::clearSelection(const char* pDocName) +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); - - // the document 'pDocName' has already been removed - if (!pDoc && !pDocName) { - clearCompleteSelection(); - } - else { + if(pDoc) { std::string docName; if (pDocName) docName = pDocName; else docName = pDoc->getName(); // active document - std::list<_SelObj> selList; - for (std::list<_SelObj>::iterator it = _SelList.begin(); it != _SelList.end(); ++it) { - if (it->DocName != docName) - selList.push_back(*it); + + 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()); } - _SelList = selList; + notify(SelectionChanges(SelectionChanges::ClrSelection,docName.c_str())); - SelectionChanges Chng; - Chng.Type = SelectionChanges::ClrSelection; - Chng.pDocName = docName.c_str(); - Chng.pObjectName = ""; - Chng.pSubName = ""; - Chng.pTypeName = ""; - - Notify(Chng); - signalSelectionChanged(Chng); - -#ifdef FC_DEBUG - Base::Console().Log("Sel : Clear selection\n"); -#endif + getMainWindow()->updateActions(); } } -void SelectionSingleton::clearCompleteSelection() +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; - Chng.Type = SelectionChanges::ClrSelection; - Chng.pDocName = ""; - Chng.pObjectName = ""; - Chng.pSubName = ""; - Chng.pTypeName = ""; + SelectionChanges Chng(SelectionChanges::ClrSelection); + FC_LOG("Clear selection"); - Notify(Chng); - signalSelectionChanged(Chng); - -#ifdef FC_DEBUG - Base::Console().Log("Sel : Clear selection\n"); -#endif + notify(std::move(Chng)); + getMainWindow()->updateActions(); } -bool SelectionSingleton::isSelected(const char* pDocName, const char* pObjectName, const char* pSubName) const +bool SelectionSingleton::isSelected(const char* pDocName, + const char* pObjectName, const char* pSubName, int resolve) const { - const char* tmpDocName = pDocName ? pDocName : ""; - const char* tmpFeaName = pObjectName ? pObjectName : ""; - const char* tmpSubName = pSubName ? pSubName : ""; - for (std::list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) - if (It->DocName == tmpDocName && It->FeatName == tmpFeaName && It->SubName == tmpSubName) - return true; - return false; + _SelObj sel; + return checkSelection(pDocName,pObjectName,pSubName,resolve,sel,&_SelList)>0; } -bool SelectionSingleton::isSelected(App::DocumentObject* obj, const char* pSubName) const +bool SelectionSingleton::isSelected(App::DocumentObject* pObject, const char* pSubName, int resolve) const { - if (!obj) return false; + 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, int 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 = 0; + if (!sel.pObject) { + if(!selList) + FC_ERR("Object not found"); + return -1; + } + if(pSubName) + sel.SubName = pSubName; + if(!resolve) + TreeWidget::checkTopParent(sel.pObject,sel.SubName); + pSubName = sel.SubName.size()?sel.SubName.c_str():0; + sel.FeatName = sel.pObject->getNameInDocument(); + sel.TypeName = sel.pObject->getTypeId().getName(); + const char *element = 0; + sel.pResolvedObject = App::GeoFeature::resolveElement(sel.pObject, + pSubName,sel.elementName,false,App::GeoFeature::Normal,0,&element); + if(!sel.pResolvedObject) { + if(!selList) + FC_ERR("Sub-object " << sel.DocName << '#' << sel.FeatName << '.' << sel.SubName << " not found"); + 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; + for (auto &s : *selList) { + if (s.DocName==pDocName && s.FeatName==pObjectName) { + if(!pSubName || s.SubName==pSubName) + return 1; + if(resolve>1 && boost::starts_with(s.SubName,prefix)) + return 1; + } + } + if(resolve==1) { + for(auto &s : *selList) { + if(s.pResolvedObject != sel.pResolvedObject) + continue; + if(!pSubName) + 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 0; for(list<_SelObj>::const_iterator It = _SelList.begin();It != _SelList.end();++It) { if (It->pObject == obj) { - if (pSubName) { - if (It->SubName == pSubName) - return true; - } - else { - return true; + 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 false; + return 0; } void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) { - // remove also from the selection, if selected - Selection().rmvSelection( Obj.getDocument()->getName(), Obj.getNameInDocument() ); + if(!Obj.getNameInDocument()) return; + + // For safty 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)); + } } @@ -1021,20 +1665,16 @@ void SelectionSingleton::slotDeletedObject(const App::DocumentObject& Obj) * A more elaborate description of the constructor. */ SelectionSingleton::SelectionSingleton() + :CurrentPreselection(SelectionChanges::ClrSelection) + ,_needPickedList(false) { hx = 0; hy = 0; hz = 0; ActiveGate = 0; + gateResolve = 1; App::GetApplication().signalDeletedObject.connect(boost::bind(&Gui::SelectionSingleton::slotDeletedObject, this, _1)); - CurrentPreselection.Type = SelectionChanges::ClrSelection; - CurrentPreselection.pDocName = 0; - CurrentPreselection.pObjectName = 0; - CurrentPreselection.pSubName = 0; - CurrentPreselection.pTypeName = 0; - CurrentPreselection.x = 0.0; - CurrentPreselection.y = 0.0; - CurrentPreselection.z = 0.0; + signalSelectionChanged.connect(boost::bind(&Gui::SelectionSingleton::slotSelectionChanged, this, _1)); } /** @@ -1069,45 +1709,56 @@ PyMethodDef SelectionSingleton::Methods[] = { {"addSelection", (PyCFunction) SelectionSingleton::sAddSelection, METH_VARARGS, "addSelection(object,[string,float,float,float]) -- Add an object to the selection\n" "where string is the sub-element name and the three floats represent a 3d point"}, + {"updateSelection", (PyCFunction) SelectionSingleton::sUpdateSelection, METH_VARARGS, + "updateSelection(show,object,[string]) -- update an object in the selection\n" + "where string is the sub-element name and the three floats represent a 3d point"}, {"removeSelection", (PyCFunction) SelectionSingleton::sRemoveSelection, METH_VARARGS, "removeSelection(object) -- Remove an object from the selection"}, {"clearSelection" , (PyCFunction) SelectionSingleton::sClearSelection, METH_VARARGS, - "clearSelection([string]) -- Clear the selection\n" + "clearSelection(doc=None,clearPreSelect=True) -- Clear the selection\n" "Clear the selection to the given document name. If no document is\n" "given the complete selection is cleared."}, {"isSelected", (PyCFunction) SelectionSingleton::sIsSelected, METH_VARARGS, - "isSelected(object) -- Check if a given object is selected"}, - {"setPreselection", (PyCFunction) SelectionSingleton::sSetPreselection, METH_VARARGS, + "isSelected(object,resolve=True) -- Check if a given object is selected"}, + {"setPreselection", (PyCFunction) SelectionSingleton::sSetPreselection, METH_VARARGS|METH_KEYWORDS, "setPreselection() -- Set preselected object"}, {"getPreselection", (PyCFunction) SelectionSingleton::sGetPreselection, METH_VARARGS, "getPreselection() -- Get preselected object"}, {"clearPreselection", (PyCFunction) SelectionSingleton::sRemPreselection, METH_VARARGS, "clearPreselection() -- Clear the preselection"}, {"countObjectsOfType", (PyCFunction) SelectionSingleton::sCountObjectsOfType, METH_VARARGS, - "countObjectsOfType(string, [string]) -- Get the number of selected objects\n" + "countObjectsOfType(string, [string],[resolve=1]) -- Get the number of selected objects\n" "The first argument defines the object type e.g. \"Part::Feature\" and the\n" "second argumeht defines the document name. If no document name is given the\n" "currently active document is used"}, {"getSelection", (PyCFunction) SelectionSingleton::sGetSelection, METH_VARARGS, - "getSelection([string]) -- Return a list of selected objects\n" - "Return a list of selected objects for a given document name. If no\n" - "document name is given the selection for the active document is returned."}, + "getSelection(docName=None,resolve=True,single=False) -- Return a list of selected objets\n" + "\ndocName - document name. None means the active document, and '*' means all document" + "\nresolve - whether to resolve the subname references." + "\n 0: do not resolve, 1: resolve, 2: resolve with element map" + "\nsingle - only return if there is only one selection"}, + {"getPickedList", (PyCFunction) SelectionSingleton::sGetPickedList, 1, + "getPickedList(docName=None) -- Return a list of objets under the last mouse click\n" + "\ndocName - document name. None means the active document, and '*' means all document"}, + {"enablePickedList", (PyCFunction) SelectionSingleton::sEnablePickedList, METH_VARARGS, + "enablePickedList(boolean) -- Enable/disable pick list"}, {"getCompleteSelection", (PyCFunction) SelectionSingleton::sGetCompleteSelection, METH_VARARGS, - "getCompleteSelection() -- Return a list of selected objects of all documents."}, + "getCompleteSelection(resolve=True) -- Return a list of selected objects of all documents."}, {"getSelectionEx", (PyCFunction) SelectionSingleton::sGetSelectionEx, METH_VARARGS, - "getSelectionEx([string]) -- Return a list of SelectionObjects\n" - "Return a list of SelectionObjects for a given document name. If no\n" - "document is given the selection of the active document is returned.\n" - "The SelectionObjects contain a variety of information about the selection,\n" - "e.g. sub-element names."}, + "getSelectionEx(docName=None,resolve=1, single=False) -- Return a list of SelectionObjects\n" + "\ndocName - document name. None means the active document, and '*' means all document" + "\nresolve - whether to resolve the subname references." + "\n 0: do not resolve, 1: resolve, 2: resolve with element map" + "\nsingle - only return if there is only one selection\n" + "\nThe SelectionObjects contain a variety of information about the selection, e.g. sub-element names."}, {"getSelectionObject", (PyCFunction) SelectionSingleton::sGetSelectionObject, METH_VARARGS, "getSelectionObject(doc,obj,sub,(x,y,z)) -- Return a SelectionObject"}, {"addObserver", (PyCFunction) SelectionSingleton::sAddSelObserver, METH_VARARGS, - "addObserver(Object) -- Install an observer\n"}, + "addObserver(Object, resolve=True) -- Install an observer\n"}, {"removeObserver", (PyCFunction) SelectionSingleton::sRemSelObserver, METH_VARARGS, "removeObserver(Object) -- Uninstall an observer\n"}, {"addSelectionGate", (PyCFunction) SelectionSingleton::sAddSelectionGate, METH_VARARGS, - "addSelectionGate(String|Filter|Gate) -- activate the selection gate.\n" + "addSelectionGate(String|Filter|Gate, resolve=True) -- activate the selection gate.\n" "The selection gate will prohibit all selections which do not match\n" "the given selection filter string.\n" " Examples strings are:\n" @@ -1126,15 +1777,49 @@ PyMethodDef SelectionSingleton::Methods[] = { "Gui.Selection.addSelectionGate(Gate())"}, {"removeSelectionGate", (PyCFunction) SelectionSingleton::sRemoveSelectionGate, METH_VARARGS, "removeSelectionGate() -- remove the active selection gate\n"}, + {"setVisible", (PyCFunction) SelectionSingleton::sSetVisible, METH_VARARGS, + "setVisible(visible=None) -- set visibility of all selection items\n" + "If 'visible' is None, then toggle visibility"}, + {"pushSelStack", (PyCFunction) SelectionSingleton::sPushSelStack, METH_VARARGS, + "pushSelStack(clearForward=True, overwrite=False) -- push current selection to stack\n\n" + "clearForward: whether to clear the forward selection stack.\n" + "overwrite: overwrite the top back selection stack with current selection."}, + {"hasSelection", (PyCFunction) SelectionSingleton::sHasSelection, METH_VARARGS, + "hasSelection(docName=None, resolve=False) -- check if there is any selection\n"}, + {"hasSubSelection", (PyCFunction) SelectionSingleton::sHasSubSelection, METH_VARARGS, + "hasSubSelection(docName=None) -- check if there is any selection with subname\n"}, + {"getSelectionFromStack",(PyCFunction) SelectionSingleton::sGetSelectionFromStack, METH_VARARGS, + "getSelectionFromStack(docName=None,resolve=1,index=0) -- Return a list of SelectionObjects from selection stack\n" + "\ndocName - document name. None means the active document, and '*' means all document" + "\nresolve - whether to resolve the subname references." + "\n 0: do not resolve, 1: resolve, 2: resolve with element map" + "\nindex - select stack index, 0 is the last pushed selection, positive index to trace further back,\n" + " and negative for forward stack item"}, {NULL, NULL, 0, NULL} /* Sentinel */ }; PyObject *SelectionSingleton::sAddSelection(PyObject * /*self*/, PyObject *args) { - PyObject *object; + SelectionLogDisabler disabler(true); + PyObject *clearPreselect = Py_True; + char *objname; + char *docname; char* subname=0; float x=0,y=0,z=0; - if (PyArg_ParseTuple(args, "O!|sfff", &(App::DocumentObjectPy::Type),&object,&subname,&x,&y,&z)) { + if (PyArg_ParseTuple(args, "ss|sfffO!", &docname, &objname , + &subname,&x,&y,&z,&PyBool_Type,&clearPreselect)) + { + Selection().addSelection(docname,objname,subname,x,y,z,0,PyObject_IsTrue(clearPreselect)); + Py_Return; + } + PyErr_Clear(); + + PyObject *object; + subname = 0; + 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()) { @@ -1144,13 +1829,15 @@ PyObject *SelectionSingleton::sAddSelection(PyObject * /*self*/, PyObject *args) Selection().addSelection(docObj->getDocument()->getName(), docObj->getNameInDocument(), - subname,x,y,z); + subname,x,y,z,0,PyObject_IsTrue(clearPreselect)); Py_Return; } PyErr_Clear(); PyObject *sequence; - if (PyArg_ParseTuple(args, "O!O", &(App::DocumentObjectPy::Type),&object,&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()) { @@ -1165,7 +1852,7 @@ PyObject *SelectionSingleton::sAddSelection(PyObject * /*self*/, PyObject *args) std::string subname = static_cast(Py::String(*it)); Selection().addSelection(docObj->getDocument()->getName(), docObj->getNameInDocument(), - subname.c_str()); + subname.c_str(),0,0,0,0,PyObject_IsTrue(clearPreselect)); } Py_Return; @@ -1180,10 +1867,39 @@ PyObject *SelectionSingleton::sAddSelection(PyObject * /*self*/, PyObject *args) return 0; } -PyObject *SelectionSingleton::sRemoveSelection(PyObject * /*self*/, PyObject *args) +PyObject *SelectionSingleton::sUpdateSelection(PyObject * /*self*/, PyObject *args) { + PyObject *show; PyObject *object; char* subname=0; + if(!PyArg_ParseTuple(args, "OO!|s", &show,&(App::DocumentObjectPy::Type),&object,&subname)) + return 0; + App::DocumentObjectPy* docObjPy = static_cast(object); + App::DocumentObject* docObj = docObjPy->getDocumentObjectPtr(); + if (!docObj || !docObj->getNameInDocument()) { + PyErr_SetString(Base::BaseExceptionFreeCADError, "Cannot check invalid object"); + return NULL; + } + + Selection().updateSelection(PyObject_IsTrue(show), + docObj->getDocument()->getName(), docObj->getNameInDocument(), subname); + Py_Return; +} + + +PyObject *SelectionSingleton::sRemoveSelection(PyObject * /*self*/, PyObject *args) +{ + SelectionLogDisabler disabler(true); + char *docname,*objname; + char* subname=0; + if(PyArg_ParseTuple(args, "ss|s", &docname,&objname,&subname)) { + Selection().rmvSelection(docname,objname,subname); + Py_Return; + } + PyErr_Clear(); + + PyObject *object; + subname = 0; if (!PyArg_ParseTuple(args, "O!|s", &(App::DocumentObjectPy::Type),&object,&subname)) return NULL; @@ -1203,10 +1919,15 @@ PyObject *SelectionSingleton::sRemoveSelection(PyObject * /*self*/, PyObject *ar PyObject *SelectionSingleton::sClearSelection(PyObject * /*self*/, PyObject *args) { + SelectionLogDisabler disabler(true); + PyObject *clearPreSelect = Py_True; char *documentName=0; - if (!PyArg_ParseTuple(args, "|s", &documentName)) - return NULL; - documentName ? Selection().clearSelection(documentName) : Selection().clearCompleteSelection(); + if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &clearPreSelect)) { + PyErr_Clear(); + if (!PyArg_ParseTuple(args, "|sO!", &documentName, &PyBool_Type, &clearPreSelect)) + return NULL; + } + Selection().clearSelection(documentName,PyObject_IsTrue(clearPreSelect)); Py_Return; } @@ -1214,11 +1935,12 @@ PyObject *SelectionSingleton::sIsSelected(PyObject * /*self*/, PyObject *args) { PyObject *object; char* subname=0; - if (!PyArg_ParseTuple(args, "O!|s", &(App::DocumentObjectPy::Type), &object, &subname)) + PyObject *resolve = Py_True; + if (!PyArg_ParseTuple(args, "O!|sO", &(App::DocumentObjectPy::Type), &object, &subname,&resolve)) return NULL; App::DocumentObjectPy* docObj = static_cast(object); - bool ok = Selection().isSelected(docObj->getDocumentObjectPtr(), subname); + bool ok = Selection().isSelected(docObj->getDocumentObjectPtr(), subname,PyObject_IsTrue(resolve)); return Py_BuildValue("O", (ok ? Py_True : Py_False)); } @@ -1226,10 +1948,11 @@ PyObject *SelectionSingleton::sCountObjectsOfType(PyObject * /*self*/, PyObject { char* objecttype; char* document=0; - if (!PyArg_ParseTuple(args, "s|s", &objecttype, &document)) + int resolve = 1; + if (!PyArg_ParseTuple(args, "s|si", &objecttype, &document,&resolve)) return NULL; - unsigned int count = Selection().countObjectsOfType(objecttype, document); + unsigned int count = Selection().countObjectsOfType(objecttype, document, resolve); #if PY_MAJOR_VERSION < 3 return PyInt_FromLong(count); #else @@ -1240,11 +1963,13 @@ PyObject *SelectionSingleton::sCountObjectsOfType(PyObject * /*self*/, PyObject PyObject *SelectionSingleton::sGetSelection(PyObject * /*self*/, PyObject *args) { char *documentName=0; - if (!PyArg_ParseTuple(args, "|s", &documentName)) - return NULL; + int resolve = 1; + PyObject *single=Py_False; + if (!PyArg_ParseTuple(args, "|siO", &documentName,&resolve,&single)) // convert args: Python->C + return NULL; // NULL triggers exception std::vector sel; - sel = Selection().getSelection(documentName); + sel = Selection().getSelection(documentName,resolve,PyObject_IsTrue(single)); try { std::set noduplicates; @@ -1265,12 +1990,25 @@ PyObject *SelectionSingleton::sGetSelection(PyObject * /*self*/, PyObject *args) } } -PyObject *SelectionSingleton::sSetPreselection(PyObject * /*self*/, PyObject *args) +PyObject *SelectionSingleton::sEnablePickedList(PyObject * /*self*/, PyObject *args) +{ + PyObject *enable = Py_True; + if (!PyArg_ParseTuple(args, "|O", &enable)) // convert args: Python->C + return NULL; // NULL triggers exception + + Selection().enablePickedList(PyObject_IsTrue(enable)); + Py_Return; +} + +PyObject *SelectionSingleton::sSetPreselection(PyObject * /*self*/, PyObject *args, PyObject *kwd) { PyObject *object; char* subname=0; float x=0,y=0,z=0; - if (PyArg_ParseTuple(args, "O!|sfff", &(App::DocumentObjectPy::Type),&object,&subname,&x,&y,&z)) { + int type=1; + static char *kwlist[] = {"obj","subname","x","y","z","tp",0}; + 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()) { @@ -1280,7 +2018,7 @@ PyObject *SelectionSingleton::sSetPreselection(PyObject * /*self*/, PyObject *ar Selection().setPreselect(docObj->getDocument()->getName(), docObj->getNameInDocument(), - subname,x,y,z); + subname,x,y,z,type); Py_Return; } @@ -1309,11 +2047,12 @@ PyObject *SelectionSingleton::sRemPreselection(PyObject * /*self*/, PyObject *ar PyObject *SelectionSingleton::sGetCompleteSelection(PyObject * /*self*/, PyObject *args) { - if (!PyArg_ParseTuple(args, "")) - return NULL; + int resolve = 1; + if (!PyArg_ParseTuple(args, "|i",&resolve)) // convert args: Python->C + return NULL; // NULL triggers exception std::vector sel; - sel = Selection().getCompleteSelection(); + sel = Selection().getCompleteSelection(resolve); try { Py::List list; @@ -1330,11 +2069,35 @@ PyObject *SelectionSingleton::sGetCompleteSelection(PyObject * /*self*/, PyObjec PyObject *SelectionSingleton::sGetSelectionEx(PyObject * /*self*/, PyObject *args) { char *documentName=0; - if (!PyArg_ParseTuple(args, "|s", &documentName)) - return NULL; + int resolve=1; + PyObject *single = Py_False; + if (!PyArg_ParseTuple(args, "|siO", &documentName,&resolve,&single)) // convert args: Python->C + return NULL; // NULL triggers exception std::vector sel; - sel = Selection().getSelectionEx(documentName); + sel = Selection().getSelectionEx(documentName, + App::DocumentObject::getClassTypeId(),resolve,PyObject_IsTrue(single)); + + 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 0; + } +} + +PyObject *SelectionSingleton::sGetPickedList(PyObject * /*self*/, PyObject *args) +{ + char *documentName=0; + if (!PyArg_ParseTuple(args, "|s", &documentName)) // convert args: Python->C + return NULL; // NULL triggers exception + + std::vector sel; + sel = Selection().getPickedListEx(documentName); try { Py::List list; @@ -1386,10 +2149,11 @@ PyObject *SelectionSingleton::sGetSelectionObject(PyObject * /*self*/, PyObject PyObject *SelectionSingleton::sAddSelObserver(PyObject * /*self*/, PyObject *args) { PyObject* o; - if (!PyArg_ParseTuple(args, "O",&o)) + int resolve = 1; + if (!PyArg_ParseTuple(args, "O|i",&o,&resolve)) return NULL; PY_TRY { - SelectionObserverPython::addObserver(Py::Object(o)); + SelectionObserverPython::addObserver(Py::Object(o),resolve); Py_Return; } PY_CATCH; } @@ -1408,27 +2172,29 @@ PyObject *SelectionSingleton::sRemSelObserver(PyObject * /*self*/, PyObject *arg PyObject *SelectionSingleton::sAddSelectionGate(PyObject * /*self*/, PyObject *args) { char* filter; - if (PyArg_ParseTuple(args, "s",&filter)) { + int resolve = 1; + if (PyArg_ParseTuple(args, "s|i",&filter,&resolve)) { PY_TRY { - Selection().addSelectionGate(new SelectionFilterGate(filter)); + Selection().addSelectionGate(new SelectionFilterGate(filter),resolve); Py_Return; } PY_CATCH; } PyErr_Clear(); PyObject* filterPy; - if (PyArg_ParseTuple(args, "O!",SelectionFilterPy::type_object(),&filterPy)) { + if (PyArg_ParseTuple(args, "O!|i",SelectionFilterPy::type_object(),&filterPy,resolve)) { PY_TRY { - Selection().addSelectionGate(new SelectionFilterGatePython(static_cast(filterPy))); + Selection().addSelectionGate(new SelectionFilterGatePython( + static_cast(filterPy)),resolve); Py_Return; } PY_CATCH; } PyErr_Clear(); PyObject* gate; - if (PyArg_ParseTuple(args, "O",&gate)) { + if (PyArg_ParseTuple(args, "O|i",&gate,&resolve)) { PY_TRY { - Selection().addSelectionGate(new SelectionGatePython(Py::Object(gate, false))); + Selection().addSelectionGate(new SelectionGatePython(Py::Object(gate, false)),resolve); Py_Return; } PY_CATCH; } @@ -1448,3 +2214,77 @@ PyObject *SelectionSingleton::sRemoveSelectionGate(PyObject * /*self*/, PyObject Py_Return; } + +PyObject *SelectionSingleton::sSetVisible(PyObject * /*self*/, PyObject *args) +{ + PyObject *visible = Py_None; + if (!PyArg_ParseTuple(args, "|O",&visible)) + return NULL; // NULL triggers exception + + PY_TRY { + int vis; + if(visible == Py_None) + vis = -1; + else + vis = PyObject_IsTrue(visible)?1:0; + Selection().setVisible(vis); + } PY_CATCH; + + Py_Return; +} + +PyObject *SelectionSingleton::sPushSelStack(PyObject * /*self*/, PyObject *args) +{ + PyObject *clear = Py_True; + PyObject *overwrite = Py_False; + if (!PyArg_ParseTuple(args, "|OO",&clear,&overwrite)) + return NULL; // NULL triggers exception + + Selection().selStackPush(PyObject_IsTrue(clear),PyObject_IsTrue(overwrite)); + Py_Return; +} + +PyObject *SelectionSingleton::sHasSelection(PyObject * /*self*/, PyObject *args) +{ + const char *doc = 0; + PyObject *resolve = Py_False; + if (!PyArg_ParseTuple(args, "|sO",&doc,&resolve)) + return NULL; // NULL triggers exception + + PY_TRY { + bool ret; + if(doc || PyObject_IsTrue(resolve)) + ret = Selection().hasSelection(doc,PyObject_IsTrue(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 = 0; + if (!PyArg_ParseTuple(args, "|s",&doc)) + return NULL; // NULL triggers exception + + PY_TRY { + return Py::new_reference_to(Py::Boolean(Selection().hasSubSelection(doc))); + } PY_CATCH; +} + +PyObject *SelectionSingleton::sGetSelectionFromStack(PyObject * /*self*/, PyObject *args) +{ + char *documentName=0; + int resolve=1; + int index=0; + if (!PyArg_ParseTuple(args, "|sii", &documentName,&resolve,&index)) // convert args: Python->C + return NULL; // NULL triggers exception + + PY_TRY { + Py::List list; + for(auto &sel : Selection().selStackGet(documentName, resolve, index)) + list.append(Py::asObject(sel.getPyObject())); + return Py::new_reference_to(list); + } PY_CATCH; +} + diff --git a/src/Gui/Selection.h b/src/Gui/Selection.h index 2a8c87e731..feea25b1e7 100644 --- a/src/Gui/Selection.h +++ b/src/Gui/Selection.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -68,20 +69,102 @@ public: RmvSelection, SetSelection, ClrSelection, - SetPreselect, - RmvPreselect + SetPreselect, // to signal observer the preselect has changed + RmvPreselect, + SetPreselectSignal, // to request 3D view to change preselect + PickedListChanged, + ShowSelection, // to show a selection + HideSelection, // to hide a selection + RmvPreselectSignal, // to request 3D view to remove preselect + MovePreselect, // to signal observer the mouse movement when preselect }; - SelectionChanges() - : Type(ClrSelection) - , pDocName(0) - , pObjectName(0) - , pSubName(0) - , pTypeName(0) - , x(0),y(0),z(0) + + SelectionChanges(MsgType type = ClrSelection, + const char *docName=0, const char *objName=0, + const char *subName=0, const char *typeName=0, + float x=0, float y=0, float z=0, int subtype=0) + : Type(type),SubType(subtype) + , x(x),y(y),z(z) { + if(docName) DocName=docName; + pDocName = DocName.c_str(); + if(objName) ObjName = objName; + pObjectName = ObjName.c_str(); + if(subName) SubName = subName; + pSubName = SubName.c_str(); + if(typeName) TypeName = typeName; + pTypeName = TypeName.c_str(); + } + + SelectionChanges(MsgType type, + const std::string &docName, + const std::string &objName, + const std::string &subName, + const std::string &typeName = std::string(), + float x=0,float y=0,float z=0, int subtype=0) + : Type(type), SubType(subtype) + , x(x),y(y),z(z) + , DocName(docName) + , ObjName(objName) + , SubName(subName) + , TypeName(typeName) + { + pDocName = DocName.c_str(); + pObjectName = ObjName.c_str(); + pSubName = SubName.c_str(); + pTypeName = TypeName.c_str(); + } + + SelectionChanges(const SelectionChanges &other) { + *this = other; + } + + SelectionChanges &operator=(const SelectionChanges &other) { + Type = other.Type; + SubType = other.SubType; + x = other.x; + y = other.y; + z = other.z; + DocName = other.DocName; + ObjName = other.ObjName; + SubName = other.SubName; + TypeName = other.TypeName; + pDocName = DocName.c_str(); + pObjectName = ObjName.c_str(); + pSubName = SubName.c_str(); + pTypeName = TypeName.c_str(); + pOriginalMsg = other.pOriginalMsg; + pSubObject = other.pSubObject; + pParentObject = other.pParentObject; + return *this; + } + + SelectionChanges(SelectionChanges &&other) { + *this = std::move(other); + } + + SelectionChanges &operator=(SelectionChanges &&other) { + Type = other.Type; + SubType = other.SubType; + x = other.x; + y = other.y; + z = other.z; + DocName = std::move(other.DocName); + ObjName = std::move(other.ObjName); + SubName = std::move(other.SubName); + TypeName = std::move(other.TypeName); + pDocName = DocName.c_str(); + pObjectName = ObjName.c_str(); + pSubName = SubName.c_str(); + pTypeName = TypeName.c_str(); + pOriginalMsg = other.pOriginalMsg; + pSubObject = other.pSubObject; + pParentObject = other.pParentObject; + return *this; } MsgType Type; + int SubType; const char* pDocName; const char* pObjectName; @@ -90,6 +173,22 @@ public: float x; float y; float z; + + // For more robust selection notification (e.g. in case user make selection + // change inside selection notification handler), the notification message + // shall make a copy of all the strings here. + std::string DocName; + std::string ObjName; + std::string SubName; + std::string TypeName; + + // Resolved sub object in case resolve!=0, otherwise this is null + App::DocumentObject *pSubObject = 0; + // Resolved parent object in case resolve!=0, otherwise this is null + App::DocumentObject *pParentObject = 0; + + // Original selection message in case resolve!=0 + const SelectionChanges *pOriginalMsg = 0; }; } //namespace Gui @@ -118,6 +217,7 @@ template class GuiExport Base::Subject; namespace Gui { + class ViewProviderDocumentObject; /** * The SelectionObserver class simplifies the step to write classes that listen @@ -130,10 +230,12 @@ class GuiExport SelectionObserver public: /// Constructor - SelectionObserver(); + SelectionObserver(bool attach = true, int resolve = 1); + SelectionObserver(const Gui::ViewProviderDocumentObject *vp, bool attach=true, int resolve=1); virtual ~SelectionObserver(); bool blockConnection(bool block); bool isConnectionBlocked() const; + bool isConnectionAttached() const; /** Attaches to the selection. */ void attachSelection(); @@ -147,6 +249,9 @@ private: private: typedef boost::signals2::connection Connection; Connection connectSelection; + std::string filterDocName; + std::string filterObjName; + int resolve; bool blockSelection; }; @@ -162,10 +267,10 @@ class GuiExport SelectionObserverPython : public SelectionObserver public: /// Constructor - SelectionObserverPython(const Py::Object& obj); + SelectionObserverPython(const Py::Object& obj, int resolve=1); virtual ~SelectionObserverPython(); - static void addObserver(const Py::Object& obj); + static void addObserver(const Py::Object& obj, int resolve=1); static void removeObserver(const Py::Object& obj); private: @@ -176,9 +281,26 @@ private: void clearSelection(const SelectionChanges&); void setPreselection(const SelectionChanges&); void removePreselection(const SelectionChanges&); + void pickedListChanged(); private: Py::Object inst; + +#define FC_PY_SEL_OBSERVER \ + FC_PY_ELEMENT(onSelectionChanged) \ + FC_PY_ELEMENT(addSelection) \ + FC_PY_ELEMENT(removeSelection) \ + FC_PY_ELEMENT(setSelection) \ + FC_PY_ELEMENT(clearSelection) \ + FC_PY_ELEMENT(setPreselection) \ + FC_PY_ELEMENT(removePreselection) \ + FC_PY_ELEMENT(pickedListChanged) + +#undef FC_PY_ELEMENT +#define FC_PY_ELEMENT(_name) Py::Object py_##_name; + + FC_PY_SEL_OBSERVER + static std::vector _instances; }; @@ -201,6 +323,18 @@ public: std::string notAllowedReason; }; +/** SelectionGateFilterExternal + * The selection gate disallows any external object + */ +class GuiExport SelectionGateFilterExternal: public SelectionGate +{ +public: + SelectionGateFilterExternal(const char *docName, const char *objName=0); + virtual bool allow(App::Document*,App::DocumentObject*, const char*) override; +private: + std::string DocName; + std::string ObjName; +}; /** The Selection class * The selection singleton keeps track of the selection state of @@ -220,70 +354,6 @@ public: class GuiExport SelectionSingleton : public Base::Subject { public: - /// Add to selection - bool addSelection(const char* pDocName, const char* pObjectName=0, const char* pSubName=0, float x=0, float y=0, float z=0); - /// Add to selection with several sub-elements - bool addSelection(const char* pDocName, const char* pObjectName, const std::vector& pSubNames); - /// Add to selection - bool addSelection(const SelectionObject&); - /// Remove from selection (for internal use) - void rmvSelection(const char* pDocName, const char* pObjectName=0, const char* pSubName=0); - /// Set the selection for a document - void setSelection(const char* pDocName, const std::vector&); - /// Clear the selection of document \a pDocName. If the document name is not given the selection of the active document is cleared. - void clearSelection(const char* pDocName=0); - /// Clear the selection of all documents - void clearCompleteSelection(); - /// Check if selected - bool isSelected(const char* pDocName, const char* pObjectName=0, const char* pSubName=0) const; - /// Check if selected - bool isSelected(App::DocumentObject*, const char* pSubName=0) const; - - /// set the preselected object (mostly by the 3D view) - bool setPreselect(const char* pDocName, const char* pObjectName, const char* pSubName, float x=0, float y=0, float z=0); - /// remove the present preselection - void rmvPreselect(); - /// sets different coords for the preselection - void setPreselectCoord(float x, float y, float z); - /// returns the present preselection - const SelectionChanges& getPreselection(void) const; - /// add a SelectionGate to control what is selectable - void addSelectionGate(Gui::SelectionGate *gate); - /// remove the active SelectionGate - void rmvSelectionGate(void); - - - /** Returns the number of selected objects with a special object type - * It's the convenient way to check if the right objects are selected to - * perform an operation (GuiCommand). The check also detects base types. - * E.g. "Part" also fits on "PartImport" or "PartTransform types. - * If no document name is given the active document is assumed. - */ - unsigned int countObjectsOfType(const Base::Type& typeId, const char* pDocName=0) const; - - /** - * Does basically the same as the method above unless that it accepts a string literal as first argument. - * \a typeName must be a registered type, otherwise 0 is returned. - */ - unsigned int countObjectsOfType(const char* typeName, const char* pDocName=0) const; - - /** Returns a vector of objects of type \a TypeName selected for the given document name \a pDocName. - * If no document name is specified the objects from the active document are regarded. - * If no objects of this document are selected an empty vector is returned. - * @note The vector reflects the sequence of selection. - */ - std::vector getObjectsOfType(const Base::Type& typeId, const char* pDocName=0) const; - - /** - * Does basically the same as the method above unless that it accepts a string literal as first argument. - * \a typeName must be a registered type otherwise an empty array is returned. - */ - std::vector getObjectsOfType(const char* typeName, const char* pDocName=0) const; - /** - * A convenience template-based method that returns an array with the correct types already. - */ - template inline std::vector getObjectsOfType(const char* pDocName=0) const; - struct SelObj { const char* DocName; const char* FeatName; @@ -291,24 +361,139 @@ public: const char* TypeName; App::Document* pDoc; App::DocumentObject* pObject; + App::DocumentObject* pResolvedObject; float x,y,z; }; + /// Add to selection + bool addSelection(const char* pDocName, const char* pObjectName=0, const char* pSubName=0, + float x=0, float y=0, float z=0, const std::vector *pickedList = 0, bool clearPreSelect=true); + bool addSelection2(const char* pDocName, const char* pObjectName=0, const char* pSubName=0, + float x=0, float y=0, float z=0, const std::vector *pickedList = 0) + { + return addSelection(pDocName,pObjectName,pSubName,x,y,z,pickedList,false); + } + + /// Add to selection + bool addSelection(const SelectionObject&, bool clearPreSelect=true); + /// Add to selection with several sub-elements + bool addSelections(const char* pDocName, const char* pObjectName, const std::vector& pSubNames); + /// Update a selection + bool updateSelection(bool show, const char* pDocName, const char* pObjectName=0, const char* pSubName=0); + /// Remove from selection (for internal use) + void rmvSelection(const char* pDocName, const char* pObjectName=0, const char* pSubName=0, + const std::vector *pickedList = 0); + /// Set the selection for a document + void setSelection(const char* pDocName, const std::vector&); + /// Clear the selection of document \a pDocName. If the document name is not given the selection of the active document is cleared. + void clearSelection(const char* pDocName=0, bool clearPreSelect=true); + /// Clear the selection of all documents + void clearCompleteSelection(bool clearPreSelect=true); + /// Check if selected + bool isSelected(const char* pDocName, const char* pObjectName=0, + const char* pSubName=0, int resolve=1) const; + /// Check if selected + bool isSelected(App::DocumentObject*, const char* pSubName=0, int resolve=1) const; + + const char *getSelectedElement(App::DocumentObject*, const char* pSubName) const; + + /// set the preselected object (mostly by the 3D view) + int setPreselect(const char* pDocName, const char* pObjectName, + const char* pSubName, float x=0, float y=0, float z=0, int signal=0); + /// remove the present preselection + void rmvPreselect(bool signal=false); + /// sets different coords for the preselection + void setPreselectCoord(float x, float y, float z); + /// returns the present preselection + const SelectionChanges& getPreselection(void) const; + /// add a SelectionGate to control what is selectable + void addSelectionGate(Gui::SelectionGate *gate, int resolve = 1); + /// remove the active SelectionGate + void rmvSelectionGate(void); + + int disableCommandLog(); + int enableCommandLog(bool silent=false); + + /** Returns the number of selected objects with a special object type + * It's the convenient way to check if the right objects are selected to + * perform an operation (GuiCommand). The check also detects base types. + * E.g. "Part" also fits on "PartImport" or "PartTransform types. + * If no document name is given the active document is assumed. + * + * Set 'resolve' to true to resolve any sub object inside selection SubName + * field + */ + unsigned int countObjectsOfType(const Base::Type& typeId=App::DocumentObject::getClassTypeId(), + const char* pDocName=0, int resolve=1) const; + + /** + * Does basically the same as the method above unless that it accepts a string literal as first argument. + * \a typeName must be a registered type, otherwise 0 is returned. + */ + unsigned int countObjectsOfType(const char* typeName, + const char* pDocName=0, int resolve=1) const; + + /** Returns a vector of objects of type \a TypeName selected for the given document name \a pDocName. + * If no document name is specified the objects from the active document are regarded. + * If no objects of this document are selected an empty vector is returned. + * @note The vector reflects the sequence of selection. + */ + std::vector getObjectsOfType(const Base::Type& typeId, + const char* pDocName=0, int resolve=1) const; + + /** + * Does basically the same as the method above unless that it accepts a string literal as first argument. + * \a typeName must be a registered type otherwise an empty array is returned. + */ + std::vector getObjectsOfType(const char* typeName, + const char* pDocName=0, int resolve=1) const; + /** + * A convenience template-based method that returns an array with the correct types already. + */ + template inline std::vector getObjectsOfType( + const char* pDocName=0, int resolve=1) const; + + /** Set selection object visibility + * + * @param visible: 1: make visible, 0: make invisible, -1: toggle visibility + */ + void setVisible(int visible); + /// signal on new object boost::signals2::signal signalSelectionChanged; + /// signal on selection change with resolved object + boost::signals2::signal signalSelectionChanged2; + /// signal on selection change with resolved object and sub element map + boost::signals2::signal signalSelectionChanged3; + /** Returns a vector of selection objects - * If no document name is given the objects of the active are returned. - * If nothing for this Document is selected an empty vector is returned. - * The vector reflects the sequence of selection. + * + * @param pDocName: document name. If no document name is given the objects + * of the active are returned. If nothing for this Document is selected an + * empty vector is returned. If document name is "*", then all document is + * considered. + * @param resolve: whether to resolve the subname reference of the selection + * @param single: if set to true, then it will return an empty vector if + * there is more than one selections. + * + * @return The returned vector reflects the sequence of selection. */ - std::vector getSelection(const char* pDocName=0) const; + std::vector getSelection(const char* pDocName=0, int resolve=1, bool single=false) const; /** Returns a vector of selection objects - * If no document name is given the objects of the active are returned. - * If nothing for this document is selected an empty vector is returned. - * The vector reflects the sequence of selection. + * + * @param pDocName: document name. If no document name is given the objects + * of the active are returned. If nothing for this Document is selected an + * empty vector is returned. If document name is "*", then all document is + * considered. + * @param typeId: specify the type of object to be returned. + * @param resolve: whether to resolve the subname reference of the selection + * @param single: if set to true, then it will return an empty vector if + * there is more than one selections. + * + * @return The returned vector reflects the sequence of selection. */ - std::vector getSelectionEx(const char* pDocName=0,Base::Type typeId=App::DocumentObject::getClassTypeId()) const; + std::vector getSelectionEx(const char* pDocName=0,Base::Type typeId=App::DocumentObject::getClassTypeId(),int resolve=1, bool single=false) const; /** * @brief getAsPropertyLinkSubList fills PropertyLinkSubList with current selection. @@ -318,15 +503,29 @@ public: int getAsPropertyLinkSubList(App::PropertyLinkSubList &prop) const; /** Returns a vector of all selection objects of all documents. */ - std::vector getCompleteSelection() const; + std::vector getCompleteSelection(int resolve=1) const; bool hasSelection() const; - bool hasSelection(const char* doc) const; + bool hasSelection(const char* doc, bool resolve=true) const; + bool hasSubSelection(const char *doc=0) const; + bool hasPreselection() const; /// Size of selected entities for all documents unsigned int size(void) const { return static_cast(_SelList.size()); } + int selStackBackSize() const {return _SelStackBack.size();} + int selStackForwardSize() const {return _SelStackForward.size();} + std::vector selStackGet(const char* pDocName=0,int resolve=1,int index=0) const; + void selStackGoBack(int count=1); + void selStackGoForward(int count=1); + void selStackPush(bool clearForward=true, bool overwrite=false); + + bool needPickedList() const; + void enablePickedList(bool); + bool hasPickedList() const; + std::vector getPickedList(const char* pDocName) const; + std::vector getPickedListEx(const char* pDocName=0,Base::Type typeId=App::DocumentObject::getClassTypeId()) const; static SelectionSingleton& instance(void); static void destruct (void); friend class SelectionFilter; @@ -336,12 +535,13 @@ public: protected: static PyObject *sAddSelection (PyObject *self,PyObject *args); + static PyObject *sUpdateSelection (PyObject *self,PyObject *args); static PyObject *sRemoveSelection (PyObject *self,PyObject *args); static PyObject *sClearSelection (PyObject *self,PyObject *args); static PyObject *sIsSelected (PyObject *self,PyObject *args); static PyObject *sCountObjectsOfType (PyObject *self,PyObject *args); static PyObject *sGetSelection (PyObject *self,PyObject *args); - static PyObject *sSetPreselection (PyObject *self,PyObject *args); + static PyObject *sSetPreselection (PyObject *self,PyObject *args,PyObject *kwd); static PyObject *sGetPreselection (PyObject *self,PyObject *args); static PyObject *sRemPreselection (PyObject *self,PyObject *args); static PyObject *sGetCompleteSelection(PyObject *self,PyObject *args); @@ -351,6 +551,14 @@ protected: static PyObject *sRemSelObserver (PyObject *self,PyObject *args); static PyObject *sAddSelectionGate (PyObject *self,PyObject *args); static PyObject *sRemoveSelectionGate (PyObject *self,PyObject *args); + static PyObject *sGetPickedList (PyObject *self,PyObject *args); + static PyObject *sEnablePickedList (PyObject *self,PyObject *args); + static PyObject *sPreselect (PyObject *self,PyObject *args); + static PyObject *sSetVisible (PyObject *self,PyObject *args); + static PyObject *sPushSelStack (PyObject *self,PyObject *args); + static PyObject *sHasSelection (PyObject *self,PyObject *args); + static PyObject *sHasSubSelection (PyObject *self,PyObject *args); + static PyObject *sGetSelectionFromStack(PyObject *self,PyObject *args); protected: /// Construction @@ -364,8 +572,16 @@ protected: /// helper to retrieve document by name App::Document* getDocument(const char* pDocName=0) const; + void slotSelectionChanged(const SelectionChanges& msg); + SelectionChanges CurrentPreselection; + std::deque NotificationQueue; + bool Notifying = false; + + void notify(SelectionChanges &&Chng); + void notify(const SelectionChanges &Chng) { notify(SelectionChanges(Chng)); } + struct _SelObj { std::string DocName; std::string FeatName; @@ -374,8 +590,29 @@ protected: App::Document* pDoc; App::DocumentObject* pObject; float x,y,z; + bool logged = false; + + std::pair elementName; + App::DocumentObject* pResolvedObject = 0; + + void log(bool remove=false, bool clearPreselect=true); }; - std::list<_SelObj> _SelList; + mutable std::list<_SelObj> _SelList; + + mutable std::list<_SelObj> _PickedList; + bool _needPickedList; + + typedef std::set > SelStackItem; + std::deque _SelStackBack; + std::deque _SelStackForward; + + int checkSelection(const char *pDocName, const char *pObjectName, + const char *pSubName,int resolve, _SelObj &sel, const std::list<_SelObj> *selList=0) const; + + std::vector getObjectList(const char* pDocName,Base::Type typeId, std::list<_SelObj> &objs, int resolve, bool single=false) const; + + static App::DocumentObject *getObjectOfType(_SelObj &sel, Base::Type type, + int resolve, const char **subelement=0); static SelectionSingleton* _pcSingleton; @@ -385,16 +622,20 @@ protected: float hx,hy,hz; Gui::SelectionGate *ActiveGate; + int gateResolve; + + int logDisabled = 0; + bool logHasSelection = false; }; /** * A convenience template-based method that returns an array with the correct types already. */ template -inline std::vector SelectionSingleton::getObjectsOfType(const char* pDocName) const +inline std::vector SelectionSingleton::getObjectsOfType(const char* pDocName, int resolve) const { std::vector type; - std::vector obj = this->getObjectsOfType(T::getClassTypeId(), pDocName); + std::vector obj = this->getObjectsOfType(T::getClassTypeId(), pDocName, resolve); type.reserve(obj.size()); for (std::vector::iterator it = obj.begin(); it != obj.end(); ++it) type.push_back(static_cast(*it)); @@ -407,6 +648,18 @@ inline SelectionSingleton& Selection(void) return SelectionSingleton::instance(); } +class GuiExport SelectionLogDisabler { +public: + SelectionLogDisabler(bool silent=false) :silent(silent) { + Selection().disableCommandLog(); + } + ~SelectionLogDisabler() { + Selection().enableCommandLog(silent); + } +private: + bool silent; +}; + } //namespace Gui #endif // GUI_SELECTION_H diff --git a/src/Gui/SelectionObject.cpp b/src/Gui/SelectionObject.cpp index 347300e144..5c36395dea 100644 --- a/src/Gui/SelectionObject.cpp +++ b/src/Gui/SelectionObject.cpp @@ -33,6 +33,7 @@ #include "SelectionObject.h" #include "Selection.h" #include "Application.h" +#include "Command.h" #include using namespace Gui; @@ -94,22 +95,12 @@ bool SelectionObject::isObjectTypeOf(const Base::Type& typeId) const std::string SelectionObject::getAsPropertyLinkSubString(void)const { - std::string buf; - buf += "(App."; - buf += "ActiveDocument";//getObject()->getDocument()->getName(); - buf += "."; - buf += getObject()->getNameInDocument(); - buf += ",["; - for(std::vector::const_iterator it = SubNames.begin();it!=SubNames.end();++it){ - buf += "\""; - buf += *it; - buf += "\""; - if(it != --SubNames.end()) - buf += ","; - } - buf += "])"; - - return buf; + std::ostringstream str; + str << "(" << Gui::Command::getObjectCmd(getObject()) << ",["; + for(std::vector::const_iterator it = SubNames.begin();it!=SubNames.end();++it) + str << "'" << *it << "',"; + str << "])"; + return str.str(); } PyObject* SelectionObject::getPyObject() diff --git a/src/Gui/SelectionObject.h b/src/Gui/SelectionObject.h index f52f56341a..d903959383 100644 --- a/src/Gui/SelectionObject.h +++ b/src/Gui/SelectionObject.h @@ -49,7 +49,7 @@ public: /*! Constructs a SelectionObject from the SelectionChanges structure. */ SelectionObject(const SelectionChanges& msg); - SelectionObject(App::DocumentObject*); + explicit SelectionObject(App::DocumentObject*); virtual ~SelectionObject(); /** * The default implementation returns an instance of @ref SelectionObjectPy. @@ -89,6 +89,9 @@ protected: std::string TypeName; std::vector SelPoses; +private: + /// to make sure no duplicates of subnames + std::set _SubNameSet; }; diff --git a/src/Gui/SelectionObjectPyImp.cpp b/src/Gui/SelectionObjectPyImp.cpp index 55597f7883..8505e260d0 100644 --- a/src/Gui/SelectionObjectPyImp.cpp +++ b/src/Gui/SelectionObjectPyImp.cpp @@ -110,7 +110,14 @@ Py::Object SelectionObjectPy::getObject(void) const Py::Tuple SelectionObjectPy::getSubObjects(void) const { - std::vector objs = getSelectionObjectPtr()->getObject()->getPySubObjects(getSelectionObjectPtr()->getSubNames()); + std::vector objs; + + for(const auto &subname : getSelectionObjectPtr()->getSubNames()) { + PyObject *pyObj=0; + Base::Matrix4D mat; + getSelectionObjectPtr()->getObject()->getSubObject(subname.c_str(),&pyObj,&mat); + if(pyObj) objs.push_back(pyObj); + } Py::Tuple temp(objs.size()); Py::sequence_index_type index = 0; diff --git a/src/Gui/SelectionView.cpp b/src/Gui/SelectionView.cpp index d5ef03ae26..a2ac856590 100644 --- a/src/Gui/SelectionView.cpp +++ b/src/Gui/SelectionView.cpp @@ -29,11 +29,13 @@ # include # include # include + #include # include # include #endif /// Here the FreeCAD includes sorted by Base,App,Gui...... +#include #include #include #include "SelectionView.h" @@ -43,7 +45,7 @@ #include "ViewProvider.h" #include "BitmapFactory.h" - +FC_LOG_LEVEL_INIT("Selection",true,true,true) using namespace Gui; using namespace Gui::DockWnd; @@ -53,6 +55,7 @@ using namespace Gui::DockWnd; SelectionView::SelectionView(Gui::Document* pcDocument, QWidget *parent) : DockWindow(pcDocument,parent) + , SelectionObserver(false,0) { setWindowTitle(tr("Selection View")); @@ -85,27 +88,45 @@ SelectionView::SelectionView(Gui::Document* pcDocument, QWidget *parent) selectionView = new QListWidget(this); selectionView->setContextMenuPolicy(Qt::CustomContextMenu); vLayout->addWidget( selectionView ); + + enablePickList = new QCheckBox(this); + enablePickList->setText(tr("Picked object list")); + vLayout->addWidget(enablePickList); + pickList = new QListWidget(this); + pickList->setVisible(false); + vLayout->addWidget(pickList); + +#if QT_VERSION >= 0x040200 + selectionView->setMouseTracking(true); // needed for itemEntered() to work + pickList->setMouseTracking(true); +#endif + resize(200, 200); connect(clearButton, SIGNAL(clicked()), searchBox, SLOT(clear())); connect(searchBox, SIGNAL(textChanged(QString)), this, SLOT(search(QString))); connect(searchBox, SIGNAL(editingFinished()), this, SLOT(validateSearch())); - connect(selectionView, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(select(QListWidgetItem*))); + connect(selectionView, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(toggleSelect(QListWidgetItem*))); + connect(selectionView, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(preselect(QListWidgetItem*))); + connect(pickList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(toggleSelect(QListWidgetItem*))); + connect(pickList, SIGNAL(itemEntered(QListWidgetItem*)), this, SLOT(preselect(QListWidgetItem*))); connect(selectionView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onItemContextMenu(QPoint))); + connect(enablePickList, SIGNAL(stateChanged(int)), this, SLOT(onEnablePickList())); - Gui::Selection().Attach(this); } SelectionView::~SelectionView() { - Gui::Selection().Detach(this); +} + +void SelectionView::leaveEvent(QEvent *) +{ + Selection().rmvPreselect(); } /// @cond DOXERR -void SelectionView::OnChange(Gui::SelectionSingleton::SubjectType &rCaller, - Gui::SelectionSingleton::MessageType Reason) +void SelectionView::onSelectionChanged(const SelectionChanges &Reason) { - Q_UNUSED(rCaller); QString selObject; QTextStream str(&selObject); if (Reason.Type == SelectionChanges::AddSelection) { @@ -116,16 +137,17 @@ void SelectionView::OnChange(Gui::SelectionSingleton::SubjectType &rCaller, // insert the selection as item str << Reason.pDocName; - str << "."; + str << "#"; str << Reason.pObjectName; + App::Document* doc = App::GetApplication().getDocument(Reason.pDocName); + App::DocumentObject* obj = doc->getObject(Reason.pObjectName); if (Reason.pSubName[0] != 0 ) { str << "."; str << Reason.pSubName; - list << QString::fromLatin1(Reason.pSubName); + auto subObj = obj->getSubObject(Reason.pSubName); + if(subObj) + obj = subObj; } - - App::Document* doc = App::GetApplication().getDocument(Reason.pDocName); - App::DocumentObject* obj = doc->getObject(Reason.pObjectName); str << " ("; str << QString::fromUtf8(obj->Label.getValue()); str << ")"; @@ -134,26 +156,31 @@ void SelectionView::OnChange(Gui::SelectionSingleton::SubjectType &rCaller, item->setData(Qt::UserRole, list); } else if (Reason.Type == SelectionChanges::ClrSelection) { - // remove all items - selectionView->clear(); + if(!Reason.pDocName[0]) { + // remove all items + selectionView->clear(); + }else{ + // build name + str << Reason.pDocName; + str << "#"; + // remove all items + for(auto item : selectionView->findItems(selObject,Qt::MatchStartsWith)) + delete item; + } } else if (Reason.Type == SelectionChanges::RmvSelection) { // build name str << Reason.pDocName; - str << "."; + str << "#"; str << Reason.pObjectName; if (Reason.pSubName[0] != 0) { str << "."; str << Reason.pSubName; } - App::Document* doc = App::GetApplication().getDocument(Reason.pDocName); - App::DocumentObject* obj = doc->getObject(Reason.pObjectName); str << " ("; - str << QString::fromUtf8(obj->Label.getValue()); - str << ")"; // remove all items - QList l = selectionView->findItems(selObject,Qt::MatchExactly); + QList l = selectionView->findItems(selObject,Qt::MatchStartsWith); if (l.size() == 1) delete l[0]; @@ -161,7 +188,7 @@ void SelectionView::OnChange(Gui::SelectionSingleton::SubjectType &rCaller, else if (Reason.Type == SelectionChanges::SetSelection) { // remove all items selectionView->clear(); - std::vector objs = Gui::Selection().getSelection(Reason.pDocName); + std::vector objs = Gui::Selection().getSelection(Reason.pDocName,0); for (std::vector::iterator it = objs.begin(); it != objs.end(); ++it) { // save as user data QStringList list; @@ -170,16 +197,17 @@ void SelectionView::OnChange(Gui::SelectionSingleton::SubjectType &rCaller, // build name str << it->DocName; - str << "."; + str << "#"; str << it->FeatName; + App::Document* doc = App::GetApplication().getDocument(it->DocName); + App::DocumentObject* obj = doc->getObject(it->FeatName); if (it->SubName && it->SubName[0] != '\0') { str << "."; str << it->SubName; - list << QString::fromLatin1(it->SubName); + auto subObj = obj->getSubObject(Reason.pSubName); + if(subObj) + obj = subObj; } - - App::Document* doc = App::GetApplication().getDocument(it->DocName); - App::DocumentObject* obj = doc->getObject(it->FeatName); str << " ("; str << QString::fromUtf8(obj->Label.getValue()); str << ")"; @@ -189,6 +217,43 @@ void SelectionView::OnChange(Gui::SelectionSingleton::SubjectType &rCaller, selObject.clear(); } } + else if (Reason.Type == SelectionChanges::PickedListChanged) { + bool picking = Selection().needPickedList(); + enablePickList->setChecked(picking); + pickList->setVisible(picking); + pickList->clear(); + if(picking) { + const auto &sels = Selection().getPickedList(Reason.pDocName); + for(const auto &sel : sels) { + App::Document* doc = App::GetApplication().getDocument(sel.DocName); + if(!doc) continue; + App::DocumentObject* obj = doc->getObject(sel.FeatName); + if(!obj) continue; + + QString selObject; + QTextStream str(&selObject); + str << sel.DocName; + str << "#"; + str << sel.FeatName; + if (sel.SubName[0] != 0 ) { + str << "."; + str << sel.SubName; + auto subObj = obj->getSubObject(sel.SubName); + if(subObj) + obj = subObj; + } + str << " ("; + str << QString::fromUtf8(obj->Label.getValue()); + str << ")"; + + this->x = sel.x; + this->y = sel.y; + this->z = sel.z; + + new QListWidgetItem(selObject, pickList); + } + } + } countLabel->setText(QString::number(selectionView->count())); } @@ -213,8 +278,8 @@ void SelectionView::search(const QString& text) list << QString::fromLatin1(doc->getName()); list << QString::fromLatin1((*it)->getNameInDocument()); // build name - str << doc->getName(); - str << "."; + str << QString::fromUtf8(doc->Label.getValue()); + str << "#"; str << (*it)->getNameInDocument(); str << " ("; str << label; @@ -235,9 +300,7 @@ void SelectionView::validateSearch(void) if (doc) { Gui::Selection().clearSelection(); for (std::vector::iterator it = searchList.begin(); it != searchList.end(); ++it) { - if (!Gui::Selection().hasSelection((*it)->getNameInDocument())) { - Gui::Selection().addSelection(doc->getName(),(*it)->getNameInDocument(),0); - } + Gui::Selection().addSelection(doc->getName(),(*it)->getNameInDocument(),0); } } } @@ -253,11 +316,15 @@ void SelectionView::select(QListWidgetItem* item) if (elements.size() < 2) return; - //Gui::Selection().clearSelection(); - Gui::Command::runCommand(Gui::Command::Gui,"Gui.Selection.clearSelection()"); - //Gui::Selection().addSelection(elements[0].toLatin1(),elements[1].toLatin1(),0); - QString cmd = QString::fromLatin1("Gui.Selection.addSelection(App.getDocument(\"%1\").getObject(\"%2\"))").arg(elements[0], elements[1]); - Gui::Command::runCommand(Gui::Command::Gui,cmd.toLatin1()); + try { + //Gui::Selection().clearSelection(); + Gui::Command::runCommand(Gui::Command::Gui,"Gui.Selection.clearSelection()"); + //Gui::Selection().addSelection(elements[0].toLatin1(),elements[1].toLatin1(),0); + QString cmd = QString::fromLatin1("Gui.Selection.addSelection(App.getDocument(\"%1\").getObject(\"%2\"))").arg(elements[0],elements[1]); + Gui::Command::runCommand(Gui::Command::Gui,cmd.toLatin1()); + }catch(Base::Exception &e) { + e.ReportException(); + } } void SelectionView::deselect(void) @@ -270,20 +337,99 @@ void SelectionView::deselect(void) return; //Gui::Selection().rmvSelection(elements[0].toLatin1(),elements[1].toLatin1(),0); - QString cmd = QString::fromLatin1("Gui.Selection.removeSelection(App.getDocument(\"%1\").getObject(\"%2\"))").arg(elements[0], elements[1]); - Gui::Command::runCommand(Gui::Command::Gui,cmd.toLatin1()); + QString cmd = QString::fromLatin1("Gui.Selection.removeSelection(App.getDocument(\"%1\").getObject(\"%2\"))").arg(elements[0],elements[1]); + try { + Gui::Command::runCommand(Gui::Command::Gui,cmd.toLatin1()); + }catch(Base::Exception &e) { + e.ReportException(); + } +} + +void SelectionView::toggleSelect(QListWidgetItem* item) +{ + if (!item) return; + std::string name = item->text().toLatin1().constData(); + char *docname = &name.at(0); + char *objname = std::strchr(docname,'#'); + if(!objname) return; + *objname++ = 0; + char *subname = std::strchr(objname,'.'); + if(subname) { + *subname++ = 0; + char *end = std::strchr(subname,' '); + if(end) *end = 0; + }else { + char *end = std::strchr(objname,' '); + if(end) *end = 0; + } + QString cmd; + if(Gui::Selection().isSelected(docname,objname,subname)) + cmd = QString::fromLatin1("Gui.Selection.removeSelection(" + "App.getDocument('%1').getObject('%2'),'%3')") + .arg(QString::fromLatin1(docname)) + .arg(QString::fromLatin1(objname)) + .arg(QString::fromLatin1(subname)); + else + cmd = QString::fromLatin1("Gui.Selection.addSelection(" + "App.getDocument('%1').getObject('%2'),'%3',%4,%5,%6)") + .arg(QString::fromLatin1(docname)) + .arg(QString::fromLatin1(objname)) + .arg(QString::fromLatin1(subname)) + .arg(x).arg(y).arg(z); + try { + Gui::Command::runCommand(Gui::Command::Gui,cmd.toLatin1()); + }catch(Base::Exception &e) { + e.ReportException(); + } +} + +void SelectionView::preselect(QListWidgetItem* item) +{ + if (!item) return; + std::string name = item->text().toLatin1().constData(); + char *docname = &name.at(0); + char *objname = std::strchr(docname,'#'); + if(!objname) return; + *objname++ = 0; + char *subname = std::strchr(objname,'.'); + if(subname) { + *subname++ = 0; + char *end = std::strchr(subname,' '); + if(end) *end = 0; + }else { + char *end = std::strchr(objname,' '); + if(end) *end = 0; + } + QString cmd = QString::fromLatin1("Gui.Selection.setPreselection(" + "App.getDocument('%1').getObject('%2'),'%3',tp=2)") + .arg(QString::fromLatin1(docname)) + .arg(QString::fromLatin1(objname)) + .arg(QString::fromLatin1(subname)); + try { + Gui::Command::runCommand(Gui::Command::Gui,cmd.toLatin1()); + }catch(Base::Exception &e) { + e.ReportException(); + } } void SelectionView::zoom(void) { select(); - Gui::Command::runCommand(Gui::Command::Gui,"Gui.SendMsgToActiveView(\"ViewSelection\")"); + try { + Gui::Command::runCommand(Gui::Command::Gui,"Gui.SendMsgToActiveView(\"ViewSelection\")"); + }catch(Base::Exception &e) { + e.ReportException(); + } } void SelectionView::treeSelect(void) { select(); - Gui::Command::runCommand(Gui::Command::Gui,"Gui.runCommand(\"Std_TreeSelection\")"); + try { + Gui::Command::runCommand(Gui::Command::Gui,"Gui.runCommand(\"Std_TreeSelection\")"); + }catch(Base::Exception &e) { + e.ReportException(); + } } void SelectionView::touch(void) @@ -294,8 +440,12 @@ void SelectionView::touch(void) QStringList elements = item->data(Qt::UserRole).toStringList(); if (elements.size() < 2) return; - QString cmd = QString::fromLatin1("App.getDocument(\"%1\").getObject(\"%2\").touch()").arg(elements[0], elements[1]); - Gui::Command::runCommand(Gui::Command::Doc,cmd.toLatin1()); + QString cmd = QString::fromLatin1("App.getDocument(\"%1\").getObject(\"%2\").touch()").arg(elements[0],elements[1]); + try { + Gui::Command::runCommand(Gui::Command::Doc,cmd.toLatin1()); + }catch(Base::Exception &e) { + e.ReportException(); + } } void SelectionView::toPython(void) @@ -455,6 +605,26 @@ bool SelectionView::onMsg(const char* /*pMsg*/,const char** /*ppReturn*/) { return false; } + +void SelectionView::hideEvent(QHideEvent *ev) { + FC_TRACE(this << " detaching selection observer"); + this->detachSelection(); + DockWindow::hideEvent(ev); +} + +void SelectionView::showEvent(QShowEvent *ev) { + FC_TRACE(this << " attaching selection observer"); + this->attachSelection(); + enablePickList->setChecked(Selection().needPickedList()); + Gui::DockWindow::showEvent(ev); +} + +void SelectionView::onEnablePickList() { + bool enabled = enablePickList->isChecked(); + Selection().enablePickedList(enabled); + pickList->setVisible(enabled); +} + /// @endcond #include "moc_SelectionView.cpp" diff --git a/src/Gui/SelectionView.h b/src/Gui/SelectionView.h index 108a94e565..cc27d318c0 100644 --- a/src/Gui/SelectionView.h +++ b/src/Gui/SelectionView.h @@ -30,6 +30,8 @@ #include "Selection.h" class QListWidget; +class QListWidgetItem; +class QCheckBox; class QLabel; namespace App { @@ -42,7 +44,7 @@ namespace DockWnd { /** A test class. A more elaborate class description. */ class SelectionView : public Gui::DockWindow, - public Gui::SelectionSingleton::ObserverType + public Gui::SelectionObserver { Q_OBJECT @@ -60,9 +62,9 @@ public: virtual ~SelectionView(); /// Observer message from the Selection - virtual void OnChange(Gui::SelectionSingleton::SubjectType &rCaller, - Gui::SelectionSingleton::MessageType Reason); + virtual void onSelectionChanged(const SelectionChanges& msg); + virtual void leaveEvent(QEvent*) override; bool onMsg(const char* pMsg,const char** ppReturn); @@ -74,6 +76,9 @@ public: QListWidget* selectionView; QLabel* countLabel; + QCheckBox *enablePickList; + QListWidget *pickList; + public Q_SLOTS: /// get called when text is entered in the search box void search(const QString& text); @@ -89,11 +94,21 @@ public Q_SLOTS: void toPython(void); void touch(void); void showPart(void); + void onEnablePickList(); + void toggleSelect(QListWidgetItem* item=0); + void preselect(QListWidgetItem* item=0); + +protected: + void showEvent(QShowEvent *) override; + void hideEvent(QHideEvent *) override; private: QString getModule(const char* type) const; QString getProperty(App::DocumentObject* obj) const; bool supportPart(App::DocumentObject* obj, const QString& part) const; + +private: + float x,y,z; std::vector searchList; };