/**************************************************************************** * Copyright (c) 2022 Zheng Lei (realthunder) * * * * 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 "Application.h" #include "Document.h" #include "SoFCUnifiedSelection.h" #include "View3DInventorSelection.h" #include "ViewProviderDocumentObject.h" #include #include #include #include FC_LOG_LEVEL_INIT("3DViewerSelection", true, true) using namespace Gui; View3DInventorSelection::View3DInventorSelection(SoFCUnifiedSelection* root) : selectionRoot(root) { selectionRoot->ref(); pcGroupOnTop = new SoSeparator; pcGroupOnTop->ref(); pcGroupOnTop->setName("GroupOnTop"); root->addChild(pcGroupOnTop); auto pcGroupOnTopPickStyle = new SoPickStyle; pcGroupOnTopPickStyle->style = SoPickStyle::UNPICKABLE; pcGroupOnTopPickStyle->setOverride(true); pcGroupOnTopPickStyle->setName("GroupOnTopPickStyle"); pcGroupOnTop->addChild(pcGroupOnTopPickStyle); coin_setenv("COIN_SEPARATE_DIFFUSE_TRANSPARENCY_OVERRIDE", "1", TRUE); auto pcGroupOnTopMaterial = new SoMaterial; pcGroupOnTopMaterial->transparency = 0.5; pcGroupOnTopMaterial->diffuseColor.setIgnored(true); pcGroupOnTopMaterial->setOverride(true); pcGroupOnTopMaterial->setName("GroupOnTopMaterial"); pcGroupOnTop->addChild(pcGroupOnTopMaterial); { auto selRoot = new SoFCSelectionRoot; selRoot->selectionStyle = SoFCSelectionRoot::PassThrough; pcGroupOnTopSel = selRoot; pcGroupOnTopSel->setName("GroupOnTopSel"); pcGroupOnTopSel->ref(); pcGroupOnTop->addChild(pcGroupOnTopSel); } { auto selRoot = new SoFCSelectionRoot; selRoot->selectionStyle = SoFCSelectionRoot::PassThrough; pcGroupOnTopPreSel = selRoot; pcGroupOnTopPreSel->setName("GroupOnTopPreSel"); pcGroupOnTopPreSel->ref(); pcGroupOnTop->addChild(pcGroupOnTopPreSel); } } View3DInventorSelection::~View3DInventorSelection() { selectionRoot->unref(); pcGroupOnTop->unref(); pcGroupOnTopPreSel->unref(); pcGroupOnTopSel->unref(); } void View3DInventorSelection::checkGroupOnTop(const SelectionChanges& Reason) { if (Reason.Type == SelectionChanges::SetSelection || Reason.Type == SelectionChanges::ClrSelection) { clearGroupOnTop(); if (Reason.Type == SelectionChanges::ClrSelection) { return; } } if (Reason.Type == SelectionChanges::RmvPreselect || Reason.Type == SelectionChanges::RmvPreselectSignal) { SoSelectionElementAction action(SoSelectionElementAction::None, true); action.apply(pcGroupOnTopPreSel); coinRemoveAllChildren(pcGroupOnTopPreSel); objectsOnTopPreSel.clear(); return; } if (!getDocument() || !Reason.pDocName || !Reason.pDocName[0] || !Reason.pObjectName) { return; } auto obj = getDocument()->getDocument()->getObject(Reason.pObjectName); if (!obj || !obj->isAttachedToDocument()) { return; } std::string key(obj->getNameInDocument()); key += '.'; auto subname = Reason.pSubName; App::ElementNamePair element; App::GeoFeature::resolveElement(obj, Reason.pSubName, element); if (Data::isMappedElement(subname) && !element.oldName.empty()) { // If we have a shortened // element name subname = element.oldName.c_str(); // use if } if (subname) { key += subname; } if (Reason.Type == SelectionChanges::RmvSelection) { auto& objs = objectsOnTop; auto pcGroup = pcGroupOnTopSel; auto it = objs.find(key.c_str()); if (it == objs.end()) { return; } int index = pcGroup->findChild(it->second); if (index >= 0) { auto node = static_cast(it->second); SoSelectionElementAction action( node->getDetail() ? SoSelectionElementAction::Remove : SoSelectionElementAction::None, true ); auto path = node->getPath(); SoTempPath tmpPath(2 + (path ? path->getLength() : 0)); tmpPath.ref(); tmpPath.append(pcGroup); tmpPath.append(node); tmpPath.append(node->getPath()); action.setElement(node->getDetail()); action.apply(&tmpPath); tmpPath.unrefNoDelete(); pcGroup->removeChild(index); FC_LOG("remove annotation " << Reason.Type << " " << key); } else { FC_LOG("remove annotation object " << Reason.Type << " " << key); } objs.erase(it); return; } auto& objs = Reason.Type == SelectionChanges::SetPreselect ? objectsOnTopPreSel : objectsOnTop; auto pcGroup = Reason.Type == SelectionChanges::SetPreselect ? pcGroupOnTopPreSel : pcGroupOnTopSel; if (objs.find(key.c_str()) != objs.end()) { return; } auto vp = freecad_cast(Application::Instance->getViewProvider(obj)); if (!vp || !vp->isSelectable() || !vp->isShow()) { return; } auto svp = vp; if (subname && *subname) { auto sobj = obj->getSubObject(subname); if (!sobj || !sobj->isAttachedToDocument()) { return; } if (sobj != obj) { svp = freecad_cast( Application::Instance->getViewProvider(sobj) ); if (!svp || !svp->isSelectable()) { return; } } } int onTop; // onTop==2 means on top only if whole object is selected, // onTop==3 means on top only if some sub-element is selected // onTop==1 means either if (vp->OnTopWhenSelected.getValue()) { onTop = vp->OnTopWhenSelected.getValue(); } else { onTop = svp->OnTopWhenSelected.getValue(); } if (Reason.Type == SelectionChanges::SetPreselect) { SoHighlightElementAction action; action.setHighlighted(true); action.setColor(selectionRoot->colorHighlight.getValue()); action.apply(pcGroupOnTopPreSel); if (!onTop) { onTop = 2; } } else { if (!onTop) { return; } SoSelectionElementAction action(SoSelectionElementAction::All); action.setColor(selectionRoot->colorHighlight.getValue()); action.apply(pcGroupOnTopSel); } if (onTop == 2 || onTop == 3) { if (subname && *subname) { size_t len = strlen(subname); if (subname[len - 1] == '.') { // ending with '.' means whole object selection if (onTop == 3) { return; } } else if (onTop == 2) { return; } } else if (onTop == 3) { return; } } std::vector groups; auto grpVp = vp; std::set visited; for (auto childVp = vp;; childVp = grpVp) { auto grp = App::GeoFeatureGroupExtension::getGroupOfObject(childVp->getObject()); if (!grp || !grp->isAttachedToDocument()) { break; } grpVp = freecad_cast(Application::Instance->getViewProvider(grp)); if (!grpVp) { break; } // avoid endless-loops if (!visited.insert(childVp).second) { break; } auto childRoot = grpVp->getChildRoot(); auto modeSwitch = grpVp->getModeSwitch(); auto idx = modeSwitch->whichChild.getValue(); if (idx < 0 || idx >= modeSwitch->getNumChildren() || modeSwitch->getChild(idx) != childRoot) { FC_LOG( "skip " << obj->getFullName() << '.' << (subname ? subname : "") << ", hidden inside geo group" ); return; } if (childRoot->findChild(childVp->getRoot()) < 0) { FC_LOG( "cannot find '" << childVp->getObject()->getFullName() << "' in geo group '" << grp->getNameInDocument() << "'" ); break; } groups.push_back(grpVp); } SoTempPath path(10); path.ref(); for (auto it = groups.rbegin(); it != groups.rend(); ++it) { auto grpVp = *it; path.append(grpVp->getRoot()); path.append(grpVp->getModeSwitch()); path.append(grpVp->getChildRoot()); } SoDetail* det = nullptr; if (vp->getDetailPath(subname, &path, true, det) && path.getLength()) { auto node = new SoFCPathAnnotation; node->setPath(&path); pcGroup->addChild(node); if (det) { SoSelectionElementAction action(SoSelectionElementAction::Append, true); action.setElement(det); SoTempPath tmpPath(path.getLength() + 2); tmpPath.ref(); tmpPath.append(pcGroup); tmpPath.append(node); tmpPath.append(&path); action.apply(&tmpPath); tmpPath.unrefNoDelete(); node->setDetail(det); det = nullptr; } FC_LOG("add annotation " << Reason.Type << " " << key); objs[key.c_str()] = node; } delete det; path.unrefNoDelete(); } void View3DInventorSelection::clearGroupOnTop() { if (!objectsOnTop.empty() || !objectsOnTopPreSel.empty()) { objectsOnTop.clear(); objectsOnTopPreSel.clear(); SoSelectionElementAction action(SoSelectionElementAction::None, true); action.apply(pcGroupOnTopPreSel); action.apply(pcGroupOnTopSel); coinRemoveAllChildren(pcGroupOnTopSel); coinRemoveAllChildren(pcGroupOnTopPreSel); FC_LOG("clear annotation"); } }