// SPDX-License-Identifier: LGPL-2.1-or-later /****************************************************************************** * Copyright (c) 2012 Jan Rheinländer * * * * 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 "ui_TaskMultiTransformParameters.h" #include "TaskMultiTransformParameters.h" #include "TaskMirroredParameters.h" #include "TaskPatternParameters.h" #include "TaskScaledParameters.h" #include "Utils.h" using namespace PartDesignGui; using namespace Gui; /* TRANSLATOR PartDesignGui::TaskMultiTransformParameters */ TaskMultiTransformParameters::TaskMultiTransformParameters( ViewProviderTransformed* TransformedView, QWidget* parent ) : TaskTransformedParameters(TransformedView, parent) , ui(new Ui_TaskMultiTransformParameters) { setupUI(); } void TaskMultiTransformParameters::setupParameterUI(QWidget* widget) { ui->setupUi(widget); QMetaObject::connectSlotsByName(this); // Create a context menu for the listview of transformation features auto action = new QAction(tr("Edit"), ui->listTransformFeatures); action->connect(action, &QAction::triggered, this, &TaskMultiTransformParameters::onTransformEdit); ui->listTransformFeatures->addAction(action); action = new QAction(tr("Delete"), ui->listTransformFeatures); action->connect(action, &QAction::triggered, this, &TaskMultiTransformParameters::onTransformDelete); ui->listTransformFeatures->addAction(action); action = new QAction(tr("Add Mirror Transformation"), ui->listTransformFeatures); action->connect( action, &QAction::triggered, this, &TaskMultiTransformParameters::onTransformAddMirrored ); ui->listTransformFeatures->addAction(action); action = new QAction(tr("Add Linear Pattern"), ui->listTransformFeatures); action->connect( action, &QAction::triggered, this, &TaskMultiTransformParameters::onTransformAddLinearPattern ); ui->listTransformFeatures->addAction(action); action = new QAction(tr("Add Polar Pattern"), ui->listTransformFeatures); action->connect( action, &QAction::triggered, this, &TaskMultiTransformParameters::onTransformAddPolarPattern ); ui->listTransformFeatures->addAction(action); action = new QAction(tr("Add Scale Transformation"), ui->listTransformFeatures); action->connect(action, &QAction::triggered, this, &TaskMultiTransformParameters::onTransformAddScaled); ui->listTransformFeatures->addAction(action); action = new QAction(tr("Move Up"), ui->listTransformFeatures); action->connect(action, &QAction::triggered, this, &TaskMultiTransformParameters::onMoveUp); ui->listTransformFeatures->addAction(action); action = new QAction(tr("Move Down"), ui->listTransformFeatures); action->connect(action, &QAction::triggered, this, &TaskMultiTransformParameters::onMoveDown); ui->listTransformFeatures->addAction(action); ui->listTransformFeatures->setContextMenuPolicy(Qt::ActionsContextMenu); connect( ui->listTransformFeatures, &QListWidget::activated, this, &TaskMultiTransformParameters::onTransformActivated ); connect(ui->buttonOK, &QToolButton::pressed, this, &TaskMultiTransformParameters::onSubTaskButtonOK); ui->buttonOK->hide(); // Get the transformFeatures data auto pcMultiTransform = TransformedView->getObject(); std::vector transformFeatures = pcMultiTransform->Transformations.getValues(); // Fill data into dialog elements ui->listTransformFeatures->setEnabled(true); ui->listTransformFeatures->clear(); for (auto it : transformFeatures) { if (it) { ui->listTransformFeatures->addItem(QString::fromUtf8(it->Label.getValue())); } } if (!transformFeatures.empty()) { ui->listTransformFeatures->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); editHint = false; } else { ui->listTransformFeatures->addItem(tr("Right-click to add a transformation")); editHint = true; } } void TaskMultiTransformParameters::retranslateParameterUI(QWidget* widget) { ui->retranslateUi(widget); } void TaskMultiTransformParameters::slotDeletedObject(const Gui::ViewProviderDocumentObject& Obj) { if (Obj.getObject() == this->subFeature) { this->subFeature = nullptr; } TaskTransformedParameters::slotDeletedObject(Obj); } void TaskMultiTransformParameters::closeSubTask() { if (subTask) { ui->buttonOK->hide(); exitSelectionMode(); // The subfeature can already be deleted (e.g. cancel) so we have to check before // calling apply if (subFeature) { subTask->apply(); } delete subTask; subTask = nullptr; subFeature = nullptr; // Remove all parameter ui widgets and layout ui->subFeatureWidget->setUpdatesEnabled(false); qDeleteAll(ui->subFeatureWidget->findChildren(QString(), Qt::FindDirectChildrenOnly)); qDeleteAll(ui->subFeatureWidget->findChildren(QString(), Qt::FindDirectChildrenOnly)); ui->subFeatureWidget->setUpdatesEnabled(true); } } void TaskMultiTransformParameters::onTransformDelete() { if (editHint) { return; // Can't delete the hint... } int row = ui->listTransformFeatures->currentIndex().row(); auto pcMultiTransform = TransformedView->getObject(); std::vector transformFeatures = pcMultiTransform->Transformations.getValues(); App::DocumentObject* feature = transformFeatures[row]; if (feature == this->subFeature) { this->subFeature = nullptr; } setupTransaction(); pcMultiTransform->getDocument()->removeObject(feature->getNameInDocument()); closeSubTask(); transformFeatures.erase(transformFeatures.begin() + row); pcMultiTransform->Transformations.setValues(transformFeatures); // Note: When the last transformation is deleted, recomputeFeature does nothing, because // Transformed::execute() says: "No transformations defined, exit silently" recomputeFeature(); ui->listTransformFeatures->model()->removeRow(row); ui->listTransformFeatures->setCurrentRow(0, QItemSelectionModel::ClearAndSelect); } void TaskMultiTransformParameters::onTransformEdit() { if (editHint) { return; // Can't edit the hint... } closeSubTask(); // For example if user is editing one subTask and then double-clicks on another // without OK'ing first ui->listTransformFeatures->currentItem()->setSelected(true); int row = ui->listTransformFeatures->currentIndex().row(); auto pcMultiTransform = TransformedView->getObject(); std::vector transformFeatures = pcMultiTransform->Transformations.getValues(); subFeature = static_cast(transformFeatures[row]); if (subFeature->is()) { subTask = new TaskMirroredParameters(this, ui->subFeatureWidget); } else if (subFeature->is() || subFeature->is()) { subTask = new TaskPatternParameters(this, ui->subFeatureWidget); } else if (subFeature->is()) { subTask = new TaskScaledParameters(this, ui->subFeatureWidget); } else { return; // TODO: Show an error? } ui->buttonOK->show(); subTask->setEnabledTransaction(isEnabledTransaction()); } void TaskMultiTransformParameters::onTransformActivated(const QModelIndex& index) { Q_UNUSED(index); onTransformEdit(); } void TaskMultiTransformParameters::onTransformAddMirrored() { closeSubTask(); std::string newFeatName = TransformedView->getObject()->getDocument()->getUniqueObjectName( "Mirror" ); auto pcBody = dynamic_cast( Part::BodyBase::findBodyOf(getTopTransformedObject()) ); if (!pcBody) { return; } if (isEnabledTransaction()) { Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Mirror")); } FCMD_OBJ_CMD(pcBody, "newObject('PartDesign::Mirrored','" << newFeatName << "')"); auto Feat = pcBody->getDocument()->getObject(newFeatName.c_str()); if (!Feat) { return; } // Gui::Command::updateActive(); App::DocumentObject* sketch = getSketchObject(); if (sketch) { FCMD_OBJ_CMD(Feat, "MirrorPlane = (" << Gui::Command::getObjectCmd(sketch) << ",['V_Axis'])"); } else { App::Origin* orig = pcBody->getOrigin(); FCMD_OBJ_CMD(Feat, "MirrorPlane = (" << Gui::Command::getObjectCmd(orig->getXY()) << ",[''])"); } finishAdd(newFeatName); // show the new view when no error if (!Feat->isError()) { TransformedView->getObject()->Visibility.setValue(true); } } void TaskMultiTransformParameters::onTransformAddLinearPattern() { // See CmdPartDesignLinearPattern // closeSubTask(); std::string newFeatName = TransformedView->getObject()->getDocument()->getUniqueObjectName( "Linear Pattern" ); auto pcBody = dynamic_cast( Part::BodyBase::findBodyOf(getTopTransformedObject()) ); if (!pcBody) { return; } if (isEnabledTransaction()) { Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Linear Pattern")); } FCMD_OBJ_CMD(pcBody, "newObject('PartDesign::LinearPattern','" << newFeatName << "')"); auto Feat = pcBody->getDocument()->getObject(newFeatName.c_str()); if (!Feat) { return; } // Gui::Command::updateActive(); App::DocumentObject* sketch = getSketchObject(); if (sketch) { FCMD_OBJ_CMD(Feat, "Direction = (" << Gui::Command::getObjectCmd(sketch) << ",['H_Axis'])"); } else { // set Direction value before filling up the combo box to avoid creating an empty item // inside updateUI() auto body = dynamic_cast(Part::BodyBase::findBodyOf(getObject())); if (body) { FCMD_OBJ_CMD( Feat, "Direction = (" << Gui::Command::getObjectCmd(body->getOrigin()->getX()) << ",[''])" ); } } FCMD_OBJ_CMD(Feat, "Length = 100"); FCMD_OBJ_CMD(Feat, "Occurrences = 2"); finishAdd(newFeatName); // show the new view when no error if (!Feat->isError()) { TransformedView->getObject()->Visibility.setValue(true); } } void TaskMultiTransformParameters::onTransformAddPolarPattern() { closeSubTask(); std::string newFeatName = TransformedView->getObject()->getDocument()->getUniqueObjectName( "Polar Pattern" ); auto pcBody = dynamic_cast( Part::BodyBase::findBodyOf(getTopTransformedObject()) ); if (!pcBody) { return; } if (isEnabledTransaction()) { Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Polar Pattern")); } FCMD_OBJ_CMD(pcBody, "newObject('PartDesign::PolarPattern','" << newFeatName << "')"); auto Feat = pcBody->getDocument()->getObject(newFeatName.c_str()); if (!Feat) { return; } // Gui::Command::updateActive(); App::DocumentObject* sketch = getSketchObject(); if (sketch) { FCMD_OBJ_CMD(Feat, "Axis = (" << Gui::Command::getObjectCmd(sketch) << ",['N_Axis'])"); } else { App::Origin* orig = pcBody->getOrigin(); FCMD_OBJ_CMD(Feat, "Axis = (" << Gui::Command::getObjectCmd(orig->getX()) << ",[''])"); } FCMD_OBJ_CMD(Feat, "Angle = 360"); FCMD_OBJ_CMD(Feat, "Occurrences = 2"); finishAdd(newFeatName); // show the new view when no error if (!Feat->isError()) { TransformedView->getObject()->Visibility.setValue(true); } } void TaskMultiTransformParameters::onTransformAddScaled() { closeSubTask(); std::string newFeatName = TransformedView->getObject()->getDocument()->getUniqueObjectName("Scale"); auto pcBody = dynamic_cast( Part::BodyBase::findBodyOf(getTopTransformedObject()) ); if (!pcBody) { return; } if (isEnabledTransaction()) { Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Scale")); } FCMD_OBJ_CMD(pcBody, "newObject('PartDesign::Scaled','" << newFeatName << "')"); auto Feat = pcBody->getDocument()->getObject(newFeatName.c_str()); if (!Feat) { return; } // Gui::Command::updateActive(); FCMD_OBJ_CMD(Feat, "Factor = 2"); FCMD_OBJ_CMD(Feat, "Occurrences = 2"); finishAdd(newFeatName); // show the new view when no error if (!Feat->isError()) { TransformedView->getObject()->Visibility.setValue(true); } } void TaskMultiTransformParameters::finishAdd(std::string& newFeatName) { // Gui::Command::updateActive(); // Gui::Command::copyVisual(newFeatName.c_str(), "ShapeColor", // getOriginals().front()->getNameInDocument().c_str()); // Gui::Command::copyVisual(newFeatName.c_str(), "DisplayMode", // getOriginals().front()->getNameInDocument().c_str()); setupTransaction(); auto pcMultiTransform = TransformedView->getObject(); if (editHint) { // Remove hint, first feature is being added ui->listTransformFeatures->model()->removeRow(0); } int row = ui->listTransformFeatures->currentIndex().row(); if (row < 0) { // Happens when first row (first transformation) is created // Hide all the originals now (hiding them in Command.cpp presents the user with an empty // screen!) hideBase(); } // Insert new transformation after the selected row // This means that in order to insert at the beginning, the user has to use "Move Up" in the // menu App::DocumentObject* newFeature = pcMultiTransform->getDocument()->getObject(newFeatName.c_str()); std::vector transformFeatures = pcMultiTransform->Transformations.getValues(); if (row == ui->listTransformFeatures->model()->rowCount() - 1) { // Note: Inserts always happen before the specified iterator so in order to append at the // end we need to use push_back() and append() transformFeatures.push_back(newFeature); ui->listTransformFeatures->addItem(QString::fromLatin1(newFeature->Label.getValue())); ui->listTransformFeatures->setCurrentRow(row + 1, QItemSelectionModel::ClearAndSelect); } else { // Note: The feature tree always seems to append to the end, no matter what we say here transformFeatures.insert(transformFeatures.begin() + row + 1, newFeature); ui->listTransformFeatures->insertItem( row + 1, QString::fromLatin1(newFeature->Label.getValue()) ); ui->listTransformFeatures->setCurrentRow(row + 1, QItemSelectionModel::ClearAndSelect); } pcMultiTransform->Transformations.setValues(transformFeatures); recomputeFeature(); // Set state to hidden - only the MultiTransform should be visible FCMD_OBJ_HIDE(newFeature); editHint = false; onTransformEdit(); } void TaskMultiTransformParameters::moveTransformFeature(const int increment) { setupTransaction(); int row = ui->listTransformFeatures->currentIndex().row(); auto pcMultiTransform = TransformedView->getObject(); std::vector transformFeatures = pcMultiTransform->Transformations.getValues(); if (transformFeatures.empty()) { return; } App::DocumentObject* feature = transformFeatures[row]; transformFeatures.erase(transformFeatures.begin() + row); auto item = new QListWidgetItem(*(ui->listTransformFeatures->item(row))); ui->listTransformFeatures->model()->removeRow(row); // After this operation, if we were to insert at index row again, things will remain unchanged row += increment; if (row < 0) { row = 0; } if (row >= ui->listTransformFeatures->model()->rowCount()) { // Note: Inserts always happen before the specified iterator so in order to append at the // end we need to use push_back() and append() transformFeatures.push_back(feature); ui->listTransformFeatures->addItem(item); ui->listTransformFeatures->setCurrentRow(row, QItemSelectionModel::ClearAndSelect); } else { transformFeatures.insert(transformFeatures.begin() + row, feature); ui->listTransformFeatures->insertItem(row, item); ui->listTransformFeatures->setCurrentRow(row, QItemSelectionModel::ClearAndSelect); } pcMultiTransform->Transformations.setValues(transformFeatures); recomputeFeature(); } void TaskMultiTransformParameters::onMoveUp() { moveTransformFeature(-1); } void TaskMultiTransformParameters::onMoveDown() { moveTransformFeature(+1); } void TaskMultiTransformParameters::onSubTaskButtonOK() { closeSubTask(); } void TaskMultiTransformParameters::onUpdateView(bool on) { blockUpdate = !on; if (on) { recomputeFeature(); } } void TaskMultiTransformParameters::apply() { auto pcMultiTransform = getObject(); std::vector transformFeatures = pcMultiTransform->Transformations.getValues(); std::stringstream str; str << Gui::Command::getObjectCmd(TransformedView->getObject()) << ".Transformations = ["; for (auto it : transformFeatures) { if (it) { str << Gui::Command::getObjectCmd(it) << ","; } } str << "]"; Gui::Command::runCommand(Gui::Command::Doc, str.str().c_str()); } TaskMultiTransformParameters::~TaskMultiTransformParameters() { try { closeSubTask(); } catch (const Py::Exception&) { Base::PyException exc; // extract the Python error text exc.reportException(); } } //************************************************************************** //************************************************************************** // TaskDialog //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TaskDlgMultiTransformParameters::TaskDlgMultiTransformParameters( ViewProviderMultiTransform* MultiTransformView ) : TaskDlgTransformedParameters(MultiTransformView) { parameter = new TaskMultiTransformParameters(MultiTransformView); parameter->setEnabledTransaction(false); Content.push_back(parameter); Content.push_back(preview); } #include "moc_TaskMultiTransformParameters.cpp"