| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| #include <QListIterator> |
| #include <QListWidgetItem> |
| #include <QTimer> |
|
|
|
|
| #include <ranges> |
|
|
| #include <fmt/format.h> |
|
|
| #include <App/Document.h> |
| #include <App/Origin.h> |
| #include <App/Datums.h> |
| #include <App/Part.h> |
| #include <Base/Console.h> |
| #include <Gui/Application.h> |
| #include <Gui/BitmapFactory.h> |
| #include <Gui/Control.h> |
| #include <Gui/ViewProviderCoordinateSystem.h> |
| #include <Mod/PartDesign/App/Body.h> |
| #include <Mod/PartDesign/App/ShapeBinder.h> |
| #include <Mod/PartDesign/App/DatumLine.h> |
| #include <Mod/PartDesign/App/DatumPlane.h> |
| #include <Mod/PartDesign/App/DatumPoint.h> |
| #include <Mod/PartDesign/App/FeaturePrimitive.h> |
| #include <Mod/Sketcher/App/SketchObject.h> |
|
|
| #include "ui_TaskFeaturePick.h" |
| #include "TaskFeaturePick.h" |
| #include "Utils.h" |
|
|
| #include <Gui/ViewParams.h> |
|
|
|
|
| using namespace PartDesignGui; |
| using namespace Attacher; |
|
|
| |
| |
| const QString TaskFeaturePick::getFeatureStatusString(const featureStatus st) |
| { |
| switch (st) { |
| case validFeature: |
| return tr("Valid"); |
| case invalidShape: |
| return tr("Invalid shape"); |
| case noWire: |
| return tr("No wire in sketch"); |
| case isUsed: |
| return tr("Sketch already used by other feature"); |
| case otherBody: |
| return tr("Belongs to another body"); |
| case otherPart: |
| return tr("Belongs to another part"); |
| case notInBody: |
| return tr("Doesn't belong to any body"); |
| case basePlane: |
| return tr("Base plane"); |
| case afterTip: |
| return tr("Feature is located after the tip of the body"); |
| } |
|
|
| return QString(); |
| } |
|
|
| TaskFeaturePick::TaskFeaturePick( |
| std::vector<App::DocumentObject*>& objects, |
| const std::vector<featureStatus>& status, |
| bool singleFeatureSelect, |
| QWidget* parent |
| ) |
| : TaskBox(Gui::BitmapFactory().pixmap("edit-select-all"), tr("Select Attachment"), true, parent) |
| , ui(new Ui_TaskFeaturePick) |
| , doSelection(false) |
| { |
|
|
| proxy = new QWidget(this); |
| ui->setupUi(proxy); |
|
|
| |
| connect(ui->checkUsed, &QCheckBox::toggled, this, &TaskFeaturePick::onUpdate); |
| connect(ui->checkOtherBody, &QCheckBox::toggled, this, &TaskFeaturePick::onUpdate); |
| connect(ui->checkOtherPart, &QCheckBox::toggled, this, &TaskFeaturePick::onUpdate); |
| connect(ui->radioIndependent, &QRadioButton::toggled, this, &TaskFeaturePick::onUpdate); |
| connect(ui->radioDependent, &QRadioButton::toggled, this, &TaskFeaturePick::onUpdate); |
| connect(ui->radioXRef, &QRadioButton::toggled, this, &TaskFeaturePick::onUpdate); |
| connect(ui->listWidget, &QListWidget::itemSelectionChanged, this, &TaskFeaturePick::onItemSelectionChanged); |
| connect(ui->listWidget, &QListWidget::itemDoubleClicked, this, &TaskFeaturePick::onDoubleClick); |
| |
|
|
|
|
| if (!singleFeatureSelect) { |
| ui->listWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); |
| } |
|
|
| |
| std::map<App::Origin*, Gui::DatumElements> originVisStatus; |
|
|
| auto statusIt = status.cbegin(); |
| auto objIt = objects.begin(); |
| assert(status.size() == objects.size()); |
|
|
| bool attached = false; |
| for (; statusIt != status.end(); ++statusIt, ++objIt) { |
| QListWidgetItem* item = new QListWidgetItem(QStringLiteral("%1 (%2)").arg( |
| QString::fromUtf8((*objIt)->Label.getValue()), |
| getFeatureStatusString(*statusIt) |
| )); |
| item->setData(Qt::UserRole, QString::fromLatin1((*objIt)->getNameInDocument())); |
| ui->listWidget->addItem(item); |
|
|
| App::Document* pDoc = (*objIt)->getDocument(); |
| documentName = pDoc->getName(); |
| if (!attached) { |
| attached = true; |
| attachDocument(Gui::Application::Instance->getDocument(pDoc)); |
| } |
|
|
| |
| auto* datum = dynamic_cast<App::DatumElement*>(*objIt); |
| if ((*statusIt == validFeature || *statusIt == basePlane) && datum) { |
| auto* origin = dynamic_cast<App::Origin*>(datum->getLCS()); |
| if (origin) { |
| if ((*objIt)->isDerivedFrom<App::Plane>()) { |
| originVisStatus[origin].setFlag(Gui::DatumElement::Planes, true); |
| } |
| else if ((*objIt)->isDerivedFrom<App::Line>()) { |
| originVisStatus[origin].setFlag(Gui::DatumElement::Axes, true); |
| } |
| } |
| } |
| } |
|
|
| |
| for (const auto& originPair : originVisStatus) { |
| const auto& origin = originPair.first; |
|
|
| auto* vpo = static_cast<Gui::ViewProviderCoordinateSystem*>( |
| Gui::Application::Instance->getViewProvider(origin) |
| ); |
| if (vpo) { |
| vpo->setTemporaryVisibility(originVisStatus[origin]); |
| vpo->setTemporaryScale(Gui::ViewParams::instance()->getDatumTemporaryScaleFactor()); |
| vpo->setPlaneLabelVisibility(true); |
| origins.push_back(vpo); |
| } |
| } |
|
|
| |
|
|
| groupLayout()->addWidget(proxy); |
| statuses = status; |
| updateList(); |
| } |
|
|
| TaskFeaturePick::~TaskFeaturePick() |
| { |
| for (Gui::ViewProviderCoordinateSystem* vpo : origins) { |
| vpo->resetTemporaryVisibility(); |
| vpo->resetTemporarySize(); |
| vpo->setPlaneLabelVisibility(false); |
| } |
| } |
|
|
| void TaskFeaturePick::updateList() |
| { |
| int index = 0; |
|
|
| for (auto status : statuses) { |
| QListWidgetItem* item = ui->listWidget->item(index); |
|
|
| switch (status) { |
| case validFeature: |
| item->setHidden(false); |
| break; |
| case invalidShape: |
| item->setHidden(true); |
| break; |
| case isUsed: |
| item->setHidden(!ui->checkUsed->isChecked()); |
| break; |
| case noWire: |
| item->setHidden(true); |
| break; |
| case otherBody: |
| item->setHidden(!ui->checkOtherBody->isChecked()); |
| break; |
| case otherPart: |
| item->setHidden(!ui->checkOtherPart->isChecked()); |
| break; |
| case notInBody: |
| item->setHidden(!ui->checkOtherPart->isChecked()); |
| break; |
| case basePlane: |
| item->setHidden(false); |
| break; |
| case afterTip: |
| item->setHidden(true); |
| break; |
| } |
|
|
| index++; |
| } |
| } |
|
|
| void TaskFeaturePick::onUpdate(bool) |
| { |
| bool enable = false; |
| if (ui->checkOtherBody->isChecked() || ui->checkOtherPart->isChecked()) { |
| enable = true; |
| } |
|
|
| ui->radioDependent->setEnabled(enable); |
| ui->radioIndependent->setEnabled(enable); |
| ui->radioXRef->setEnabled(enable); |
|
|
| updateList(); |
| } |
|
|
| std::vector<App::DocumentObject*> TaskFeaturePick::getFeatures() |
| { |
| features.clear(); |
| QListIterator<QListWidgetItem*> i(ui->listWidget->selectedItems()); |
| while (i.hasNext()) { |
|
|
| auto item = i.next(); |
| if (item->isHidden()) { |
| continue; |
| } |
|
|
| QString t = item->data(Qt::UserRole).toString(); |
| features.push_back(t); |
| } |
|
|
| std::vector<App::DocumentObject*> result; |
|
|
| for (const auto& feature : features) { |
| result.push_back( |
| App::GetApplication().getDocument(documentName.c_str())->getObject(feature.toLatin1().data()) |
| ); |
| } |
|
|
| return result; |
| } |
|
|
| std::vector<App::DocumentObject*> TaskFeaturePick::buildFeatures() |
| { |
| int index = 0; |
| std::vector<App::DocumentObject*> result; |
| try { |
| auto activeBody = PartDesignGui::getBody(false); |
| if (!activeBody) { |
| return result; |
| } |
|
|
| auto activePart = PartDesignGui::getPartFor(activeBody, false); |
|
|
| for (auto status : statuses) { |
| QListWidgetItem* item = ui->listWidget->item(index); |
|
|
| if (item->isSelected() && !item->isHidden()) { |
| QString t = item->data(Qt::UserRole).toString(); |
| auto obj = App::GetApplication() |
| .getDocument(documentName.c_str()) |
| ->getObject(t.toLatin1().data()); |
|
|
| |
| if (status == otherBody || status == otherPart || status == notInBody) { |
| if (!ui->radioXRef->isChecked()) { |
| auto copy = makeCopy(obj, "", ui->radioIndependent->isChecked()); |
|
|
| if (status == otherBody) { |
| activeBody->addObject(copy); |
| } |
| else if (status == otherPart) { |
| auto oBody = PartDesignGui::getBodyFor(obj, false); |
| if (!oBody) { |
| activePart->addObject(copy); |
| } |
| else { |
| activeBody->addObject(copy); |
| } |
| } |
| else if (status == notInBody) { |
| activeBody->addObject(copy); |
| |
| |
| if (copy->isDerivedFrom<Sketcher::SketchObject>()) { |
| Sketcher::SketchObject* sketch |
| = static_cast<Sketcher::SketchObject*>(copy); |
| PartDesignGui::fixSketchSupport(sketch); |
| } |
| } |
| result.push_back(copy); |
| } |
| else { |
| result.push_back(obj); |
| } |
| } |
| else { |
| result.push_back(obj); |
| } |
| } |
|
|
| index++; |
| } |
| } |
| catch (const Base::Exception& e) { |
| e.reportException(); |
| } |
| catch (Py::Exception& e) { |
| |
| e.clear(); |
| Base::Console().warning("Unexpected PyCXX exception\n"); |
| } |
| catch (const boost::exception&) { |
| |
| Base::Console().warning("Unexpected boost exception\n"); |
| } |
|
|
| return result; |
| } |
|
|
| App::DocumentObject* TaskFeaturePick::makeCopy(App::DocumentObject* obj, std::string sub, bool independent) |
| { |
|
|
| App::DocumentObject* copy = nullptr; |
| |
| if (!obj) { |
| return copy; |
| } |
| if (independent |
| && (obj->isDerivedFrom<Sketcher::SketchObject>() |
| || obj->isDerivedFrom<PartDesign::FeaturePrimitive>())) { |
|
|
| |
| |
| auto* doc = App::GetApplication().getActiveDocument(); |
| const auto name = fmt::format("Copy{}", obj->getNameInDocument()); |
| copy = doc->addObject(obj->getTypeId().getName(), name.c_str()); |
|
|
| |
| std::vector<App::Property*> props; |
| std::vector<App::Property*> cprops; |
| obj->getPropertyList(props); |
| copy->getPropertyList(cprops); |
|
|
| auto it = cprops.begin(); |
| for (App::Property* prop : props) { |
|
|
| |
| if (independent |
| && (prop->isDerivedFrom<App::PropertyLink>() |
| || prop->isDerivedFrom<App::PropertyLinkList>() |
| || prop->isDerivedFrom<App::PropertyLinkSub>() |
| || prop->isDerivedFrom<App::PropertyLinkSubList>() |
| || (prop->getGroup() && strcmp(prop->getGroup(), "Attachment") == 0))) { |
|
|
| ++it; |
| continue; |
| } |
|
|
| App::Property* cprop = *it++; |
|
|
| if (prop->getName() && strcmp(prop->getName(), "Label") == 0) { |
| static_cast<App::PropertyString*>(cprop)->setValue(name.c_str()); |
| continue; |
| } |
|
|
| cprop->Paste(*prop); |
|
|
| |
| |
| if (auto* sketchObj = freecad_cast<Sketcher::SketchObject*>(obj)) { |
| sketchObj->delConstraintsToExternal(); |
| } |
| } |
| } |
| else { |
|
|
| const std::string name = (!independent ? std::string("Reference") : std::string("Copy")) |
| + obj->getNameInDocument(); |
| const std::string entity = sub; |
|
|
| Part::PropertyPartShape* shapeProp = nullptr; |
|
|
| |
| if (obj->isDerivedFrom<Part::Datum>()) { |
| auto* doc = App::GetApplication().getActiveDocument(); |
| copy = doc->addObject<Part::Datum>(name.c_str()); |
|
|
| |
| |
| |
| long int mode = mmDeactivated; |
| Part::Datum* datumCopy = static_cast<Part::Datum*>(copy); |
|
|
| if (obj->is<PartDesign::Point>()) { |
| mode = mm0Vertex; |
| } |
| else if (obj->is<PartDesign::Line>()) { |
| mode = mm1TwoPoints; |
| } |
| else if (obj->is<PartDesign::Plane>()) { |
| mode = mmFlatFace; |
| } |
| else { |
| return copy; |
| } |
|
|
| |
| |
| if (!independent) { |
| datumCopy->AttachmentSupport.setValue(obj, entity.c_str()); |
| datumCopy->MapMode.setValue(mode); |
| } |
| else if (!entity.empty()) { |
| datumCopy->Shape.setValue( |
| static_cast<Part::Datum*>(obj)->Shape.getShape().getSubShape(entity.c_str()) |
| ); |
| } |
| else { |
| datumCopy->Shape.setValue(static_cast<Part::Datum*>(obj)->Shape.getValue()); |
| } |
| } |
| else if (obj->is<PartDesign::ShapeBinder>() || obj->isDerivedFrom<Part::Feature>()) { |
|
|
| auto* doc = App::GetApplication().getActiveDocument(); |
| auto* shapeBinderObj = doc->addObject<PartDesign::ShapeBinder>(name.c_str()); |
| if (!independent) { |
| shapeBinderObj->Support.setValue(obj, entity.c_str()); |
| } |
| else { |
| shapeProp = &shapeBinderObj->Shape; |
| } |
| copy = shapeBinderObj; |
| } |
| else if (obj->isDerivedFrom<App::Plane>() || obj->isDerivedFrom<App::Line>()) { |
|
|
| auto* doc = App::GetApplication().getActiveDocument(); |
| auto* shapeBinderObj = doc->addObject<PartDesign::ShapeBinder>(name.c_str()); |
| if (!independent) { |
| shapeBinderObj->Support.setValue(obj, entity.c_str()); |
| } |
| else { |
| std::vector<std::string> subvalues; |
| subvalues.push_back(entity); |
| Part::TopoShape shape |
| = PartDesign::ShapeBinder::buildShapeFromReferences(shapeBinderObj, subvalues); |
| shapeBinderObj->Shape.setValue(shape); |
| } |
| copy = shapeBinderObj; |
| } |
|
|
| if (independent && shapeProp) { |
| auto* featureObj = static_cast<Part::Feature*>(obj); |
| shapeProp->setValue( |
| entity.empty() ? featureObj->Shape.getValue() |
| : featureObj->Shape.getShape().getSubShape(entity.c_str()) |
| ); |
| } |
| } |
|
|
| return copy; |
| } |
|
|
| bool TaskFeaturePick::isSingleSelectionEnabled() const |
| { |
| ParameterGrp::handle hGrp = App::GetApplication() |
| .GetUserParameter() |
| .GetGroup("BaseApp") |
| ->GetGroup("Preferences") |
| ->GetGroup("Selection"); |
| return hGrp->GetBool("singleClickFeatureSelect", true); |
| } |
|
|
| void TaskFeaturePick::onSelectionChanged(const Gui::SelectionChanges& msg) |
| { |
| if (doSelection) { |
| return; |
| } |
| doSelection = true; |
| ui->listWidget->clearSelection(); |
| for (Gui::SelectionSingleton::SelObj obj : Gui::Selection().getSelection()) { |
| for (int row = 0; row < ui->listWidget->count(); row++) { |
| QListWidgetItem* item = ui->listWidget->item(row); |
| QString t = item->data(Qt::UserRole).toString(); |
| if (t.compare(QString::fromLatin1(obj.FeatName)) == 0) { |
| item->setSelected(true); |
|
|
| if (msg.Type == Gui::SelectionChanges::AddSelection) { |
| if (isSingleSelectionEnabled()) { |
| QMetaObject::invokeMethod( |
| qobject_cast<Gui::ControlSingleton*>(&Gui::Control()), |
| "accept", |
| Qt::QueuedConnection |
| ); |
| } |
| } |
| } |
| } |
| } |
| doSelection = false; |
| } |
|
|
| void TaskFeaturePick::onItemSelectionChanged() |
| { |
| if (doSelection) { |
| return; |
| } |
| doSelection = true; |
| ui->listWidget->blockSignals(true); |
| Gui::Selection().clearSelection(); |
| for (int row = 0; row < ui->listWidget->count(); row++) { |
| QListWidgetItem* item = ui->listWidget->item(row); |
| QString t = item->data(Qt::UserRole).toString(); |
| if (item->isSelected()) { |
| Gui::Selection().addSelection(documentName.c_str(), t.toLatin1()); |
| } |
| } |
| ui->listWidget->blockSignals(false); |
| doSelection = false; |
| } |
|
|
| void TaskFeaturePick::onDoubleClick(QListWidgetItem* item) |
| { |
| if (doSelection) { |
| return; |
| } |
| doSelection = true; |
| QString t = item->data(Qt::UserRole).toString(); |
| Gui::Selection().addSelection(documentName.c_str(), t.toLatin1()); |
| doSelection = false; |
|
|
| QMetaObject::invokeMethod( |
| qobject_cast<Gui::ControlSingleton*>(&Gui::Control()), |
| "accept", |
| Qt::QueuedConnection |
| ); |
| } |
|
|
| void TaskFeaturePick::slotDeletedObject(const Gui::ViewProviderDocumentObject& Obj) |
| { |
| if (const auto it = std::ranges::find(origins, &Obj); it != origins.end()) { |
| origins.erase(it); |
| } |
| } |
|
|
| void TaskFeaturePick::slotUndoDocument(const Gui::Document&) |
| { |
| if (origins.empty()) { |
| QTimer::singleShot(100, &Gui::Control(), &Gui::ControlSingleton::closeDialog); |
| } |
| } |
|
|
| void TaskFeaturePick::slotDeleteDocument(const Gui::Document&) |
| { |
| origins.clear(); |
| QTimer::singleShot(100, &Gui::Control(), &Gui::ControlSingleton::closeDialog); |
| } |
|
|
| void TaskFeaturePick::showExternal(bool val) |
| { |
| ui->checkOtherBody->setChecked(val); |
| ui->checkOtherPart->setChecked(val); |
| updateList(); |
| } |
|
|
|
|
| |
| |
| |
| |
|
|
| TaskDlgFeaturePick::TaskDlgFeaturePick( |
| std::vector<App::DocumentObject*>& objects, |
| const std::vector<TaskFeaturePick::featureStatus>& status, |
| std::function<bool(std::vector<App::DocumentObject*>)> afunc, |
| std::function<void(std::vector<App::DocumentObject*>)> wfunc, |
| bool singleFeatureSelect, |
| std::function<void(void)> abortfunc |
| ) |
| : TaskDialog() |
| , accepted(false) |
| { |
| pick = new TaskFeaturePick(objects, status, singleFeatureSelect); |
| Content.push_back(pick); |
|
|
| acceptFunction = afunc; |
| workFunction = wfunc; |
| abortFunction = abortfunc; |
| } |
|
|
| TaskDlgFeaturePick::~TaskDlgFeaturePick() |
| { |
| |
| |
| if (accepted) { |
| try { |
| workFunction(pick->buildFeatures()); |
| } |
| catch (...) { |
| } |
| } |
| else if (abortFunction) { |
|
|
| |
| |
| |
| for (auto it : Content) { |
| delete it; |
| } |
| Content.clear(); |
|
|
| try { |
| abortFunction(); |
| } |
| catch (...) { |
| } |
| } |
| } |
|
|
| |
|
|
|
|
| void TaskDlgFeaturePick::open() |
| {} |
|
|
| void TaskDlgFeaturePick::clicked(int) |
| {} |
|
|
| bool TaskDlgFeaturePick::accept() |
| { |
| accepted = acceptFunction(pick->getFeatures()); |
| return accepted; |
| } |
|
|
| bool TaskDlgFeaturePick::reject() |
| { |
| accepted = false; |
| return true; |
| } |
|
|
| void TaskDlgFeaturePick::showExternal(bool val) |
| { |
| pick->showExternal(val); |
| } |
|
|
|
|
| #include "moc_TaskFeaturePick.cpp" |
|
|