| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #include <QApplication> |
| | #include <QInputDialog> |
| | #include <QMessageBox> |
| | #include <TopExp_Explorer.hxx> |
| |
|
| | #include <App/Datums.h> |
| | #include <App/Document.h> |
| | #include <App/GeoFeatureGroupExtension.h> |
| | #include <App/Origin.h> |
| | #include <App/Part.h> |
| | #include <Base/Console.h> |
| | #include <Base/Tools.h> |
| | #include <Gui/Command.h> |
| | #include <Gui/Control.h> |
| | #include <Gui/Document.h> |
| | #include <Gui/Application.h> |
| | #include <Gui/MainWindow.h> |
| | #include <Gui/MDIView.h> |
| | #include <Mod/Sketcher/App/SketchObject.h> |
| | #include <Mod/PartDesign/App/Body.h> |
| | #include <Mod/PartDesign/App/FeatureBase.h> |
| | #include <Mod/PartDesign/App/FeatureSketchBased.h> |
| |
|
| | #include "TaskFeaturePick.h" |
| | #include "Utils.h" |
| | #include "WorkflowManager.h" |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | namespace PartDesignGui |
| | { |
| |
|
| | |
| | App::Part* assertActivePart() |
| | { |
| | App::Part* rv = Gui::Application::Instance->activeView()->getActiveObject<App::Part*>(PARTKEY); |
| |
|
| | if (!rv) { |
| | Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager(); |
| | rcCmdMgr.runCommandByName("Std_Part"); |
| | rv = Gui::Application::Instance->activeView()->getActiveObject<App::Part*>(PARTKEY); |
| | if (!rv) { |
| | QMessageBox::critical( |
| | nullptr, |
| | QObject::tr("Part creation failed"), |
| | QObject::tr("Failed to create a part object.") |
| | ); |
| | } |
| | } |
| |
|
| | return rv; |
| | } |
| |
|
| | } |
| |
|
| | |
| | |
| | DEF_STD_CMD_A(CmdPartDesignBody) |
| |
|
| | CmdPartDesignBody::CmdPartDesignBody() |
| | : Command("PartDesign_Body") |
| | { |
| | sAppModule = "PartDesign"; |
| | sGroup = QT_TR_NOOP("PartDesign"); |
| | sMenuText = QT_TR_NOOP("New Body"); |
| | sToolTipText = QT_TR_NOOP("Creates a new body and activates it"); |
| | sWhatsThis = "PartDesign_Body"; |
| | sStatusTip = sToolTipText; |
| | sPixmap = "PartDesign_Body"; |
| | } |
| |
|
| | void CmdPartDesignBody::activated(int iMsg) |
| | { |
| | Q_UNUSED(iMsg); |
| |
|
| | App::Part* actPart = PartDesignGui::getActivePart(); |
| | App::Part* partOfBaseFeature = nullptr; |
| |
|
| | std::vector<App::DocumentObject*> features = getSelection().getObjectsOfType( |
| | Part::Feature::getClassTypeId() |
| | ); |
| | App::DocumentObject* baseFeature = nullptr; |
| | bool addtogroup = false; |
| |
|
| | Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter().GetGroup( |
| | "BaseApp/Preferences/Mod/PartDesign" |
| | ); |
| |
|
| | bool allowCompound = hGrp->GetBool("AllowCompoundDefault", true); |
| |
|
| | if (!features.empty()) { |
| | if (features.size() == 1) { |
| | baseFeature = features[0]; |
| | if (baseFeature->isDerivedFrom(PartDesign::Feature::getClassTypeId()) |
| | && PartDesign::Body::findBodyOf(baseFeature)) { |
| | |
| | QMessageBox::warning( |
| | Gui::getMainWindow(), |
| | QObject::tr("Bad base feature"), |
| | QObject::tr("A body cannot be based on a Part Design feature.") |
| | ); |
| | baseFeature = nullptr; |
| | } |
| | else if (PartDesign::Body::findBodyOf(baseFeature)) { |
| | QMessageBox::warning( |
| | Gui::getMainWindow(), |
| | QObject::tr("Bad base feature"), |
| | QObject::tr("%1 already belongs to a body and cannot be used as a base feature for another body.") |
| | .arg(QString::fromUtf8(baseFeature->Label.getValue())) |
| | ); |
| | baseFeature = nullptr; |
| | } |
| | else if (baseFeature->isDerivedFrom(Part::BodyBase::getClassTypeId())) { |
| | |
| | baseFeature = nullptr; |
| | } |
| | else { |
| | partOfBaseFeature = App::Part::getPartOfObject(baseFeature); |
| | if (partOfBaseFeature && partOfBaseFeature != actPart) { |
| | |
| | QMessageBox::warning( |
| | Gui::getMainWindow(), |
| | QObject::tr("Bad base feature"), |
| | QObject::tr("Base feature (%1) belongs to other part.") |
| | .arg(QString::fromUtf8(baseFeature->Label.getValue())) |
| | ); |
| | baseFeature = nullptr; |
| | } |
| | else if (baseFeature->isDerivedFrom<Sketcher::SketchObject>()) { |
| | |
| | addtogroup = true; |
| | } |
| | |
| | |
| | else if (!baseFeature->isDerivedFrom<PartDesign::Feature>()) { |
| | const TopoDS_Shape& shape |
| | = static_cast<Part::Feature*>(baseFeature)->Shape.getValue(); |
| | if (!shape.IsNull()) { |
| | int numSolids = 0; |
| | int numShells = 0; |
| | for (TopExp_Explorer xp(shape, TopAbs_SOLID); xp.More(); xp.Next()) { |
| | numSolids++; |
| | } |
| | for (TopExp_Explorer xp(shape, TopAbs_SHELL, TopAbs_SOLID); xp.More(); |
| | xp.Next()) { |
| | numShells++; |
| | } |
| |
|
| | QString warning; |
| | if (numSolids > 1 && numShells == 0) { |
| | warning = QObject::tr( |
| | "The selected shape consists of multiple solids.\n" |
| | "This may lead to unexpected results." |
| | ); |
| | } |
| | else if (numShells > 1 && numSolids == 0) { |
| | warning = QObject::tr( |
| | "The selected shape consists of multiple shells.\n" |
| | "This may lead to unexpected results." |
| | ); |
| | } |
| | else if (numShells == 1 && numSolids == 0) { |
| | warning = QObject::tr( |
| | "The selected shape consists of only a shell.\n" |
| | "This may lead to unexpected results." |
| | ); |
| | } |
| | else if (numSolids + numShells > 1) { |
| | warning = QObject::tr( |
| | "The selected shape consists of multiple solids or shells.\n" |
| | "This may lead to unexpected results." |
| | ); |
| | } |
| |
|
| | if (!warning.isEmpty()) { |
| | QMessageBox::warning( |
| | Gui::getMainWindow(), |
| | QObject::tr("Base feature"), |
| | warning |
| | ); |
| | } |
| | } |
| | } |
| | } |
| | } |
| | else { |
| | QMessageBox::warning( |
| | Gui::getMainWindow(), |
| | QObject::tr("Bad base feature"), |
| | QObject::tr("Body may be based on no more than one feature.") |
| | ); |
| | return; |
| | } |
| | } |
| |
|
| | openCommand(QT_TRANSLATE_NOOP("Command", "Add a Body")); |
| |
|
| | std::string bodyName = getUniqueObjectName("Body"); |
| | const char* bodyString = bodyName.c_str(); |
| |
|
| | |
| | doCommand(Doc, "App.activeDocument().addObject('PartDesign::Body','%s')", bodyString); |
| | |
| | std::string labelString = QObject::tr("Body").toUtf8().toStdString(); |
| | labelString = Base::Tools::escapeEncodeString(labelString); |
| | doCommand(Doc, "App.ActiveDocument.getObject('%s').Label = '%s'", bodyString, labelString.c_str()); |
| | doCommand( |
| | Doc, |
| | "App.ActiveDocument.getObject('%s').AllowCompound = %s", |
| | bodyString, |
| | allowCompound ? "True" : "False" |
| | ); |
| | if (baseFeature) { |
| | if (partOfBaseFeature) { |
| | |
| | doCommand( |
| | Doc, |
| | "App.activeDocument().%s.removeObject(App.activeDocument().%s)", |
| | partOfBaseFeature->getNameInDocument(), |
| | baseFeature->getNameInDocument() |
| | ); |
| | } |
| | if (addtogroup) { |
| | doCommand( |
| | Doc, |
| | "App.activeDocument().%s.Group = [App.activeDocument().%s]", |
| | bodyString, |
| | baseFeature->getNameInDocument() |
| | ); |
| | } |
| | else { |
| | doCommand( |
| | Doc, |
| | "App.activeDocument().%s.BaseFeature = App.activeDocument().%s", |
| | bodyString, |
| | baseFeature->getNameInDocument() |
| | ); |
| | } |
| | } |
| | addModule(Gui, "PartDesignGui"); |
| |
|
| | if (actPart) { |
| | doCommand( |
| | Doc, |
| | "App.activeDocument().%s.addObject(App.ActiveDocument.%s)", |
| | actPart->getNameInDocument(), |
| | bodyString |
| | ); |
| | } |
| |
|
| | doCommand( |
| | Gui::Command::Gui, |
| | "Gui.activateView('Gui::View3DInventor', True)\n" |
| | "Gui.activeView().setActiveObject('%s', App.activeDocument().%s)", |
| | PDBODYKEY, |
| | bodyString |
| | ); |
| |
|
| | |
| | doCommand(Gui, "Gui.Selection.clearSelection()"); |
| | doCommand(Gui, "Gui.Selection.addSelection(App.ActiveDocument.%s)", bodyString); |
| |
|
| | |
| | if (baseFeature) { |
| | PartDesign::Body* body = dynamic_cast<PartDesign::Body*>( |
| | baseFeature->getDocument()->getObject(bodyString) |
| | ); |
| | if (body) { |
| | std::vector<App::DocumentObject*> links = body->Group.getValues(); |
| | for (auto it : links) { |
| | if (it->isDerivedFrom<PartDesign::FeatureBase>()) { |
| | PartDesign::FeatureBase* base = static_cast<PartDesign::FeatureBase*>(it); |
| | if (base && base->BaseFeature.getValue() == baseFeature) { |
| | Gui::Application::Instance->hideViewProvider(baseFeature); |
| | break; |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | if (addtogroup) { |
| | std::vector<App::DocumentObject*> planes; |
| | std::vector<PartDesignGui::TaskFeaturePick::featureStatus> status; |
| | unsigned validPlaneCount = 0; |
| | for (auto plane : body->getOrigin()->planes()) { |
| | planes.push_back(plane); |
| | status.push_back(PartDesignGui::TaskFeaturePick::basePlane); |
| | validPlaneCount++; |
| | } |
| |
|
| | if (validPlaneCount > 1) { |
| | |
| | auto accepter = [](const std::vector<App::DocumentObject*>& features) -> bool { |
| | return !features.empty(); |
| | }; |
| |
|
| | |
| | auto worker = [baseFeature](const std::vector<App::DocumentObject*>& features) { |
| | |
| | |
| | if (features.empty()) { |
| | return; |
| | } |
| | App::Plane* plane = static_cast<App::Plane*>(features.front()); |
| | std::string supportString = Gui::Command::getObjectCmd(plane, "(", ", [''])"); |
| |
|
| | FCMD_OBJ_CMD(baseFeature, "AttachmentSupport = " << supportString); |
| | FCMD_OBJ_CMD( |
| | baseFeature, |
| | "MapMode = '" |
| | << Attacher::AttachEngine::getModeName(Attacher::mmFlatFace) << "'" |
| | ); |
| | Gui::Command::updateActive(); |
| | }; |
| |
|
| | |
| | std::string docname = getDocument()->getName(); |
| | auto quitter = [docname]() { |
| | Gui::Document* document = Gui::Application::Instance->getDocument( |
| | docname.c_str() |
| | ); |
| | if (document) { |
| | document->abortCommand(); |
| | } |
| | }; |
| |
|
| | |
| | Gui::TaskView::TaskDialog* dlg = Gui::Control().activeDialog(); |
| | if (!dlg) { |
| | Gui::Selection().clearSelection(); |
| | Gui::Control().showDialog( |
| | new PartDesignGui::TaskDlgFeaturePick(planes, status, accepter, worker, true, quitter) |
| | ); |
| | } |
| | } |
| | } |
| | } |
| | } |
| |
|
| | updateActive(); |
| | } |
| |
|
| | bool CmdPartDesignBody::isActive() |
| | { |
| | return hasActiveDocument(); |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | DEF_STD_CMD_A(CmdPartDesignMigrate) |
| |
|
| | CmdPartDesignMigrate::CmdPartDesignMigrate() |
| | : Command("PartDesign_Migrate") |
| | { |
| | sAppModule = "PartDesign"; |
| | sGroup = QT_TR_NOOP("PartDesign"); |
| | sMenuText = QT_TR_NOOP("Migrate"); |
| | sToolTipText = QT_TR_NOOP("Migrates the document to the modern Part Design workflow"); |
| | sWhatsThis = "PartDesign_Migrate"; |
| | sStatusTip = sToolTipText; |
| | sPixmap = "PartDesign_Migrate"; |
| | } |
| |
|
| | void CmdPartDesignMigrate::activated(int iMsg) |
| | { |
| | Q_UNUSED(iMsg); |
| | App::Document* doc = getDocument(); |
| |
|
| | std::set<PartDesign::Feature*> migrateFeatures; |
| |
|
| |
|
| | |
| | for (const auto& feat : doc->getObjects()) { |
| | if (feat->isDerivedFrom(PartDesign::Feature::getClassTypeId()) |
| | && !PartDesign::Body::findBodyOf(feat) && PartDesign::Body::isSolidFeature(feat)) { |
| | migrateFeatures.insert(static_cast<PartDesign::Feature*>(feat)); |
| | } |
| | } |
| |
|
| | if (migrateFeatures.empty()) { |
| | if (!PartDesignGui::isModernWorkflow(doc)) { |
| | |
| | PartDesignGui::WorkflowManager::instance()->forceWorkflow( |
| | doc, |
| | PartDesignGui::Workflow::Modern |
| | ); |
| | } |
| | else { |
| | |
| | QMessageBox::warning( |
| | nullptr, |
| | QObject::tr("Nothing to migrate"), |
| | QObject::tr( |
| | "No Part Design features without body found" |
| | " Nothing to migrate." |
| | ) |
| | ); |
| | } |
| | return; |
| | } |
| |
|
| | |
| | PartDesignGui::WorkflowManager::instance()->forceWorkflow(doc, PartDesignGui::Workflow::Modern); |
| |
|
| | |
| | std::list<std::list<PartDesign::Feature*>> featureChains; |
| | std::list<PartDesign::Feature*> chain; |
| |
|
| | for (auto featIt = migrateFeatures.begin(); !migrateFeatures.empty();) { |
| | Part::Feature* base = (*featIt)->getBaseObject(true); |
| |
|
| | chain.push_front(*featIt); |
| |
|
| | if (!base || !base->isDerivedFrom(PartDesign::Feature::getClassTypeId()) |
| | || PartDesignGui::isAnyNonPartDesignLinksTo( |
| | static_cast<PartDesign::Feature*>(base), |
| | true |
| | )) { |
| | |
| | auto newChainIt = featureChains.emplace(featureChains.end()); |
| | newChainIt->splice(newChainIt->end(), chain); |
| | } |
| | else { |
| | |
| | PartDesign::Feature* baseFeat = static_cast<PartDesign::Feature*>(base); |
| |
|
| | auto baseFeatSetIt = migrateFeatures.find(baseFeat); |
| |
|
| | if (baseFeatSetIt != migrateFeatures.end()) { |
| | |
| | migrateFeatures.erase(featIt); |
| | featIt = baseFeatSetIt; |
| | continue; |
| | } |
| | else { |
| | |
| | std::list<PartDesign::Feature*>::iterator baseFeatIt; |
| | auto isChain = |
| | [baseFeat, &baseFeatIt](std::list<PartDesign::Feature*>& fchain) mutable -> bool { |
| | baseFeatIt = std::ranges::find(fchain, baseFeat); |
| | return baseFeatIt != fchain.end(); |
| | }; |
| |
|
| | if (auto chainIt = std::ranges::find_if(featureChains, isChain); |
| | chainIt != featureChains.end()) { |
| | assert(baseFeatIt != chainIt->end()); |
| | if (std::next(baseFeatIt) == chainIt->end()) { |
| | |
| | chainIt->splice(chainIt->end(), chain); |
| | |
| | |
| | |
| | } |
| | else { |
| | |
| | |
| | auto newChainIt = featureChains.emplace(featureChains.end()); |
| | newChainIt->splice(newChainIt->end(), chain); |
| | |
| | newChainIt = featureChains.emplace(featureChains.end()); |
| | newChainIt->splice( |
| | newChainIt->end(), |
| | *chainIt, |
| | std::next(baseFeatIt), |
| | chainIt->end() |
| | ); |
| | } |
| | } |
| | else { |
| | |
| | |
| | |
| | |
| | auto newChainIt = featureChains.emplace(featureChains.end()); |
| | newChainIt->splice(newChainIt->end(), chain); |
| | } |
| | } |
| | } |
| | migrateFeatures.erase(featIt); |
| | featIt = migrateFeatures.begin(); |
| | |
| | } |
| |
|
| | |
| | |
| | App::Part* actPart = PartDesignGui::assertActivePart(); |
| |
|
| | if (!actPart) { |
| | return; |
| | } |
| |
|
| | |
| | Gui::Command::openCommand( |
| | QT_TRANSLATE_NOOP("Command", "Migrate legacy Part Design features to bodies") |
| | ); |
| |
|
| | for (auto chainIt = featureChains.begin(); !featureChains.empty(); |
| | featureChains.erase(chainIt), chainIt = featureChains.begin()) { |
| | #ifndef FC_DEBUG |
| | if (chainIt->empty()) { |
| | continue; |
| | } |
| | #else |
| | assert(!chainIt->empty()); |
| | #endif |
| | Part::Feature* base = chainIt->front()->getBaseObject(true); |
| |
|
| | |
| | for (; chainIt != featureChains.end(); chainIt++) { |
| | base = chainIt->front()->getBaseObject(true); |
| | if (!base || !base->isDerivedFrom(PartDesign::Feature::getClassTypeId())) { |
| | break; |
| | } |
| | else { |
| | |
| | base = PartDesign::Body::findBodyOf(base); |
| | if (base) { |
| | break; |
| | } |
| | } |
| | } |
| |
|
| | if (chainIt == featureChains.end()) { |
| | |
| | |
| | chainIt = featureChains.begin(); |
| | base = chainIt->front()->getBaseObject(true); |
| | } |
| |
|
| | |
| | std::string bodyName = getUniqueObjectName( |
| | std::string(chainIt->back()->getNameInDocument()).append("Body").c_str() |
| | ); |
| |
|
| | Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter().GetGroup( |
| | "BaseApp/Preferences/Mod/PartDesign" |
| | ); |
| |
|
| | bool allowCompound = hGrp->GetBool("AllowCompoundDefault", true); |
| |
|
| | |
| | doCommand(Doc, "App.activeDocument().addObject('PartDesign::Body','%s')", bodyName.c_str()); |
| | doCommand( |
| | Doc, |
| | "App.ActiveDocument.getObject('%s').AllowCompound = %s", |
| | bodyName.c_str(), |
| | allowCompound ? "True" : "False" |
| | ); |
| | doCommand( |
| | Doc, |
| | "App.activeDocument().%s.addObject(App.ActiveDocument.%s)", |
| | actPart->getNameInDocument(), |
| | bodyName.c_str() |
| | ); |
| | if (base) { |
| | doCommand( |
| | Doc, |
| | "App.activeDocument().%s.BaseFeature = App.activeDocument().%s", |
| | bodyName.c_str(), |
| | base->getNameInDocument() |
| | ); |
| | } |
| |
|
| | |
| | for (auto feature : *chainIt) { |
| | if (feature->isDerivedFrom(PartDesign::ProfileBased::getClassTypeId())) { |
| | |
| | PartDesign::ProfileBased* sketchBased = static_cast<PartDesign::ProfileBased*>(feature); |
| | Part::Part2DObject* sketch = sketchBased->getVerifiedSketch(true); |
| | if (sketch) { |
| | doCommand( |
| | Doc, |
| | "App.activeDocument().%s.addObject(App.activeDocument().%s)", |
| | bodyName.c_str(), |
| | sketch->getNameInDocument() |
| | ); |
| |
|
| | if (sketch->isDerivedFrom(Sketcher::SketchObject::getClassTypeId())) { |
| | try { |
| | PartDesignGui::fixSketchSupport( |
| | static_cast<Sketcher::SketchObject*>(sketch) |
| | ); |
| | } |
| | catch (Base::Exception&) { |
| | QMessageBox::critical( |
| | Gui::getMainWindow(), |
| | QObject::tr("Sketch plane cannot be migrated"), |
| | QObject::tr( |
| | "Please edit '%1' and redefine it to use a Base or " |
| | "Datum plane as the sketch plane." |
| | ) |
| | .arg(QString::fromUtf8(sketch->Label.getValue())) |
| | ); |
| | } |
| | } |
| | else { |
| | |
| | } |
| | } |
| | } |
| | doCommand( |
| | Doc, |
| | "App.activeDocument().%s.addObject(App.activeDocument().%s)", |
| | bodyName.c_str(), |
| | feature->getNameInDocument() |
| | ); |
| |
|
| | PartDesignGui::relinkToBody(feature); |
| | } |
| | } |
| |
|
| | updateActive(); |
| | } |
| |
|
| | bool CmdPartDesignMigrate::isActive() |
| | { |
| | return hasActiveDocument(); |
| | } |
| |
|
| | |
| | |
| | |
| | DEF_STD_CMD_A(CmdPartDesignMoveTip) |
| |
|
| | CmdPartDesignMoveTip::CmdPartDesignMoveTip() |
| | : Command("PartDesign_MoveTip") |
| | { |
| | sAppModule = "PartDesign"; |
| | sGroup = QT_TR_NOOP("PartDesign"); |
| | sMenuText = QT_TR_NOOP("Set Tip"); |
| | sToolTipText = QT_TR_NOOP("Moves the tip of the body to the selected feature"); |
| | sWhatsThis = "PartDesign_MoveTip"; |
| | sStatusTip = sToolTipText; |
| | sPixmap = "PartDesign_MoveTip"; |
| | } |
| |
|
| | void CmdPartDesignMoveTip::activated(int iMsg) |
| | { |
| | Q_UNUSED(iMsg); |
| | std::vector<App::DocumentObject*> features = getSelection().getObjectsOfType( |
| | Part::Feature::getClassTypeId() |
| | ); |
| | App::DocumentObject* selFeature; |
| | PartDesign::Body* body = nullptr; |
| |
|
| | if (features.size() == 1) { |
| | selFeature = features.front(); |
| | if (selFeature->isDerivedFrom<PartDesign::Body>()) { |
| | body = static_cast<PartDesign::Body*>(selFeature); |
| | } |
| | else { |
| | body = PartDesignGui::getBodyFor(selFeature, false); |
| | } |
| | } |
| | else { |
| | selFeature = nullptr; |
| | } |
| |
|
| | if (!selFeature) { |
| | QMessageBox::warning( |
| | nullptr, |
| | QObject::tr("Selection error"), |
| | QObject::tr("Select exactly one Part Design feature or a body.") |
| | ); |
| | return; |
| | } |
| | else if (!body) { |
| | QMessageBox::warning( |
| | nullptr, |
| | QObject::tr("Selection error"), |
| | QObject::tr( |
| | "Could not determine a body for the selected feature '%s'.", |
| | selFeature->Label.getValue() |
| | ) |
| | ); |
| | return; |
| | } |
| | else if (!selFeature->isDerivedFrom(PartDesign::Feature::getClassTypeId()) && selFeature != body |
| | && body->BaseFeature.getValue() != selFeature) { |
| | QMessageBox::warning( |
| | nullptr, |
| | QObject::tr("Selection error"), |
| | QObject::tr("Only a solid feature can be the tip of a body.") |
| | ); |
| | return; |
| | } |
| |
|
| | App::DocumentObject* oldTip = body->Tip.getValue(); |
| | if (oldTip == selFeature) { |
| | Base::Console().message("%s is already the tip of the body\n", selFeature->getNameInDocument()); |
| | return; |
| | } |
| |
|
| | openCommand(QT_TRANSLATE_NOOP("Command", "Move tip to selected feature")); |
| |
|
| | if (selFeature == body) { |
| | FCMD_OBJ_CMD(body, "Tip = None"); |
| | } |
| | else { |
| | FCMD_OBJ_CMD(body, "Tip = " << getObjectCmd(selFeature)); |
| |
|
| | |
| | FCMD_OBJ_SHOW(selFeature); |
| | } |
| |
|
| | |
| | |
| | updateActive(); |
| | } |
| |
|
| | bool CmdPartDesignMoveTip::isActive() |
| | { |
| | return hasActiveDocument(); |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | DEF_STD_CMD_A(CmdPartDesignDuplicateSelection) |
| |
|
| | CmdPartDesignDuplicateSelection::CmdPartDesignDuplicateSelection() |
| | : Command("PartDesign_DuplicateSelection") |
| | { |
| | sAppModule = "PartDesign"; |
| | sGroup = QT_TR_NOOP("PartDesign"); |
| | sMenuText = QT_TR_NOOP("Duplicate &Object"); |
| | sToolTipText = QT_TR_NOOP("Duplicates the selected object and adds it to the active body"); |
| | sWhatsThis = "PartDesign_DuplicateSelection"; |
| | sStatusTip = sToolTipText; |
| | } |
| |
|
| | void CmdPartDesignDuplicateSelection::activated(int iMsg) |
| | { |
| | Q_UNUSED(iMsg); |
| | PartDesign::Body* pcActiveBody = PartDesignGui::getBody( false); |
| |
|
| | std::vector<App::DocumentObject*> beforeFeatures = getDocument()->getObjects(); |
| |
|
| | openCommand(QT_TRANSLATE_NOOP("Command", "Duplicate a Part Design object")); |
| | doCommand(Doc, "FreeCADGui.runCommand('Std_DuplicateSelection')"); |
| |
|
| | if (pcActiveBody) { |
| | |
| | std::vector<App::DocumentObject*> afterFeatures = getDocument()->getObjects(); |
| | std::vector<App::DocumentObject*> newFeatures; |
| | std::sort(beforeFeatures.begin(), beforeFeatures.end()); |
| | std::sort(afterFeatures.begin(), afterFeatures.end()); |
| | std::set_difference( |
| | afterFeatures.begin(), |
| | afterFeatures.end(), |
| | beforeFeatures.begin(), |
| | beforeFeatures.end(), |
| | std::back_inserter(newFeatures) |
| | ); |
| |
|
| | for (auto feature : newFeatures) { |
| | if (PartDesign::Body::isAllowed(feature)) { |
| | |
| | auto body = App::GeoFeatureGroupExtension::getGroupOfObject(feature); |
| | if (!body) { |
| | FCMD_OBJ_CMD(pcActiveBody, "addObject(" << getObjectCmd(feature) << ")"); |
| | FCMD_OBJ_HIDE(feature); |
| | } |
| | } |
| | } |
| |
|
| | |
| | if (!newFeatures.empty()) { |
| | FCMD_OBJ_SHOW(newFeatures.back()); |
| | } |
| | } |
| |
|
| | updateActive(); |
| | } |
| |
|
| | bool CmdPartDesignDuplicateSelection::isActive() |
| | { |
| | return hasActiveDocument(); |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | DEF_STD_CMD_A(CmdPartDesignMoveFeature) |
| |
|
| | CmdPartDesignMoveFeature::CmdPartDesignMoveFeature() |
| | : Command("PartDesign_MoveFeature") |
| | { |
| | sAppModule = "PartDesign"; |
| | sGroup = QT_TR_NOOP("PartDesign"); |
| | sMenuText = QT_TR_NOOP("Move Object To…"); |
| | sToolTipText = QT_TR_NOOP("Moves the selected object to another body"); |
| | sWhatsThis = "PartDesign_MoveFeature"; |
| | sStatusTip = sToolTipText; |
| | sPixmap = "PartDesign_MoveFeature"; |
| | } |
| |
|
| | void CmdPartDesignMoveFeature::activated(int iMsg) |
| | { |
| | Q_UNUSED(iMsg); |
| | std::vector<App::DocumentObject*> features = getSelection().getObjectsOfType( |
| | Part::Feature::getClassTypeId() |
| | ); |
| | if (features.empty()) { |
| | return; |
| | } |
| |
|
| | |
| | if (std::any_of(std::begin(features), std::end(features), [](App::DocumentObject* obj) { |
| | return !PartDesignGui::isFeatureMovable(obj); |
| | })) { |
| | |
| | QMessageBox::warning( |
| | Gui::getMainWindow(), |
| | QObject::tr("Features cannot be moved"), |
| | QObject::tr("Some of the selected features have dependencies in the source body") |
| | ); |
| | return; |
| | } |
| |
|
| | |
| | std::vector<App::DocumentObject*> dependencies = PartDesignGui::collectMovableDependencies( |
| | features |
| | ); |
| | if (!dependencies.empty()) { |
| | features.insert(std::end(features), std::begin(dependencies), std::end(dependencies)); |
| | } |
| |
|
| | |
| | std::vector<App::DocumentObject*> bodies = getDocument()->getObjectsOfType( |
| | Part::BodyBase::getClassTypeId() |
| | ); |
| |
|
| | std::set<App::DocumentObject*> source_bodies; |
| | for (auto feat : features) { |
| | |
| | PartDesign::Body* source = PartDesign::Body::findBodyOf(feat); |
| | source_bodies.insert(static_cast<App::DocumentObject*>(source)); |
| | } |
| |
|
| | if (source_bodies.size() != 1) { |
| | |
| | QMessageBox::warning( |
| | Gui::getMainWindow(), |
| | QObject::tr("Features cannot be moved"), |
| | QObject::tr("Only features of a single source body can be moved") |
| | ); |
| | return; |
| | } |
| |
|
| | auto source_body = *source_bodies.begin(); |
| |
|
| | std::vector<App::DocumentObject*> target_bodies; |
| | for (auto body : bodies) { |
| | if (!source_bodies.count(body)) { |
| | target_bodies.push_back(body); |
| | } |
| | } |
| |
|
| | if (target_bodies.empty()) { |
| | QMessageBox::warning( |
| | Gui::getMainWindow(), |
| | QObject::tr("Features cannot be moved"), |
| | QObject::tr("There are no other bodies to move to") |
| | ); |
| | return; |
| | } |
| |
|
| | |
| | bool ok; |
| | QStringList items; |
| | for (auto body : target_bodies) { |
| | items.push_back(QString::fromUtf8(body->Label.getValue())); |
| | } |
| | QString text = QInputDialog::getItem( |
| | Gui::getMainWindow(), |
| | qApp->translate("PartDesign_MoveFeature", "Select Body"), |
| | qApp->translate("PartDesign_MoveFeature", "Select a body from the list"), |
| | items, |
| | 0, |
| | false, |
| | &ok, |
| | Qt::MSWindowsFixedSizeDialogHint |
| | ); |
| | if (!ok) { |
| | return; |
| | } |
| | int index = items.indexOf(text); |
| | if (index < 0) { |
| | return; |
| | } |
| |
|
| | PartDesign::Body* target = static_cast<PartDesign::Body*>(target_bodies[index]); |
| |
|
| | openCommand(QT_TRANSLATE_NOOP("Command", "Move an object")); |
| |
|
| | std::stringstream stream; |
| | stream << "features_ = [" << getObjectCmd(features.back()); |
| | features.pop_back(); |
| |
|
| | for (auto feat : features) { |
| | stream << ", " << getObjectCmd(feat); |
| | } |
| |
|
| | stream << "]"; |
| | runCommand(Doc, stream.str().c_str()); |
| | FCMD_OBJ_CMD(source_body, "removeObjects(features_)"); |
| | FCMD_OBJ_CMD(target, "addObjects(features_)"); |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | updateActive(); |
| | } |
| |
|
| | bool CmdPartDesignMoveFeature::isActive() |
| | { |
| | return hasActiveDocument(); |
| | } |
| |
|
| | DEF_STD_CMD_A(CmdPartDesignMoveFeatureInTree) |
| |
|
| | CmdPartDesignMoveFeatureInTree::CmdPartDesignMoveFeatureInTree() |
| | : Command("PartDesign_MoveFeatureInTree") |
| | { |
| | sAppModule = "PartDesign"; |
| | sGroup = QT_TR_NOOP("PartDesign"); |
| | sMenuText = QT_TR_NOOP("Move Feature After…"); |
| | sToolTipText = QT_TR_NOOP("Moves the selected feature after another feature in the same body"); |
| | sWhatsThis = "PartDesign_MoveFeatureInTree"; |
| | sStatusTip = sToolTipText; |
| | sPixmap = "PartDesign_MoveFeatureInTree"; |
| | } |
| |
|
| | void CmdPartDesignMoveFeatureInTree::activated(int iMsg) |
| | { |
| | Q_UNUSED(iMsg); |
| | std::vector<App::DocumentObject*> features = getSelection().getObjectsOfType( |
| | Part::Feature::getClassTypeId() |
| | ); |
| |
|
| | |
| | std::vector<App::DocumentObject*> datums = getSelection().getObjectsOfType( |
| | App::DatumElement::getClassTypeId() |
| | ); |
| | features.insert(features.end(), datums.begin(), datums.end()); |
| |
|
| | if (features.empty()) { |
| | return; |
| | } |
| |
|
| | PartDesign::Body* body = PartDesignGui::getBodyFor(features.front(), false); |
| | App::DocumentObject* bodyBase = nullptr; |
| | |
| | bool allFeaturesFromSameBody = true; |
| |
|
| | if (body) { |
| | bodyBase = body->BaseFeature.getValue(); |
| | for (auto feat : features) { |
| | if (!body->hasObject(feat)) { |
| | allFeaturesFromSameBody = false; |
| | break; |
| | } |
| | if (bodyBase == feat) { |
| | QMessageBox::warning( |
| | nullptr, |
| | QObject::tr("Selection error"), |
| | QObject::tr("Impossible to move the base feature of a body.") |
| | ); |
| | return; |
| | } |
| | } |
| | } |
| | if (!body || !allFeaturesFromSameBody) { |
| | QMessageBox::warning( |
| | nullptr, |
| | QObject::tr("Selection error"), |
| | QObject::tr("Select one or more features from the same body.") |
| | ); |
| | return; |
| | } |
| |
|
| | |
| | const std::vector<App::DocumentObject*>& model = body->Group.getValues(); |
| |
|
| | |
| | bool ok; |
| | QStringList items; |
| | if (bodyBase) { |
| | items.push_back(QString::fromUtf8(bodyBase->Label.getValue())); |
| | } |
| | else { |
| | items.push_back(QObject::tr("Beginning of the body")); |
| | } |
| | for (auto feat : model) { |
| | items.push_back(QString::fromUtf8(feat->Label.getValue())); |
| | } |
| |
|
| | QString text = QInputDialog::getItem( |
| | Gui::getMainWindow(), |
| | qApp->translate("PartDesign_MoveFeatureInTree", "Move Feature After…"), |
| | qApp->translate("PartDesign_MoveFeatureInTree", "Select a feature from the list"), |
| | items, |
| | 0, |
| | false, |
| | &ok, |
| | Qt::MSWindowsFixedSizeDialogHint |
| | ); |
| | if (!ok) { |
| | return; |
| | } |
| | int index = items.indexOf(text); |
| | |
| | App::DocumentObject* target = index != 0 ? model[index - 1] : nullptr; |
| |
|
| | openCommand(QT_TRANSLATE_NOOP("Command", "Move a feature inside body")); |
| |
|
| | App::DocumentObject* lastObject = target; |
| | for (auto feat : features) { |
| | if (feat == target) { |
| | continue; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | FCMD_OBJ_CMD(body, "removeObject(" << getObjectCmd(feat) << ")"); |
| | FCMD_OBJ_CMD( |
| | body, |
| | "insertObject(" << getObjectCmd(feat) << "," << getObjectCmd(lastObject) << ", True)" |
| | ); |
| |
|
| | lastObject = feat; |
| | } |
| |
|
| | |
| | |
| | |
| | std::vector<App::DocumentObject*> bodyFeatures; |
| | std::map<App::DocumentObject*, size_t> orders; |
| | for (auto obj : body->Group.getValues()) { |
| | if (obj->isDerivedFrom<PartDesign::Feature>()) { |
| | orders.emplace(obj, bodyFeatures.size()); |
| | bodyFeatures.push_back(obj); |
| | } |
| | } |
| | bool failed = false; |
| | std::ostringstream ss; |
| | for (size_t i = 0; i < bodyFeatures.size(); ++i) { |
| | auto feat = bodyFeatures[i]; |
| | for (auto obj : feat->getOutList()) { |
| | if (obj->isDerivedFrom<PartDesign::Feature>()) { |
| | continue; |
| | } |
| | for (auto dep : App::Document::getDependencyList({obj})) { |
| | auto it = orders.find(dep); |
| | if (it != orders.end() && it->second > i) { |
| | ss << feat->Label.getValue() << ", " << obj->Label.getValue() << " -> " |
| | << it->first->Label.getValue(); |
| | if (!failed) { |
| | failed = true; |
| | } |
| | else { |
| | ss << std::endl; |
| | } |
| | } |
| | } |
| | } |
| | } |
| | if (failed) { |
| | QMessageBox::critical( |
| | nullptr, |
| | QObject::tr("Dependency violation"), |
| | QObject::tr("Early feature must not depend on later feature.\n\n") |
| | + QString::fromUtf8(ss.str().c_str()) |
| | ); |
| | abortCommand(); |
| | return; |
| | } |
| |
|
| | |
| | |
| | |
| | if (lastObject != target && body->Tip.getValue() == target |
| | && lastObject->isDerivedFrom<PartDesign::Feature>()) { |
| | QMessageBox msgBox(Gui::getMainWindow()); |
| | msgBox.setIcon(QMessageBox::Question); |
| | msgBox.setWindowTitle(qApp->translate("PartDesign_MoveFeatureInTree", "Move Tip")); |
| | msgBox.setText(qApp->translate( |
| | "PartDesign_MoveFeatureInTree", |
| | "The moved feature appears after the currently set tip." |
| | )); |
| | msgBox.setInformativeText( |
| | qApp->translate("PartDesign_MoveFeatureInTree", "Set tip to last feature?") |
| | ); |
| | msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); |
| | msgBox.setDefaultButton(QMessageBox::No); |
| | int ret = msgBox.exec(); |
| | if (ret == QMessageBox::Yes) { |
| | FCMD_OBJ_CMD(body, "Tip = " << getObjectCmd(lastObject)); |
| | } |
| | } |
| |
|
| | updateActive(); |
| | } |
| |
|
| | bool CmdPartDesignMoveFeatureInTree::isActive() |
| | { |
| | return hasActiveDocument(); |
| | } |
| |
|
| |
|
| | |
| | |
| | |
| |
|
| | void CreatePartDesignBodyCommands() |
| | { |
| | Gui::CommandManager& rcCmdMgr = Gui::Application::Instance->commandManager(); |
| |
|
| | rcCmdMgr.addCommand(new CmdPartDesignBody()); |
| | rcCmdMgr.addCommand(new CmdPartDesignMigrate()); |
| | rcCmdMgr.addCommand(new CmdPartDesignMoveTip()); |
| |
|
| | rcCmdMgr.addCommand(new CmdPartDesignDuplicateSelection()); |
| | rcCmdMgr.addCommand(new CmdPartDesignMoveFeature()); |
| | rcCmdMgr.addCommand(new CmdPartDesignMoveFeatureInTree()); |
| | } |
| |
|