From cd7725227f7fd2cd485adab4c95b99f59366d552 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Sat, 22 Jun 2019 10:23:35 +0800 Subject: [PATCH] ViewProvider(DocumentObject): new APIs for context aware selection Context aware selection makes it possible to select the same Coin3D node in different hierarchies (i.e. context) without ambiguity. New/modified APIs in ViewProvider (the first two are the most crucial APIs for context aware selection to work): * getElementPicked(), supercedes getElement(). Given a Coin3D pick point, this function returns a dot separated subname reference as a path leads to the selected object. * getDetailPath(), supercedes getDetail(). Given a subname reference, this function returns an SoFullPath leads to the Coin3D node of the selected object or sub-element (with SoDetail). * (has)HiddenMarker(), check/return a special text marker for context aware override of object visibility. * partialRender(), render only part of the object based on given subname references. It can render, e.g. some faces of a solid, or some children of a container. It uses the 'secondary' extension of SoSelectionElementAction to select which elements to render or hide. The actually rendering functionality will be added in the following patch. * update()/onChanged(), modified to sync Visibility property from DocumentObject. * useNewSelectionModel(), modified to return the default true view parameter. This is for test in the early stage of Link development, probably not needed anymore. * getBoundingBox(), a convenience function to obtain the bounding box of a sub-object/element regardless of object's visibility. It uses getDetailPath() and SoGetBoundingBoxAction to obtain bounding box through Coin3D. It will be used in later sub-element box selection functionality. New/modified APIs in ViewProviderDocumentObject: * getElementPicked()/getDetailPath() provides actual implementation to support container like (sub)object selection without ambiguity. It relies on DocumentObject::getSubObject() to walk the path. * reattach(), called when undo deleteion * forceUpdate()/isUpdateForced(), force update even if object is invisible. These are used by Link to force update the visual of a linked object regardless of its visibility. * getLinkedViewProvider(), return the linked view provider with hierarchy. ViewProviderDocumentObjectPy: * Object attribute is made writtable. Assigning it is equaivalant of calling ViewProviderDocumentObject::attach() in Python. --- src/Gui/ViewProvider.cpp | 145 ++++++++++++ src/Gui/ViewProvider.h | 62 ++++- src/Gui/ViewProviderDocumentObject.cpp | 242 +++++++++++++++++++- src/Gui/ViewProviderDocumentObject.h | 45 +++- src/Gui/ViewProviderDocumentObjectPy.xml | 10 +- src/Gui/ViewProviderDocumentObjectPyImp.cpp | 23 +- src/Gui/ViewProviderPy.xml | 55 +++++ src/Gui/ViewProviderPyImp.cpp | 145 ++++++++++++ 8 files changed, 713 insertions(+), 14 deletions(-) diff --git a/src/Gui/ViewProvider.cpp b/src/Gui/ViewProvider.cpp index b98987c974..4ba9a26f6b 100644 --- a/src/Gui/ViewProvider.cpp +++ b/src/Gui/ViewProvider.cpp @@ -30,12 +30,14 @@ # include # include # include +# include # include # include # include # include # include # include +# include #endif /// Here the FreeCAD includes sorted by Base,App,Gui...... @@ -55,6 +57,7 @@ #include "View3DInventorViewer.h" #include "SoFCDB.h" #include "ViewProviderExtension.h" +#include "ViewParams.h" #include @@ -883,6 +886,87 @@ std::vector< App::DocumentObject* > ViewProvider::claimChildren3D(void) const } return vec; } +bool ViewProvider::getElementPicked(const SoPickedPoint *pp, std::string &subname) const { + if(!isSelectable()) return false; + auto vector = getExtensionsDerivedFromType(); + for(Gui::ViewProviderExtension* ext : vector) + if(ext->extensionGetElementPicked(pp,subname)) + return true; + subname = getElement(pp?pp->getDetail():0); + return true; +} + +bool ViewProvider::getDetailPath(const char *subname, SoFullPath *pPath, bool append, SoDetail *&det) const { + if(pcRoot->findChild(pcModeSwitch) < 0) { + // this is possible in case of editing, where the switch node + // of the linked view object is temporarily removed from its root + // if(append) + // pPath->append(pcRoot); + return false; + } + if(append) { + pPath->append(pcRoot); + pPath->append(pcModeSwitch); + } + auto vector = getExtensionsDerivedFromType(); + for(Gui::ViewProviderExtension* ext : vector) + if(ext->extensionGetDetailPath(subname,pPath,det)) + return true; + det = getDetail(subname); + return true; +} + +const std::string &ViewProvider::hiddenMarker() { + return App::DocumentObject::hiddenMarker(); +} + +const char *ViewProvider::hasHiddenMarker(const char *subname) { + return App::DocumentObject::hasHiddenMarker(subname); +} + +int ViewProvider::partialRender(const std::vector &elements, bool clear) { + if(elements.empty()) { + auto node = pcModeSwitch->getChild(_iActualMode); + if(node) { + FC_LOG("partial render clear"); + SoSelectionElementAction action(SoSelectionElementAction::None,true); + action.apply(node); + } + } + int count = 0; + SoFullPath *path = static_cast(new SoPath); + path->ref(); + SoSelectionElementAction action; + action.setSecondary(true); + for(auto element : elements) { + bool hidden = hasHiddenMarker(element.c_str()); + if(hidden) + element.resize(element.size()-hiddenMarker().size()); + path->truncate(0); + SoDetail *det = 0; + if(getDetailPath(element.c_str(),path,false,det)) { + if(!hidden && !det) { + FC_LOG("partial render element not found: " << element); + continue; + } + FC_LOG("partial render (" << path->getLength() << "): " << element); + if(!hidden) + action.setType(clear?SoSelectionElementAction::Remove:SoSelectionElementAction::Append); + else + action.setType(clear?SoSelectionElementAction::Show:SoSelectionElementAction::Hide); + action.setElement(det); + action.apply(path); + ++count; + } + delete det; + } + path->unref(); + return count; +} + +bool ViewProvider::useNewSelectionModel() const { + return ViewParams::instance()->getUseNewSelection(); +} void ViewProvider::beforeDelete() { auto vector = getExtensionsDerivedFromType(); @@ -890,6 +974,67 @@ void ViewProvider::beforeDelete() { ext->extensionBeforeDelete(); } +Base::BoundBox3d ViewProvider::getBoundingBox(const char *subname, bool transform, MDIView *view) const { + if(!pcRoot || !pcModeSwitch || pcRoot->findChild(pcModeSwitch)<0) + return Base::BoundBox3d(); + + if(!view) + view = Application::Instance->activeView(); + auto iview = dynamic_cast(view); + if(!iview) { + auto doc = Application::Instance->activeDocument(); + if(doc) { + auto views = doc->getMDIViewsOfType(View3DInventor::getClassTypeId()); + if(views.size()) + iview = dynamic_cast(views.front()); + } + if(!iview) { + FC_ERR("no view"); + return Base::BoundBox3d(); + } + } + + View3DInventorViewer* viewer = iview->getViewer(); + SoGetBoundingBoxAction bboxAction(viewer->getSoRenderManager()->getViewportRegion()); + + auto mode = pcModeSwitch->whichChild.getValue(); + if(mode < 0) + pcModeSwitch->whichChild = getDefaultMode(); + + SoTempPath path(20); + path.ref(); + if(subname && subname[0]) { + SoDetail *det=0; + if(!getDetailPath(subname,&path,true,det)) { + if(mode < 0) + pcModeSwitch->whichChild = mode; + path.unrefNoDelete(); + return Base::BoundBox3d(); + } + delete det; + } + SoTempPath resetPath(3); + resetPath.ref(); + if(!transform) { + resetPath.append(pcRoot); + resetPath.append(pcModeSwitch); + bboxAction.setResetPath(&resetPath,true,SoGetBoundingBoxAction::TRANSFORM); + } + if(path.getLength()) + bboxAction.apply(&path); + else + bboxAction.apply(pcRoot); + if(mode < 0) + pcModeSwitch->whichChild = mode; + resetPath.unrefNoDelete(); + path.unrefNoDelete(); + auto bbox = bboxAction.getBoundingBox(); + float minX,minY,minZ,maxX,maxY,maxZ; + bbox.getMax().getValue(maxX,maxY,maxZ); + bbox.getMin().getValue(minX,minY,minZ); + return Base::BoundBox3d(minX,minY,minZ,maxX,maxY,maxZ); +} + bool ViewProvider::isLinkVisible() const { auto ext = getExtensionByType(true); if(!ext) diff --git a/src/Gui/ViewProvider.h b/src/Gui/ViewProvider.h index a525778534..9cdcfb4147 100644 --- a/src/Gui/ViewProvider.h +++ b/src/Gui/ViewProvider.h @@ -35,6 +35,7 @@ #include #include #include +#include class SbVec2s; class SbVec3f; @@ -48,6 +49,7 @@ class SbMatrix; class SoEventCallback; class SoPickedPoint; class SoDetail; +class SoFullPath; class QString; class QMenu; class QObject; @@ -151,12 +153,44 @@ public: //@{ /// indicates if the ViewProvider use the new Selection model - virtual bool useNewSelectionModel(void) const {return false;} - /// indicates if the ViewProvider can be selected + virtual bool useNewSelectionModel(void) const; virtual bool isSelectable(void) const {return true;} + /// return a hit element given the picked point which contains the full node path + virtual bool getElementPicked(const SoPickedPoint *, std::string &subname) const; /// return a hit element to the selection path or 0 virtual std::string getElement(const SoDetail *) const { return std::string(); } - virtual SoDetail* getDetail(const char*) const { return 0; } + /// return the coin node detail of the subelement + virtual SoDetail* getDetail(const char *) const { return 0; } + + /** return the coin node detail and path to the node of the subelement + * + * @param subname: dot separated string reference to the sub element + * @param pPath: output coin path leading to the returned element detail + * @param append: If true, pPath will be first appended with the root node and + * the mode switch node of this view provider. + * + * @return the coint detail of the subelement + * + * If this view provider links to other view provider, then the + * implementation of getDetailPath() shall also append all intermediate + * nodes starting just after the mode switch node up till the mode switch of + * the linked view provider. + */ + virtual bool getDetailPath(const char *subname, SoFullPath *pPath, bool append, SoDetail *&det) const; + + /** partial rendering setup + * + * @param subelements: a list of dot separated string refer to the sub element + * @param clear: if true, remove the the subelement from partial rendering. + * If else, add the subelement for rendering. + * + * @return Return the number of subelement found + * + * Partial rendering only works if there is at least one SoFCSelectRoot node + * in this view provider + */ + int partialRender(const std::vector &subelements, bool clear); + virtual std::vector getModelPoints(const SoPickedPoint *) const; /// return the highlight lines for a given element or the whole shape virtual std::vector getSelectionShape(const char* Element) const { @@ -164,6 +198,13 @@ public: return std::vector(); } + /** Return the bound box of this view object + * + * This method shall work regardless whether the current view object is + * visible or not. + */ + Base::BoundBox3d getBoundingBox(const char *subname=0, bool transform=true, MDIView *view=0) const; + /** * Get called if the object is about to get deleted. * Here you can delete other objects, switch their visibility or prevent the deletion of the object. @@ -346,6 +387,19 @@ public: const std::string getOverrideMode(); //@} + /** @name Color mangement methods + */ + //@{ + virtual std::map getElementColors(const char *element=0) const { + (void)element; + return {}; + } + virtual void setElementColors(const std::map &colors) { + (void)colors; + } + static const std::string &hiddenMarker(); + static const char *hasHiddenMarker(const char *subname); + //@} /** @name Edit methods * if the Viewprovider goes in edit mode @@ -441,7 +495,7 @@ public: void setDefaultMode(int); int getDefaultMode() const; //@} - + protected: /** Helper method to check that the node is valid, i.e. it must not cause * and infinite recursion. diff --git a/src/Gui/ViewProviderDocumentObject.cpp b/src/Gui/ViewProviderDocumentObject.cpp index b868b4af5b..ba557ff54e 100644 --- a/src/Gui/ViewProviderDocumentObject.cpp +++ b/src/Gui/ViewProviderDocumentObject.cpp @@ -30,22 +30,34 @@ # include # include # include +# include +# include +# include +# include +# include +# include #endif /// Here the FreeCAD includes sorted by Base,App,Gui...... #include #include +#include +#include #include #include +#include #include #include "Application.h" #include "Document.h" #include "Selection.h" #include "MainWindow.h" #include "MDIView.h" +#include "View3DInventor.h" +#include "View3DInventorViewer.h" #include "TaskView/TaskAppearance.h" #include "ViewProviderDocumentObject.h" #include "ViewProviderExtension.h" +#include "Tree.h" #include FC_LOG_LEVEL_INIT("Gui",true,true) @@ -61,6 +73,15 @@ ViewProviderDocumentObject::ViewProviderDocumentObject() { ADD_PROPERTY(DisplayMode,((long)0)); ADD_PROPERTY(Visibility,(true)); + ADD_PROPERTY(ShowInTree,(true)); + + static const char* OnTopEnum[]= {"Disabled","Enabled","Object","Element",NULL}; + ADD_PROPERTY(OnTopWhenSelected,((long int)0)); + ADD_PROPERTY_TYPE(OnTopWhenSelected,((long int)0), "Base", App::Prop_None, + "Enabled: Display the object on top of any other object when selected\n" + "Object: On top only if the whole object is selected\n" + "Element: On top only if some sub-element of the object is selected"); + OnTopWhenSelected.setEnums(OnTopEnum); sPixmap = "Feature"; } @@ -79,10 +100,16 @@ void ViewProviderDocumentObject::getTaskViewContent(std::vector(); + for(Gui::ViewProviderExtension* ext : vector) + ext->extensionStartRestoring(); } void ViewProviderDocumentObject::finishRestoring() { + auto vector = getExtensionsDerivedFromType(); + for(Gui::ViewProviderExtension* ext : vector) + ext->extensionFinishRestoring(); } bool ViewProviderDocumentObject::isAttachedToDocument() const @@ -100,6 +127,10 @@ const char* ViewProviderDocumentObject::detachFromDocument() bool ViewProviderDocumentObject::removeDynamicProperty(const char* name) { + App::Property* prop = getDynamicPropertyByName(name); + if(!prop || prop->testStatus(App::Property::LockDynamic)) + return false; + // transactions of view providers are also managed in App::Document. App::DocumentObject* docobject = getObject(); App::Document* document = docobject ? docobject->getDocument() : nullptr; @@ -147,6 +178,8 @@ void ViewProviderDocumentObject::onChanged(const App::Property* prop) Visibility.getValue() ? show() : hide(); Visibility.setStatus(App::Property::User2, false); } + if(getObject() && getObject()->Visibility.getValue()!=Visibility.getValue()) + getObject()->Visibility.setValue(Visibility.getValue()); } if (pcDocument && !pcDocument->isModified()) { @@ -160,13 +193,13 @@ void ViewProviderDocumentObject::onChanged(const App::Property* prop) void ViewProviderDocumentObject::hide(void) { + ViewProvider::hide(); // use this bit to check whether 'Visibility' must be adjusted if (Visibility.testStatus(App::Property::User2) == false) { Visibility.setStatus(App::Property::User2, true); Visibility.setValue(false); Visibility.setStatus(App::Property::User2, false); } - ViewProvider::hide(); } void ViewProviderDocumentObject::show(void) @@ -182,7 +215,7 @@ void ViewProviderDocumentObject::show(void) void ViewProviderDocumentObject::updateView() { - if(testStatus(ViewStatus::UpdatingView)) + if(!pcObject || testStatus(ViewStatus::UpdatingView)) return; Base::ObjectStatusLocker lock(ViewStatus::UpdatingView,this); @@ -196,7 +229,7 @@ void ViewProviderDocumentObject::updateView() for (std::map::iterator it = Map.begin(); it != Map.end(); ++it) { updateData(it->second); } - if (vis) ViewProvider::show(); + if (vis && Visibility.getValue()) ViewProvider::show(); } void ViewProviderDocumentObject::attach(App::DocumentObject *pcObj) @@ -204,6 +237,10 @@ void ViewProviderDocumentObject::attach(App::DocumentObject *pcObj) // save Object pointer pcObject = pcObj; + if(pcObj && pcObj->getNameInDocument() && + Visibility.getValue()!=pcObj->Visibility.getValue()) + pcObj->Visibility.setValue(Visibility.getValue()); + // Retrieve the supported display modes of the view provider aDisplayModesArray = this->getDisplayModes(); @@ -229,19 +266,35 @@ void ViewProviderDocumentObject::attach(App::DocumentObject *pcObj) ext->extensionAttach(pcObj); } -void ViewProviderDocumentObject::updateData(const App::Property* prop) +void ViewProviderDocumentObject::reattach(App::DocumentObject *pcObj) { + auto vector = getExtensionsDerivedFromType(); + for (Gui::ViewProviderExtension* ext : vector) + ext->extensionReattach(pcObj); +} + +void ViewProviderDocumentObject::update(const App::Property* prop) { - ViewProvider::updateData(prop); + // bypass view provider update to always allow changing visibility from + // document object + if(prop == &getObject()->Visibility) { + if(!isRestoring() && Visibility.getValue()!=getObject()->Visibility.getValue()) + Visibility.setValue(!Visibility.getValue()); + }else + ViewProvider::update(prop); } Gui::Document* ViewProviderDocumentObject::getDocument() const { + if(!pcObject) + throw Base::RuntimeError("View provider detached"); App::Document* pAppDoc = pcObject->getDocument(); return Gui::Application::Instance->getDocument(pAppDoc); } Gui::MDIView* ViewProviderDocumentObject::getActiveView() const { + if(!pcObject) + throw Base::RuntimeError("View provider detached"); App::Document* pAppDoc = pcObject->getDocument(); Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc); return pGuiDoc->getActiveView(); @@ -249,6 +302,8 @@ Gui::MDIView* ViewProviderDocumentObject::getActiveView() const Gui::MDIView* ViewProviderDocumentObject::getEditingView() const { + if(!pcObject) + throw Base::RuntimeError("View provider detached"); App::Document* pAppDoc = pcObject->getDocument(); Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc); return pGuiDoc->getEditingViewOfViewProvider(const_cast(this)); @@ -256,6 +311,8 @@ Gui::MDIView* ViewProviderDocumentObject::getEditingView() const Gui::MDIView* ViewProviderDocumentObject::getInventorView() const { + if(!pcObject) + throw Base::RuntimeError("View provider detached"); App::Document* pAppDoc = pcObject->getDocument(); Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc); @@ -269,6 +326,8 @@ Gui::MDIView* ViewProviderDocumentObject::getInventorView() const Gui::MDIView* ViewProviderDocumentObject::getViewOfNode(SoNode* node) const { + if(!pcObject) + throw Base::RuntimeError("View provider detached"); App::Document* pAppDoc = pcObject->getDocument(); Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc); return pGuiDoc->getViewOfNode(node); @@ -276,6 +335,8 @@ Gui::MDIView* ViewProviderDocumentObject::getViewOfNode(SoNode* node) const SoNode* ViewProviderDocumentObject::findFrontRootOfType(const SoType& type) const { + if(!pcObject) + return 0; // first get the document this object is part of and get its GUI counterpart App::Document* pAppDoc = pcObject->getDocument(); Gui::Document* pGuiDoc = Gui::Application::Instance->getDocument(pAppDoc); @@ -336,6 +397,160 @@ PyObject* ViewProviderDocumentObject::getPyObject() return pyViewObject; } +bool ViewProviderDocumentObject::canDropObjectEx(App::DocumentObject* obj, App::DocumentObject *owner, + const char *subname, const std::vector &elements) const +{ + auto vector = getExtensionsDerivedFromType(); + for(Gui::ViewProviderExtension* ext : vector){ + if(ext->extensionCanDropObjectEx(obj,owner,subname,elements)) + return true; + } + if(obj && obj->getDocument()!=getObject()->getDocument()) + return false; + return canDropObject(obj); +} + +int ViewProviderDocumentObject::replaceObject( + App::DocumentObject *oldObj, App::DocumentObject *newObj) +{ + if(!oldObj || !oldObj->getNameInDocument() + || !newObj || !newObj->getNameInDocument()) + { + FC_THROWM(Base::RuntimeError,"Invalid object"); + } + + auto obj = getObject(); + if(!obj || !obj->getNameInDocument()) + FC_THROWM(Base::RuntimeError,"View provider not attached"); + + int res = ViewProvider::replaceObject(oldObj,newObj); + if(res>=0) + return res; + + std::vector > > propChanges; + std::vector props; + obj->getPropertyList(props); + for(auto prop : props) { + auto linkProp = Base::freecad_dynamic_cast(prop); + if(!linkProp) + continue; + std::unique_ptr copy(linkProp->CopyOnLinkReplace(obj, oldObj,newObj)); + if(!copy) + continue; + propChanges.emplace_back(prop,std::move(copy)); + } + + if(propChanges.empty()) + return 0; + + // Global search for affected links + for(auto doc : App::GetApplication().getDocuments()) { + for(auto o : doc->getObjects()) { + if(o == obj) + continue; + std::vector props; + o->getPropertyList(props); + for(auto prop : props) { + auto linkProp = Base::freecad_dynamic_cast(prop); + if(!linkProp) + continue; + std::unique_ptr copy(linkProp->CopyOnLinkReplace(obj,oldObj,newObj)); + if(!copy) + continue; + propChanges.emplace_back(App::DocumentObjectT(prop),std::move(copy)); + } + } + } + + for(auto &v : propChanges) { + auto prop = v.first.getProperty(); + if(prop) + prop->Paste(*v.second.get()); + } + return 1; +} + +bool ViewProviderDocumentObject::showInTree() const { + return ShowInTree.getValue(); +} + +bool ViewProviderDocumentObject::getElementPicked(const SoPickedPoint *pp, std::string &subname) const +{ + if(!isSelectable()) return false; + auto vector = getExtensionsDerivedFromType(); + for(Gui::ViewProviderExtension* ext : vector) + if(ext->extensionGetElementPicked(pp,subname)) + return true; + + auto childRoot = getChildRoot(); + int idx; + if(!childRoot || + (idx=pcModeSwitch->whichChild.getValue())<0 || + pcModeSwitch->getChild(idx)!=childRoot) + { + return ViewProvider::getElementPicked(pp,subname); + } + + SoPath* path = pp->getPath(); + idx = path->findNode(childRoot); + if(idx<0 || idx+1>=path->getLength()) + return false; + auto vp = getDocument()->getViewProvider(path->getNode(idx+1)); + if(!vp) return false; + auto obj = vp->getObject(); + if(!obj || !obj->getNameInDocument()) + return false; + std::ostringstream str; + str << obj->getNameInDocument() << '.'; + if(vp->getElementPicked(pp,subname)) + str << subname; + subname = str.str(); + return true; +} + +bool ViewProviderDocumentObject::getDetailPath(const char *subname, SoFullPath *path, bool append, SoDetail *&det) const +{ + auto len = path->getLength(); + if(!append && len>=2) + len -= 2; + if(ViewProvider::getDetailPath(subname,path,append,det)) { + if(det || !subname || !*subname) + return true; + } + + if(det) { + delete det; + det = 0; + } + + const char *dot = strchr(subname,'.'); + if(!dot) return false; + auto obj = getObject(); + if(!obj || !obj->getNameInDocument()) return false; + auto sobj = obj->getSubObject(std::string(subname,dot-subname+1).c_str()); + if(!sobj) return false; + auto vp = Application::Instance->getViewProvider(sobj); + if(!vp) return false; + + auto childRoot = getChildRoot(); + if(!childRoot) + path->truncate(len); + else { + auto idx = pcModeSwitch->whichChild.getValue(); + if(idx < 0 || pcModeSwitch->getChild(idx)!=childRoot) + return false; + path->append(childRoot); + } + bool ret = false; + if(path->getLength()) { + SoNode * tail = path->getTail(); + const SoChildList * children = tail->getChildren(); + if(children && children->find(vp->getRoot())>=0) + ret = vp->getDetailPath(dot+1,path,true,det); + } + return ret; +} + void ViewProviderDocumentObject::onPropertyStatusChanged( const App::Property &prop, unsigned long oldStatus) { @@ -344,6 +559,23 @@ void ViewProviderDocumentObject::onPropertyStatusChanged( pcObject->getDocument()->signalChangePropertyEditor(*pcObject->getDocument(),prop); } +ViewProviderDocumentObject *ViewProviderDocumentObject::getLinkedViewProvider( + std::string *subname, bool recursive) const +{ + (void)subname; + auto self = const_cast(this); + if(!pcObject || !pcObject->getNameInDocument()) + return self; + auto linked = pcObject->getLinkedObject(recursive); + if(!linked || linked == pcObject) + return self; + auto res = Base::freecad_dynamic_cast( + Application::Instance->getViewProvider(linked)); + if(!res) + res = self; + return res; +} + std::string ViewProviderDocumentObject::getFullName() const { if(pcObject) return pcObject->getFullName() + ".ViewObject"; diff --git a/src/Gui/ViewProviderDocumentObject.h b/src/Gui/ViewProviderDocumentObject.h index 540e0cba23..59e52a3311 100644 --- a/src/Gui/ViewProviderDocumentObject.h +++ b/src/Gui/ViewProviderDocumentObject.h @@ -24,9 +24,10 @@ #ifndef GUI_VIEWPROVIDER_DOCUMENTOBJECT_H #define GUI_VIEWPROVIDER_DOCUMENTOBJECT_H +#include + #include "ViewProvider.h" #include -#include class SoMaterial; class SoDrawStyle; @@ -59,9 +60,12 @@ public: // Display properties App::PropertyEnumeration DisplayMode; App::PropertyBool Visibility; + App::PropertyBool ShowInTree; + App::PropertyEnumeration OnTopWhenSelected; virtual void attach(App::DocumentObject *pcObject); - virtual void updateData(const App::Property*); + virtual void reattach(App::DocumentObject *); + virtual void update(const App::Property*) override; /// Set the active mode, i.e. the first item of the 'Display' property. void setActiveMode(); /// Hide the object in the view @@ -69,6 +73,13 @@ public: /// Show the object in the view virtual void show(void); + virtual bool canDropObjectEx(App::DocumentObject *, App::DocumentObject *, + const char *, const std::vector &) const override; + + virtual int replaceObject(App::DocumentObject*, App::DocumentObject*) override; + + virtual bool showInTree() const; + /// Get a list of TaskBoxes associated with this object virtual void getTaskViewContent(std::vector&) const; @@ -83,6 +94,22 @@ public: /// Get the python wrapper for that ViewProvider PyObject* getPyObject(); + /// return a hit element given the picked point which contains the full node path + virtual bool getElementPicked(const SoPickedPoint *, std::string &subname) const override; + /// return the coin node detail and path to the node of the subname + virtual bool getDetailPath(const char *subname, SoFullPath *pPath, bool append, SoDetail *&det) const override; + + /* Force update visual + * + * These method exists because some view provider skips visual update when + * hidden (e.g. PartGui::ViewProviderPartExt). Call this function to force + * visual update. + */ + //@{ + virtual void forceUpdate(bool enable = true) {(void)enable;} + virtual bool isUpdateForced() const {return false;} + //@} + /** @name Restoring view provider from document load */ //@{ virtual void startRestoring(); @@ -96,6 +123,20 @@ public: const char* group=0, const char* doc=0, short attr=0, bool ro=false, bool hidden=false) override; + /** Return the linked view object + * + * This function is mainly used for GUI navigation (e.g. + * StdCmdLinkSelectLinked). + * + * @param subname: output as the subname referencing the linked object + * @param recursive: whether to follow the link recursively + * + * @return Returns the linked view provider. If none, it shall return + * itself. + */ + virtual ViewProviderDocumentObject *getLinkedViewProvider( + std::string *subname=0, bool recursive=false) const; + virtual std::string getFullName() const override; protected: diff --git a/src/Gui/ViewProviderDocumentObjectPy.xml b/src/Gui/ViewProviderDocumentObjectPy.xml index 5901cddf18..f21375938a 100644 --- a/src/Gui/ViewProviderDocumentObjectPy.xml +++ b/src/Gui/ViewProviderDocumentObjectPy.xml @@ -18,12 +18,18 @@ Update the view representation of the object - + - Return the associated data object + Set/Get the associated data object + + + Reference count to force update visual + + + Return the document the view provider is part of diff --git a/src/Gui/ViewProviderDocumentObjectPyImp.cpp b/src/Gui/ViewProviderDocumentObjectPyImp.cpp index 603b19997a..8d3d299aef 100644 --- a/src/Gui/ViewProviderDocumentObjectPyImp.cpp +++ b/src/Gui/ViewProviderDocumentObjectPyImp.cpp @@ -29,7 +29,7 @@ #include #include -#include +#include // inclusion of the generated files (generated out of ViewProviderDocumentObjectPy.xml) #include "ViewProviderDocumentObjectPy.h" @@ -62,6 +62,27 @@ Py::Object ViewProviderDocumentObjectPy::getObject(void) const return Py::Object(obj->getPyObject(), true); // do not inc'ref twice } +void ViewProviderDocumentObjectPy::setObject(Py::Object pyobj) +{ + if(!PyObject_TypeCheck(*pyobj,&App::DocumentObjectPy::Type)) + throw Py::TypeError("Expect document object"); + App::DocumentObject* obj = getViewProviderDocumentObjectPtr()->getObject(); + if(obj) + throw Py::RuntimeError("View object already attached"); + getViewProviderDocumentObjectPtr()->attach( + static_cast(*pyobj)->getDocumentObjectPtr()); +} + +Py::Boolean ViewProviderDocumentObjectPy::getForceUpdate() const +{ + return Py::Boolean(getViewProviderDocumentObjectPtr()->isUpdateForced()); +} + +void ViewProviderDocumentObjectPy::setForceUpdate(Py::Boolean arg) +{ + getViewProviderDocumentObjectPtr()->forceUpdate(arg); +} + Py::Object ViewProviderDocumentObjectPy::getDocument(void) const { Document* doc = getViewProviderDocumentObjectPtr()->getDocument(); diff --git a/src/Gui/ViewProviderPy.xml b/src/Gui/ViewProviderPy.xml index 19656034c0..7328cf647c 100644 --- a/src/Gui/ViewProviderPy.xml +++ b/src/Gui/ViewProviderPy.xml @@ -120,11 +120,66 @@ Returns 1 if succeed, 0 if not found, -1 if not supported Returns list of objects that are to be grouped in tree under this object. + + + + partialRender(sub=None,clear=False): render only part of the object + + sub: string or list of string refer to the subelement. If it is None then + reset the partial rendering. + clear: true to add, or false to remove the subelement(s) for rendering. + + + + + + +getElementColors(elementName=None) -> dict(elementName:color) + + + + + + +setElementColors(colors): set element colors + +colors: color dictonary of type elementName:(r,g,b,a) + + + + + + getElementPicked(pickPoint): return the picked subelement + + + + + + getDetailPath(subname,path,append=True): return Coin detail and path of an subelement + + subelement: dot separated string reference to the sub element + pPath: output coin path leading to the returned element detail + append: If true, path will be first appended with the root node and the mode + switch node of this view provider. + + + Trigger icon changed signal + + + +getBoundingBox(subname=None, transform=True, view=None): obtain the bounding box of this view object + +* subname: the optional subname referring a sub-object +* transform: whether to apply the transformation matrix of this view provider +* view: the MDIView, default to active view + + + A pivy Separator to add a custom scene graph to this ViewProvider diff --git a/src/Gui/ViewProviderPyImp.cpp b/src/Gui/ViewProviderPyImp.cpp index d31dd64c76..7fba68e6c7 100644 --- a/src/Gui/ViewProviderPyImp.cpp +++ b/src/Gui/ViewProviderPyImp.cpp @@ -377,6 +377,135 @@ PyObject* ViewProviderPy::claimChildren(PyObject* args) return Py::new_reference_to(ret); } +PyObject* ViewProviderPy::partialRender(PyObject* args) +{ + PyObject *value = Py_None; + PyObject *clear = Py_False; + if (!PyArg_ParseTuple(args, "|OO",&value,&clear)) + return NULL; // NULL triggers exception + + std::vector values; + if(value != Py_None) { + PyObject *item = 0; + Py_ssize_t nSize; + if (PyList_Check(value) || PyTuple_Check(value)) + nSize = PySequence_Size(value); + else { + item = value; + value = 0; + nSize = 1; + } + values.resize(nSize); + for (Py_ssize_t i = 0; i < nSize; ++i) { + if(value) item = PySequence_GetItem(value, i); + if (PyUnicode_Check(item)) { +#if PY_MAJOR_VERSION >= 3 + values[i] = PyUnicode_AsUTF8(item); +#else + PyObject* unicode = PyUnicode_AsUTF8String(item); + values[i] = PyString_AsString(unicode); + Py_DECREF(unicode); +#endif + } +#if PY_MAJOR_VERSION < 3 + else if (PyString_Check(item)) { + values[i] = PyString_AsString(item); + } +#endif + else { + std::string error = std::string("type must be str or unicode"); + if(item) { + error += " not, "; + error += item->ob_type->tp_name; + } + throw Base::TypeError(error + item->ob_type->tp_name); + } + } + } + + Py::Int ret(getViewProviderPtr()->partialRender(values,PyObject_IsTrue(clear))); + return Py::new_reference_to(ret); +} + +PyObject* ViewProviderPy::getElementColors(PyObject* args) +{ + const char *element = 0; + if (!PyArg_ParseTuple(args, "|s", &element)) + return 0; + + Py::Dict dict; + for(auto &v : getViewProviderPtr()->getElementColors(element)) { + auto &c = v.second; + dict.setItem(Py::String(v.first), + Py::TupleN(Py::Float(c.r),Py::Float(c.g),Py::Float(c.b),Py::Float(c.a))); + } + return Py::new_reference_to(dict); +} + +PyObject* ViewProviderPy::setElementColors(PyObject* args) +{ + PyObject *pyObj; + if (!PyArg_ParseTuple(args, "O", &pyObj)) + return 0; + + if(!PyDict_Check(pyObj)) + throw Py::TypeError("Expect a dict"); + + std::map colors; + Py::Dict dict(pyObj); + for(auto it=dict.begin();it!=dict.end();++it) { + const auto &value = *it; + if(!value.first.isString() || !value.second.isSequence()) + throw Py::TypeError("Expect the dictonary contain items of type elementName:(r,g,b,a)"); + + App::PropertyColor prop; + prop.setPyObject(value.second.ptr()); + colors[value.first.as_string()] = prop.getValue(); + } + getViewProviderPtr()->setElementColors(colors); + Py_Return; +} + +PyObject* ViewProviderPy::getElementPicked(PyObject* args) +{ + PyObject *obj; + if (!PyArg_ParseTuple(args, "O",&obj)) + return NULL; + void *ptr = 0; + Base::Interpreter().convertSWIGPointerObj("pivy.coin", "_p_SoPickedPoint", obj, &ptr, 0); + SoPickedPoint *pp = reinterpret_cast(ptr); + if(!pp) + throw Base::TypeError("type must be of coin.SoPickedPoint"); + std::string name; + if(!getViewProviderPtr()->getElementPicked(pp,name)) + Py_Return; + return Py::new_reference_to(Py::String(name)); +} + +PyObject* ViewProviderPy::getDetailPath(PyObject* args) +{ + const char *sub; + PyObject *path; + PyObject *append = Py_True; + if (!PyArg_ParseTuple(args, "sO|O",&sub,&path,&append)) + return NULL; + void *ptr = 0; + Base::Interpreter().convertSWIGPointerObj("pivy.coin", "_p_SoPath", path, &ptr, 0); + SoPath *pPath = reinterpret_cast(ptr); + if(!pPath) + throw Base::TypeError("type must be of coin.SoPath"); + SoDetail *det = 0; + if(!getViewProviderPtr()->getDetailPath( + sub,static_cast(pPath),PyObject_IsTrue(append),det)) + { + if(det) delete det; + Py_Return; + } + if(!det) + return Py::new_reference_to(Py::True()); + return Base::Interpreter().createSWIGPointerObj("pivy.coin", "_p_SoDetail", (void*)det, 0); +} + PyObject *ViewProviderPy::signalChangeIcon(PyObject *args) { if (!PyArg_ParseTuple(args, "")) @@ -385,6 +514,22 @@ PyObject *ViewProviderPy::signalChangeIcon(PyObject *args) Py_Return; } +PyObject *ViewProviderPy::getBoundingBox(PyObject *args) { + PyObject *transform=Py_True; + PyObject *pyView = 0; + const char *subname = 0; + if (!PyArg_ParseTuple(args, "|sOO!", &subname,&transform,View3DInventorPy::type_object(),&pyView)) + return NULL; + PY_TRY { + View3DInventor *view = 0; + if(pyView) + view = static_cast(pyView)->getView3DIventorPtr(); + auto bbox = getViewProviderPtr()->getBoundingBox(subname,PyObject_IsTrue(transform),view); + Py::Object ret(new Base::BoundBoxPy(new Base::BoundBox3d(bbox))); + return Py::new_reference_to(ret); + } PY_CATCH; +} + PyObject *ViewProviderPy::doubleClicked(PyObject *args) { if(!PyArg_ParseTuple(args, "")) return 0;