/*************************************************************************** * Copyright (c) 2004 Jürgen Riegel * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Inventor/SoMouseWheelEvent.h" #include "Inventor/SoFCTransform.h" #include "ViewProvider.h" #include "ActionFunction.h" #include "Application.h" #include "BitmapFactory.h" #include "Document.h" #include "DockWindowManager.h" #include "SoFCDB.h" #include "View3DInventor.h" #include "View3DInventorViewer.h" #include "ViewParams.h" #include "ViewProviderExtension.h" #include "ViewProviderLink.h" #include "ViewProviderPy.h" #include FC_LOG_LEVEL_INIT("ViewProvider", true, true) using namespace std; using namespace Gui; namespace Gui { void coinRemoveAllChildren(SoGroup* group) { if (!group) { return; } int count = group->getNumChildren(); if (!count) { return; } FC_TRACE("coin remove all children " << count); SbBool autonotify = group->enableNotify(FALSE); for (; count > 0; --count) { group->removeChild(count - 1); } group->enableNotify(autonotify); group->touch(); } } // namespace Gui //************************************************************************** //************************************************************************** // ViewProvider //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ PROPERTY_SOURCE_ABSTRACT(Gui::ViewProvider, App::TransactionalObject) ViewProvider::ViewProvider() : overrideMode("As Is") , toggleVisibilityMode(ToggleVisibilityMode::CanToggleVisibility) { setStatus(UpdateData, true); // SoFCSeparator and SoFCSelectionRoot can both track render cache setting. // We change to SoFCSelectionRoot so that we can dynamically change full // selection mode (full highlight vs. boundbox). Note that comparing to // SoFCSeparator, there are some small overhead with SoFCSelectionRoot for // selection context tracking. // // pcRoot = new SoFCSeparator(true); pcRoot = new SoFCSelectionRoot(true); pcRoot->ref(); pcModeSwitch = new SoSwitch(); pcModeSwitch->ref(); pcModeSwitch->setName("ModeSwitch"); pcTransform = new SoFCTransform(); pcTransform->ref(); pcRoot->addChild(pcTransform); pcRoot->addChild(pcModeSwitch); sPixmap = "px"; pcModeSwitch->whichChild = _iActualMode; setRenderCacheMode(ViewParams::instance()->getRenderCache()); } ViewProvider::~ViewProvider() { if (pyViewObject) { Base::PyGILStateLocker lock; pyViewObject->setInvalid(); pyViewObject->DecRef(); } pcRoot->unref(); pcTransform->unref(); pcModeSwitch->unref(); if (pcAnnotation) { pcAnnotation->unref(); } } ViewProvider* ViewProvider::startEditing(int ModNum) { try { if (setEdit(ModNum)) { _iEditMode = ModNum; return this; } } catch (const Base::Exception& e) { e.reportException(); } return nullptr; } int ViewProvider::getEditingMode() const { return _iEditMode; } bool ViewProvider::isEditing() const { return getEditingMode() > -1; } void ViewProvider::finishEditing() { unsetEdit(_iEditMode); _iEditMode = -1; } bool ViewProvider::setEdit(int ModNum) { Q_UNUSED(ModNum); return true; } void ViewProvider::unsetEdit(int ModNum) { Q_UNUSED(ModNum); } void ViewProvider::setEditViewer(View3DInventorViewer*, int ModNum) { Q_UNUSED(ModNum); } void ViewProvider::unsetEditViewer(View3DInventorViewer*) {} bool ViewProvider::isUpdatesEnabled() const { return testStatus(UpdateData); } void ViewProvider::setUpdatesEnabled(bool enable) { setStatus(UpdateData, enable); } void highlight(const HighlightMode& high) { Q_UNUSED(high); } void ViewProvider::eventCallback(void* ud, SoEventCallback* node) { const SoEvent* ev = node->getEvent(); auto viewer = static_cast(node->getUserData()); auto self = static_cast(ud); assert(self); try { // Keyboard events if (ev->getTypeId().isDerivedFrom(SoKeyboardEvent::getClassTypeId())) { auto ke = static_cast(ev); const SbBool press = ke->getState() == SoButtonEvent::DOWN ? true : false; switch (ke->getKey()) { case SoKeyboardEvent::ESCAPE: if (self->keyPressed(press, ke->getKey())) { node->setHandled(); } else if (QApplication::mouseButtons() == Qt::NoButton) { // Because of a Coin bug // (https://bitbucket.org/Coin3D/coin/pull-requests/119), FC may crash if // user hits ESC to cancel while still holding the mouse button while using // some SoDragger. Therefore, we shall ignore ESC while any mouse button is // pressed, until this Coin bug is fixed. if (!press) { // react only on key release // Let first selection mode terminate Gui::Document* doc = Gui::Application::Instance->activeDocument(); const auto view = qobject_cast(doc->getActiveView()); if (view) { Gui::View3DInventorViewer* viewer = view->getViewer(); if (viewer->isSelecting()) { return; } } DockWindowManager* pDockMgr = DockWindowManager::instance(); if (QWidget* widget = pDockMgr->getDockWindow("Tasks")) { auto* ev = new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); qApp->postEvent(widget, ev); } auto func = new Gui::TimerFunction(); func->setAutoDelete(true); func->setFunction([doc]() { doc->resetEdit(); }); func->singleShot(0); } } else if (press) { FC_WARN("Release all mouse buttons before exiting editing"); } break; default: // call the virtual method if (self->keyPressed(press, ke->getKey())) { node->setHandled(); } break; } } // switching the mouse buttons else if (ev->getTypeId().isDerivedFrom(SoMouseButtonEvent::getClassTypeId())) { const auto event = (const SoMouseButtonEvent*)ev; const int button = event->getButton(); const SbBool press = event->getState() == SoButtonEvent::DOWN ? true : false; // call the virtual method if (self->mouseButtonPressed(button, press, ev->getPosition(), viewer)) { node->setHandled(); } } else if (ev->getTypeId().isDerivedFrom(SoMouseWheelEvent::getClassTypeId())) { const auto event = (const SoMouseWheelEvent*)ev; if (self->mouseWheelEvent(event->getDelta(), event->getPosition(), viewer)) { node->setHandled(); } } // Mouse Movement handling else if (ev->getTypeId().isDerivedFrom(SoLocation2Event::getClassTypeId())) { if (self->mouseMove(ev->getPosition(), viewer)) { node->setHandled(); } } } catch (const Base::Exception& e) { Base::Console().error( "Unhandled exception in ViewProvider::eventCallback: %s\n" "(Event type: %s, object type: %s)\n", e.what(), ev->getTypeId().getName().getString(), self->getTypeId().getName() ); } catch (const std::exception& e) { Base::Console().error( "Unhandled std exception in ViewProvider::eventCallback: %s\n" "(Event type: %s, object type: %s)\n", e.what(), ev->getTypeId().getName().getString(), self->getTypeId().getName() ); } catch (...) { Base::Console().error( "Unhandled unknown C++ exception in ViewProvider::eventCallback" " (Event type: %s, object type: %s)\n", ev->getTypeId().getName().getString(), self->getTypeId().getName() ); } } SoSeparator* ViewProvider::getAnnotation() { if (!pcAnnotation) { pcAnnotation = new SoSeparator(); pcAnnotation->ref(); pcRoot->addChild(pcAnnotation); } return pcAnnotation; } void ViewProvider::update(const App::Property* prop) { // Hide the object temporarily to speed up the update if (!isUpdatesEnabled()) { return; } bool vis = ViewProvider::isShow(); if (vis) { ViewProvider::hide(); } updateData(prop); if (vis) { ViewProvider::show(); } } QIcon ViewProvider::getIcon() const { return mergeGreyableOverlayIcons(Gui::BitmapFactory().pixmap(sPixmap)); } QIcon ViewProvider::mergeGreyableOverlayIcons(const QIcon& orig) const { auto vector = getExtensionsDerivedFromType(); QIcon overlayedIcon = orig; for (Gui::ViewProviderExtension* ext : vector) { if (!ext->ignoreOverlayIcon()) { overlayedIcon = ext->extensionMergeGreyableOverlayIcons(overlayedIcon); } } return overlayedIcon; } QIcon ViewProvider::mergeColorfulOverlayIcons(const QIcon& orig) const { auto vector = getExtensionsDerivedFromType(); QIcon overlayedIcon = orig; for (Gui::ViewProviderExtension* ext : vector) { if (!ext->ignoreOverlayIcon()) { overlayedIcon = ext->extensionMergeColorfullOverlayIcons(overlayedIcon); } } return overlayedIcon; } void ViewProvider::setTransformation(const Base::Matrix4D& rcMatrix) { pcTransform->setMatrix(convert(rcMatrix)); } void ViewProvider::setTransformation(const SbMatrix& rcMatrix) { pcTransform->setMatrix(rcMatrix); } SbMatrix ViewProvider::convert(const Base::Matrix4D& rcMatrix) { return Base::convertTo(rcMatrix); } Base::Matrix4D ViewProvider::convert(const SbMatrix& smat) { return Base::convertTo(smat); } void ViewProvider::addDisplayMaskMode(SoNode* node, const char* type) { _sDisplayMaskModes[type] = pcModeSwitch->getNumChildren(); pcModeSwitch->addChild(node); } void ViewProvider::setDisplayMaskMode(const char* type) { std::map::const_iterator it = _sDisplayMaskModes.find(type); if (it != _sDisplayMaskModes.end()) { _iActualMode = it->second; } else { _iActualMode = -1; } setModeSwitch(); } SoNode* ViewProvider::getDisplayMaskMode(const char* type) const { std::map::const_iterator it = _sDisplayMaskModes.find(type); if (it != _sDisplayMaskModes.end()) { return pcModeSwitch->getChild(it->second); } return nullptr; } std::vector ViewProvider::getDisplayMaskModes() const { std::vector types; for (const auto& it : _sDisplayMaskModes) { types.push_back(it.first); } return types; } /** * If you add new viewing modes in @ref getDisplayModes() then you need to reimplement * also seDisplaytMode() to handle these new modes by setting the appropriate display * mode. */ void ViewProvider::setDisplayMode(const char* ModeName) { _sCurrentMode = ModeName; // infom the exteensions auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { ext->extensionSetDisplayMode(ModeName); } } const char* ViewProvider::getDefaultDisplayMode() const { return nullptr; } vector ViewProvider::getDisplayModes() const { std::vector modes; auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { auto extModes = ext->extensionGetDisplayModes(); modes.insert(modes.end(), extModes.begin(), extModes.end()); } return modes; } std::string ViewProvider::getActiveDisplayMode() const { return _sCurrentMode; } void ViewProvider::hide() { auto exts = getExtensionsDerivedFromType(); if (pcModeSwitch->whichChild.getValue() >= 0) { pcModeSwitch->whichChild = -1; for (auto ext : exts) { ext->extensionModeSwitchChange(); } } // tell extensions that we hide for (Gui::ViewProviderExtension* ext : exts) { ext->extensionHide(); } } void ViewProvider::show() { setModeSwitch(); // tell extensions that we show auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { ext->extensionShow(); } } bool ViewProvider::isShow() const { return pcModeSwitch->whichChild.getValue() != -1; } void ViewProvider::setVisible(bool s) { s ? show() : hide(); } bool ViewProvider::isVisible() const { return isShow(); } void ViewProvider::setOverrideMode(const std::string& mode) { if (mode == "As Is") { viewOverrideMode = -1; overrideMode = mode; } else { std::map::const_iterator it = _sDisplayMaskModes.find(mode); if (it == _sDisplayMaskModes.end()) { return; // view style not supported } viewOverrideMode = (*it).second; overrideMode = mode; } if (pcModeSwitch->whichChild.getValue() != -1) { setModeSwitch(); } else { for (auto ext : getExtensionsDerivedFromType()) { ext->extensionModeSwitchChange(); } } } const string ViewProvider::getOverrideMode() { return overrideMode; } void ViewProvider::setModeSwitch() { if (viewOverrideMode == -1) { pcModeSwitch->whichChild = _iActualMode; } else if (viewOverrideMode < pcModeSwitch->getNumChildren()) { pcModeSwitch->whichChild = viewOverrideMode; } else { return; } for (auto ext : getExtensionsDerivedFromType()) { ext->extensionModeSwitchChange(); } } void ViewProvider::setDefaultMode(int val) { _iActualMode = val; for (auto ext : getExtensionsDerivedFromType()) { ext->extensionModeSwitchChange(); } } int ViewProvider::getDefaultMode() const { return viewOverrideMode >= 0 ? viewOverrideMode : _iActualMode; } void ViewProvider::onBeforeChange(const App::Property* prop) { Application::Instance->signalBeforeChangeObject(*this, *prop); App::TransactionalObject::onBeforeChange(prop); } void ViewProvider::onChanged(const App::Property* prop) { Application::Instance->signalChangedObject(*this, *prop); Application::Instance->updateActions(); App::TransactionalObject::onChanged(prop); } std::string ViewProvider::toString() const { return SoFCDB::writeNodesToString(pcRoot); } PyObject* ViewProvider::getPyObject() { if (!pyViewObject) { pyViewObject = new ViewProviderPy(this); } pyViewObject->IncRef(); return pyViewObject; } #include namespace Gui { using Graph = boost::adjacency_list< boost::vecS, // class OutEdgeListS : a Sequence or an AssociativeContainer boost::vecS, // class VertexListS : a Sequence or a RandomAccessContainer boost::directedS, // class DirectedS : This is a directed graph boost::no_property, // class VertexProperty: boost::no_property, // class EdgeProperty: boost::no_property, // class GraphProperty: boost::listS // class EdgeListS: >; using Vertex = boost::graph_traits::vertex_descriptor; using Edge = boost::graph_traits::edge_descriptor; void addNodes(Graph& graph, std::map& vertexNodeMap, SoNode* node) { if (node->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())) { auto group = static_cast(node); Vertex groupV = vertexNodeMap[group]; for (int i = 0; i < group->getNumChildren(); i++) { SoNode* child = group->getChild(i); auto it = vertexNodeMap.find(child); // the child node is not yet added to the map if (it == vertexNodeMap.end()) { Vertex childV = add_vertex(graph); vertexNodeMap[child] = childV; add_edge(groupV, childV, graph); addNodes(graph, vertexNodeMap, child); } // the child is already there, only add the edge then else { add_edge(groupV, it->second, graph); } } } } } // namespace Gui bool ViewProvider::checkRecursion(SoNode* node) { if (node->getTypeId().isDerivedFrom(SoGroup::getClassTypeId())) { std::list make_order; Graph graph; std::map vertexNodeMap; Vertex groupV = add_vertex(graph); vertexNodeMap[node] = groupV; addNodes(graph, vertexNodeMap, node); try { boost::topological_sort(graph, std::front_inserter(make_order)); } catch (const std::exception&) { return false; } } return true; } SoPickedPoint* ViewProvider::getPointOnRay(const SbVec2s& pos, const View3DInventorViewer* viewer) const { return viewer->getPointOnRay(pos, this); } SoPickedPoint* ViewProvider::getPointOnRay( const SbVec3f& pos, const SbVec3f& dir, const View3DInventorViewer* viewer ) const { return viewer->getPointOnRay(pos, dir, this); } std::vector ViewProvider::getModelPoints(const SoPickedPoint* pp) const { // the default implementation just returns the picked point from the visual representation std::vector pts; const SbVec3f& vec = pp->getPoint(); pts.emplace_back(vec[0], vec[1], vec[2]); return pts; } bool ViewProvider::keyPressed(bool pressed, int key) { (void)pressed; (void)key; return false; } bool ViewProvider::mouseMove(const SbVec2s& cursorPos, View3DInventorViewer* viewer) { (void)cursorPos; (void)viewer; return false; } bool ViewProvider::mouseButtonPressed( int button, bool pressed, const SbVec2s& cursorPos, const View3DInventorViewer* viewer ) { (void)button; (void)pressed; (void)cursorPos; (void)viewer; return false; } bool ViewProvider::mouseWheelEvent(int delta, const SbVec2s& cursorPos, const View3DInventorViewer* viewer) { (void)delta; (void)cursorPos; (void)viewer; return false; } void ViewProvider::setupContextMenu(QMenu* menu, QObject* receiver, const char* method) { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { ext->extensionSetupContextMenu(menu, receiver, method); } } bool ViewProvider::onDelete(const vector& subNames) { bool del = true; auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { del &= ext->extensionOnDelete(subNames); } return del; } bool ViewProvider::canDelete(App::DocumentObject*) const { return false; } bool ViewProvider::canDragObject(App::DocumentObject* obj) const { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { if (ext->extensionCanDragObject(obj)) { return true; } } return false; } bool ViewProvider::canDragObjectToTarget( App::DocumentObject* obj, [[maybe_unused]] App::DocumentObject* target ) const { return canDragObject(obj); } bool ViewProvider::canDragObjects() const { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { if (ext->extensionCanDragObjects()) { return true; } } return false; } void ViewProvider::dragObject(App::DocumentObject* obj) { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { if (ext->extensionCanDragObject(obj)) { ext->extensionDragObject(obj); return; } } throw Base::RuntimeError( "ViewProvider::dragObject: no extension for dragging given object available." ); } bool ViewProvider::canDropObject(App::DocumentObject* obj) const { auto vector = getExtensionsDerivedFromType(); #if FC_DEBUG Base::Console().log("Check extensions for drop\n"); #endif for (Gui::ViewProviderExtension* ext : vector) { #if FC_DEBUG Base::Console().log("Check extensions %s\n", ext->name().c_str()); #endif if (ext->extensionCanDropObject(obj)) { return true; } } return false; } bool ViewProvider::canDropObjects() const { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { if (ext->extensionCanDropObjects()) { return true; } } return false; } bool ViewProvider::canDragAndDropObject(App::DocumentObject* obj) const { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { if (ext->extensionCanDragAndDropObject(obj)) { return true; } } return false; } void ViewProvider::dropObject(App::DocumentObject* obj) { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { if (ext->extensionCanDropObject(obj)) { ext->extensionDropObject(obj); break; } } } bool ViewProvider::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; } } return canDropObject(obj); } std::string ViewProvider::dropObjectEx( App::DocumentObject* obj, App::DocumentObject* owner, const char* subname, const std::vector& elements ) { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { if (ext->extensionCanDropObjectEx(obj, owner, subname, elements)) { return ext->extensionDropObjectEx(obj, owner, subname, elements); } } dropObject(obj); return {}; } int ViewProvider::replaceObject(App::DocumentObject* oldValue, App::DocumentObject* newValue) { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { if (ext->extensionCanDropObject(newValue)) { int ret = ext->extensionReplaceObject(oldValue, newValue); if (ret >= 0) { return !!ret; } } } return -1; } void ViewProvider::Restore(Base::XMLReader& reader) { // Because some PropertyLists type properties are stored in a separate file, // and is thus restored outside this function. So we rely on Gui::Document // to set the isRestoring flags for us. // // setStatus(Gui::isRestoring, true); TransactionalObject::Restore(reader); // setStatus(Gui::isRestoring, false); } void ViewProvider::updateData(const App::Property* prop) { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { ext->extensionUpdateData(prop); } } SoSeparator* ViewProvider::getBackRoot() const { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { auto* node = ext->extensionGetBackRoot(); if (node) { return node; } } return nullptr; } SoGroup* ViewProvider::getChildRoot() const { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { auto* node = ext->extensionGetChildRoot(); if (node) { return node; } } return nullptr; } SoSeparator* ViewProvider::getFrontRoot() const { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { auto* node = ext->extensionGetFrontRoot(); if (node) { return node; } } return nullptr; } std::vector ViewProvider::claimChildren() const { std::vector vec; auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { std::vector nvec = ext->extensionClaimChildren(); if (!nvec.empty()) { vec.insert(std::end(vec), std::begin(nvec), std::end(nvec)); } } return vec; } std::vector ViewProvider::claimChildrenRecursive() const { std::vector children = claimChildren(); for (auto* child : claimChildren()) { auto* vp = Application::Instance->getViewProvider(child); if (!vp) { continue; } std::vector nvec = vp->claimChildrenRecursive(); if (!nvec.empty()) { children.insert(std::end(children), std::begin(nvec), std::end(nvec)); } } return children; } std::vector ViewProvider::claimChildren3D() const { std::vector vec; auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { std::vector nvec = ext->extensionClaimChildren3D(); if (!nvec.empty()) { vec.insert(std::end(vec), std::begin(nvec), std::end(nvec)); } } return vec; } bool ViewProvider::getElementPicked(const SoPickedPoint* pp, std::string& subname) const { auto vector = getExtensionsDerivedFromType(); for (Gui::ViewProviderExtension* ext : vector) { if (ext->extensionGetElementPicked(pp, subname)) { return true; } } subname = getElement(pp ? pp->getDetail() : nullptr); 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; auto 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 = nullptr; 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(); for (Gui::ViewProviderExtension* ext : vector) { ext->extensionBeforeDelete(); } } void ViewProvider::setRenderCacheMode(int mode) { pcRoot->renderCaching = mode == 0 ? SoSeparator::AUTO : (mode == 1 ? SoSeparator::ON : SoSeparator::OFF); } 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 = qobject_cast(view); if (!iview) { auto doc = Application::Instance->activeDocument(); if (doc) { auto views = doc->getMDIViewsOfType(View3DInventor::getClassTypeId()); if (!views.empty()) { iview = qobject_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 (!Base::Tools::isNullOrEmpty(subname)) { SoDetail* det = nullptr; 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) { return true; } return ext->isLinkVisible(); } void ViewProvider::setLinkVisible(bool visible) { auto ext = getExtensionByType(true); if (ext) { ext->setLinkVisible(visible); } }