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:
Zheng, Lei
2019-06-22 10:23:35 +08:00
committed by wmayer
parent eb6e56912e
commit cd7725227f
8 changed files with 713 additions and 14 deletions

View File

@@ -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";