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.
This commit is contained in:
@@ -30,22 +30,34 @@
|
||||
# include <Inventor/nodes/SoDrawStyle.h>
|
||||
# include <Inventor/nodes/SoMaterial.h>
|
||||
# include <Inventor/nodes/SoSeparator.h>
|
||||
# include <Inventor/nodes/SoSwitch.h>
|
||||
# include <Inventor/nodes/SoTransform.h>
|
||||
# include <Inventor/SoPickedPoint.h>
|
||||
# include <Inventor/SoFullPath.h>
|
||||
# include <Inventor/misc/SoChildList.h>
|
||||
# include <Inventor/details/SoDetail.h>
|
||||
#endif
|
||||
|
||||
/// Here the FreeCAD includes sorted by Base,App,Gui......
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/Console.h>
|
||||
#include <Base/Tools.h>
|
||||
#include <Base/BoundBox.h>
|
||||
#include <App/Material.h>
|
||||
#include <App/DocumentObjectGroup.h>
|
||||
#include <App/DocumentObserver.h>
|
||||
#include <App/Origin.h>
|
||||
#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 <Gui/ViewProviderDocumentObjectPy.h>
|
||||
|
||||
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<Gui::TaskView::T
|
||||
void ViewProviderDocumentObject::startRestoring()
|
||||
{
|
||||
hide();
|
||||
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
|
||||
for(Gui::ViewProviderExtension* ext : vector)
|
||||
ext->extensionStartRestoring();
|
||||
}
|
||||
|
||||
void ViewProviderDocumentObject::finishRestoring()
|
||||
{
|
||||
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
|
||||
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<ViewStatus,ViewProviderDocumentObject> lock(ViewStatus::UpdatingView,this);
|
||||
@@ -196,7 +229,7 @@ void ViewProviderDocumentObject::updateView()
|
||||
for (std::map<std::string, App::Property*>::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<Gui::ViewProviderExtension>();
|
||||
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<ViewProviderDocumentObject*>(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<std::string> &elements) const
|
||||
{
|
||||
auto vector = getExtensionsDerivedFromType<Gui::ViewProviderExtension>();
|
||||
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<std::pair<App::DocumentObjectT, std::unique_ptr<App::Property> > > propChanges;
|
||||
std::vector<App::Property*> props;
|
||||
obj->getPropertyList(props);
|
||||
for(auto prop : props) {
|
||||
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
|
||||
if(!linkProp)
|
||||
continue;
|
||||
std::unique_ptr<App::Property> 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<App::Property*> props;
|
||||
o->getPropertyList(props);
|
||||
for(auto prop : props) {
|
||||
auto linkProp = Base::freecad_dynamic_cast<App::PropertyLinkBase>(prop);
|
||||
if(!linkProp)
|
||||
continue;
|
||||
std::unique_ptr<App::Property> 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<Gui::ViewProviderExtension>();
|
||||
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<ViewProviderDocumentObject*>(this);
|
||||
if(!pcObject || !pcObject->getNameInDocument())
|
||||
return self;
|
||||
auto linked = pcObject->getLinkedObject(recursive);
|
||||
if(!linked || linked == pcObject)
|
||||
return self;
|
||||
auto res = Base::freecad_dynamic_cast<ViewProviderDocumentObject>(
|
||||
Application::Instance->getViewProvider(linked));
|
||||
if(!res)
|
||||
res = self;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string ViewProviderDocumentObject::getFullName() const {
|
||||
if(pcObject)
|
||||
return pcObject->getFullName() + ".ViewObject";
|
||||
|
||||
Reference in New Issue
Block a user