| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| |
|
| | #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" |
| |
|