diff --git a/src/Gui/ActiveObjectList.cpp b/src/Gui/ActiveObjectList.cpp index 6f65de40e9..d2f85a2e62 100644 --- a/src/Gui/ActiveObjectList.cpp +++ b/src/Gui/ActiveObjectList.cpp @@ -28,44 +28,116 @@ #endif +#include #include "ActiveObjectList.h" #include #include +#include #include +#include "Tree.h" - +FC_LOG_LEVEL_INIT("MDIView",true,true); using namespace Gui; - -void Gui::ActiveObjectList::setObject(App::DocumentObject* obj, const char* name, const Gui::HighlightMode& mode) +App::DocumentObject *ActiveObjectList::getObject(const ObjectInfo &info, bool resolve, + App::DocumentObject **parent, std::string *subname) const { - ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/TreeView"); - bool autoExpand = hGrp->GetBool("TreeActiveAutoExpand", true); + if(parent) *parent = info.obj; + if(subname) *subname = info.subname; + auto obj = info.obj; + if(!obj || !obj->getNameInDocument()) + return 0; + if(info.subname.size()) { + obj = obj->getSubObject(info.subname.c_str()); + if(!obj) + return 0; + } + return resolve?obj->getLinkedObject(true):obj; +} - if (hasObject(name)) { - App::DocumentObject* act = getObject(name); - Gui::Document* doc = Application::Instance->getDocument(act->getDocument()); - Gui::ViewProviderDocumentObject* viewProvider = static_cast - (doc->getViewProvider(act)); - doc->signalHighlightObject(*viewProvider, mode, false); - if (autoExpand) - doc->signalExpandObject(*viewProvider, Gui::CollapseItem); +void ActiveObjectList::setHighlight(const ObjectInfo &info, HighlightMode mode, bool enable) { + auto obj = getObject(info,false); + if(!obj) return; + auto vp = dynamic_cast(Application::Instance->getViewProvider(obj)); + if(!vp) return; + + if(FC_TREEPARAM(TreeActiveAutoExpand)) + vp->getDocument()->signalExpandObject(*vp, + enable?Gui::ExpandPath:Gui::CollapseItem, info.obj, info.subname.c_str()); + + vp->getDocument()->signalHighlightObject(*vp, mode,enable,info.obj,info.subname.c_str()); +} + +Gui::ActiveObjectList::ObjectInfo Gui::ActiveObjectList::getObjectInfo( + App::DocumentObject *obj, const char *subname) const +{ + ObjectInfo info; + info.obj = 0; + if(!obj || !obj->getNameInDocument()) + return info; + if(subname) { + info.obj = obj; + if(subname) info.subname = subname; + }else{ + // If the input object is not from this document, it must be brought in + // by some link type object of this document. We only accept the object + // if we can find such object in the current selection. + auto sels = Gui::Selection().getSelection(_Doc->getDocument()->getName(),false); + for(auto &sel : sels) { + if(sel.pObject == obj || sel.pObject->getLinkedObject(true)==obj) { + info.obj = sel.pObject; + break; + } + for(auto dot=strchr(sel.SubName,'.');dot;dot=strchr(dot+1,'.')) { + std::string subname(sel.SubName,dot-sel.SubName+1); + auto sobj = sel.pObject->getSubObject(subname.c_str()); + if(!sobj) break; + if(sobj == obj || sobj->getLinkedObject(true) == obj) { + info.obj = sel.pObject; + info.subname = subname; + break; + } + } + if(info.obj) break; + } + if(!info.obj && obj->getDocument()==_Doc->getDocument()) + info.obj = obj; + } + return info; +} + +bool Gui::ActiveObjectList::hasObject(App::DocumentObject *obj, + const char *name, const char *subname) const +{ + auto it = _ObjectMap.find(name); + if(it==_ObjectMap.end()) + return false; + auto info = getObjectInfo(obj,subname); + return info.obj==it->second.obj && info.subname==it->second.subname; +} + +void Gui::ActiveObjectList::setObject(App::DocumentObject* obj, const char* name, + const char *subname, const Gui::HighlightMode& mode) +{ + auto it = _ObjectMap.find(name); + if(it!=_ObjectMap.end()) { + setHighlight(it->second,mode,false); + _ObjectMap.erase(it); + } + if(!obj) return; + + auto info = getObjectInfo(obj,subname); + if(!info.obj) { + FC_ERR("Cannot set active object " + << obj->getFullName() << '.' << (subname?subname:"") + << " in document '" << _Doc->getDocument()->getName() + << "'. Not found in current selection"); + return; } - if (obj) { - Gui::Document* doc = Application::Instance->getDocument(obj->getDocument()); - Gui::ViewProviderDocumentObject* viewProvider = static_cast - (doc->getViewProvider(obj)); - doc->signalHighlightObject(*viewProvider, mode, true); - if (autoExpand) - doc->signalExpandObject(*viewProvider, Gui::ExpandPath); - _ObjectMap[name] = obj; - } - else { - if (hasObject(name)) - _ObjectMap.erase(name); - } + _ObjectMap[name] = info; + setHighlight(info,mode,true); } bool Gui::ActiveObjectList::hasObject(const char*name)const @@ -73,14 +145,12 @@ bool Gui::ActiveObjectList::hasObject(const char*name)const return _ObjectMap.find(name) != _ObjectMap.end(); } -void ActiveObjectList::objectDeleted(const ViewProviderDocumentObject& viewProviderIn) +void ActiveObjectList::objectDeleted(const ViewProviderDocumentObject &vp) { - App::DocumentObject* object = viewProviderIn.getObject(); //maybe boost::bimap or boost::multi_index - std::map::iterator it; - for (it = _ObjectMap.begin(); it != _ObjectMap.end(); ++it) + for (auto it = _ObjectMap.begin(); it != _ObjectMap.end(); ++it) { - if (it->second == object) + if (it->second.obj == vp.getObject()) { _ObjectMap.erase(it); return; diff --git a/src/Gui/ActiveObjectList.h b/src/Gui/ActiveObjectList.h index fd0f5d4915..416530ce67 100644 --- a/src/Gui/ActiveObjectList.h +++ b/src/Gui/ActiveObjectList.h @@ -34,32 +34,50 @@ namespace App { namespace Gui { - class Document; class ViewProviderDocumentObject; - /** List of active or special objects - * This class holds a list of objects with a special name. - * Its mainly used to points to something like the active Body or Part in a edit session. - * The class is used the viewer (editor) of a document. - * @see Gui::MDIViewer - * @author Jürgen Riegel - */ - class GuiExport ActiveObjectList + /** List of active or special objects + * This class holds a list of objects with a special name. + * Its mainly used to points to something like the active Body or Part in a edit session. + * The class is used the viewer (editor) of a document. + * @see Gui::MDIViewer + * @author Jürgen Riegel + */ + class GuiExport ActiveObjectList { - public: + ActiveObjectList(Document *doc) + :_Doc(doc) + {} + template - inline _T getObject(const char* name) const - { - std::map::const_iterator pos = _ObjectMap.find(name); - return pos == _ObjectMap.end() ? 0 : dynamic_cast<_T>(pos->second); + inline _T getObject(const char* name, App::DocumentObject **parent=0, std::string *subname=0) const { + auto it = _ObjectMap.find(name); + if(it==_ObjectMap.end()) + return 0; + return dynamic_cast<_T>(getObject(it->second,true,parent,subname)); } - void setObject(App::DocumentObject*, const char*, const Gui::HighlightMode& m = Gui::UserDefined); + void setObject(App::DocumentObject*, const char*, const char *subname=0, + const Gui::HighlightMode& m = Gui::LightBlue); bool hasObject(const char*)const; void objectDeleted(const ViewProviderDocumentObject& viewProviderIn); - protected: - std::map _ObjectMap; + bool hasObject(App::DocumentObject *obj, const char *, const char *subname=0) const; + + private: + struct ObjectInfo; + void setHighlight(const ObjectInfo &info, Gui::HighlightMode mode, bool enable); + App::DocumentObject *getObject(const ObjectInfo &info, bool resolve, + App::DocumentObject **parent=0, std::string *subname=0) const; + ObjectInfo getObjectInfo(App::DocumentObject *obj, const char *subname) const; + + private: + struct ObjectInfo { + App::DocumentObject *obj; + std::string subname; + }; + std::map _ObjectMap; + Document *_Doc; }; } //namespace Gui diff --git a/src/Gui/MDIView.h b/src/Gui/MDIView.h index a10be328c9..064a68e346 100644 --- a/src/Gui/MDIView.h +++ b/src/Gui/MDIView.h @@ -113,18 +113,22 @@ public: /// access getter for the active object list template - inline _T getActiveObject(const char* name) const + inline _T getActiveObject(const char* name, App::DocumentObject **parent=0, std::string *subname=0) const { - return ActiveObjects.getObject<_T>(name); + return ActiveObjects.getObject<_T>(name,parent,subname); } - void setActiveObject(App::DocumentObject*o, const char*n) + void setActiveObject(App::DocumentObject*o, const char*n, const char *subname=0) { - ActiveObjects.setObject(o, n); + ActiveObjects.setObject(o, n, subname); } bool hasActiveObject(const char*n) const { return ActiveObjects.hasObject(n); } + bool isActiveObject(App::DocumentObject*o, const char*n, const char *subname=0) const + { + return ActiveObjects.hasObject(o,n,subname); + } public Q_SLOTS: virtual void setOverrideCursor(const QCursor&); diff --git a/src/Gui/View3DPy.cpp b/src/Gui/View3DPy.cpp index f376519dd7..fe3079bb40 100644 --- a/src/Gui/View3DPy.cpp +++ b/src/Gui/View3DPy.cpp @@ -64,6 +64,7 @@ #include #include #include +#include #include using namespace Gui; @@ -178,8 +179,8 @@ void View3DInventorPy::init_type() "Remove the DraggerCalback function from the coin node\n" "Possibles types :\n" "'addFinishCallback','addStartCallback','addMotionCallback','addValueChangedCallback'\n"); - add_varargs_method("setActiveObject", &View3DInventorPy::setActiveObject, "setActiveObject(name,object)\nadd or set a new active object"); - add_varargs_method("getActiveObject", &View3DInventorPy::getActiveObject, "getActiveObject(name)\nreturns the active object for the given type"); + add_varargs_method("setActiveObject", &View3DInventorPy::setActiveObject, "setActiveObject(name,object,subname=None)\nadd or set a new active object"); + add_varargs_method("getActiveObject", &View3DInventorPy::getActiveObject, "getActiveObject(name,resolve=True)\nreturns the active object for the given type"); add_varargs_method("getViewProvidersOfType", &View3DInventorPy::getViewProvidersOfType, "getViewProvidersOfType(name)\nreturns a list of view providers for the given type"); add_varargs_method("redraw", &View3DInventorPy::redraw, "redraw(): renders the scene on screen (useful for animations)"); add_varargs_method("setName",&View3DInventorPy::setName,"setName(str): sets a name to this viewer\nThe name sets the widget's windowTitle and appears on the viewer tab"); @@ -1372,19 +1373,41 @@ Py::Object View3DInventorPy::getObjectInfo(const Py::Tuple& args) dict.setItem("z", Py::Float(pt[2])); Gui::Document* doc = _view->getViewer()->getDocument(); - ViewProvider *vp = doc ? doc->getViewProviderByPathFromTail(Point->getPath()) - : _view->getViewer()->getViewProviderByPathFromTail(Point->getPath()); - if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) { + ViewProvider *vp = doc ? doc->getViewProviderByPathFromHead(Point->getPath()) + : _view->getViewer()->getViewProviderByPath(Point->getPath()); + if(vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) { + if(!vp->isSelectable()) + return ret; ViewProviderDocumentObject* vpd = static_cast(vp); - dict.setItem("Document", - Py::String(vpd->getObject()->getDocument()->getName())); - dict.setItem("Object", - Py::String(vpd->getObject()->getNameInDocument())); - if (vp->useNewSelectionModel()) { - dict.setItem("Component", - Py::String(vpd->getElement(Point->getDetail()))); - } - else { + if(vp->useNewSelectionModel()) { + std::string subname; + if(!vp->getElementPicked(Point,subname)) + return ret; + auto obj = vpd->getObject(); + if(!obj) + return ret; + if(subname.size()) { + std::pair elementName; + auto sobj = App::GeoFeature::resolveElement(obj,subname.c_str(),elementName); + if(!sobj) + return ret; + if(sobj!=obj) { + dict.setItem("ParentObject",Py::Object(obj->getPyObject(),true)); + dict.setItem("SubName",Py::String(subname)); + obj = sobj; + } + subname = elementName.second.size()?elementName.second:elementName.first; + } + dict.setItem("Document", + Py::String(obj->getDocument()->getName())); + dict.setItem("Object", + Py::String(obj->getNameInDocument())); + dict.setItem("Component",Py::String(subname)); + } else { + dict.setItem("Document", + Py::String(vpd->getObject()->getDocument()->getName())); + dict.setItem("Object", + Py::String(vpd->getObject()->getNameInDocument())); // search for a SoFCSelection node SoFCDocumentObjectAction objaction; objaction.apply(Point->getPath()); @@ -1461,19 +1484,41 @@ Py::Object View3DInventorPy::getObjectsInfo(const Py::Tuple& args) dict.setItem("y", Py::Float(pt[1])); dict.setItem("z", Py::Float(pt[2])); - ViewProvider *vp = doc ? doc->getViewProviderByPathFromTail(point->getPath()) - : _view->getViewer()->getViewProviderByPathFromTail(point->getPath()); - if (vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) { + ViewProvider *vp = doc ? doc->getViewProviderByPathFromHead(point->getPath()) + : _view->getViewer()->getViewProviderByPath(point->getPath()); + if(vp && vp->isDerivedFrom(ViewProviderDocumentObject::getClassTypeId())) { + if(!vp->isSelectable()) + continue; ViewProviderDocumentObject* vpd = static_cast(vp); - dict.setItem("Document", - Py::String(vpd->getObject()->getDocument()->getName())); - dict.setItem("Object", - Py::String(vpd->getObject()->getNameInDocument())); - if (vp->useNewSelectionModel()) { - dict.setItem("Component", - Py::String(vpd->getElement(point->getDetail()))); - } - else { + if(vp->useNewSelectionModel()) { + std::string subname; + if(!vp->getElementPicked(point,subname)) + continue; + auto obj = vpd->getObject(); + if(!obj) + continue; + if(subname.size()) { + std::pair elementName; + auto sobj = App::GeoFeature::resolveElement(obj,subname.c_str(),elementName); + if(!sobj) + continue; + if(sobj!=obj) { + dict.setItem("ParentObject",Py::Object(obj->getPyObject(),true)); + dict.setItem("SubName",Py::String(subname)); + obj = sobj; + } + subname = elementName.second.size()?elementName.second:elementName.first; + } + dict.setItem("Document", + Py::String(obj->getDocument()->getName())); + dict.setItem("Object", + Py::String(obj->getNameInDocument())); + dict.setItem("Component",Py::String(subname)); + } else { + dict.setItem("Document", + Py::String(vpd->getObject()->getDocument()->getName())); + dict.setItem("Object", + Py::String(vpd->getObject()->getNameInDocument())); // search for a SoFCSelection node SoFCDocumentObjectAction objaction; objaction.apply(point->getPath()); @@ -2403,37 +2448,43 @@ Py::Object View3DInventorPy::removeDraggerCallback(const Py::Tuple& args) Py::Object View3DInventorPy::setActiveObject(const Py::Tuple& args) { - PyObject* docObject = 0; - char* name; + PyObject* docObject = Py_None; + char* name; + char *subname = 0; + if (!PyArg_ParseTuple(args.ptr(), "s|Os", &name, &docObject, &subname)) + throw Py::Exception(); - //allow reset of active object by setting "None" - if (args.length() == 2 && args.back() == Py::None()) { - PyArg_Parse(args.front().ptr(), "s", &name); - _view->setActiveObject(NULL, name); - return Py::None(); - } - - if (!PyArg_ParseTuple(args.ptr(), "sO!", &name, &App::DocumentObjectPy::Type, &docObject)) - throw Py::Exception(); - - if (docObject){ - App::DocumentObject* obj = static_cast(docObject)->getDocumentObjectPtr(); - _view->setActiveObject(obj, name); - } - return Py::None(); + if (docObject == Py_None) + _view->setActiveObject(0, name); + else{ + if(!PyObject_TypeCheck(docObject, &App::DocumentObjectPy::Type)) + throw Py::TypeError("Expect the second argument to be a document object or None"); + App::DocumentObject* obj = static_cast(docObject)->getDocumentObjectPtr(); + _view->setActiveObject(obj, name, subname); + } + return Py::None(); } Py::Object View3DInventorPy::getActiveObject(const Py::Tuple& args) { char* name; - if (!PyArg_ParseTuple(args.ptr(), "s", &name)) - throw Py::Exception(); - - App::DocumentObject* obj = _view->getActiveObject(name); + PyObject *resolve = Py_True; + if (!PyArg_ParseTuple(args.ptr(), "s|O", &name,&resolve)) + throw Py::Exception(); + + App::DocumentObject *parent = 0; + std::string subname; + App::DocumentObject* obj = _view->getActiveObject(name,&parent,&subname); if(!obj) return Py::None(); - return Py::Object(obj->getPyObject()); + if(PyObject_IsTrue(resolve)) + return Py::asObject(obj->getPyObject()); + + return Py::TupleN( + Py::asObject(obj->getPyObject()), + Py::asObject(parent->getPyObject()), + Py::String(subname.c_str())); } Py::Object View3DInventorPy::getViewProvidersOfType(const Py::Tuple& args)