/************************************************************************ ** ** @file dialogrestrictdimension.cpp ** @author Roman Telezhynskyi ** @date 5 10, 2020 ** ** @brief ** @copyright ** This source code is part of the Valentina project, a pattern making ** program, whose allow create and modeling patterns of clothing. ** Copyright (C) 2020 Valentina project ** All Rights Reserved. ** ** Valentina is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** Valentina 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 General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with Valentina. If not, see . ** *************************************************************************/ #include "dialogrestrictdimension.h" #include "ui_dialogrestrictdimension.h" #include #include "../vpatterndb/variables/vmeasurement.h" #include "../vwidgets/vdecorationaligningdelegate.h" namespace { //--------------------------------------------------------------------------------------------------------------------- auto FilterByMinimum(const QVector &base, qreal restriction) -> QVector { if (restriction <= 0) { return base; } QVector filtered; filtered.reserve(base.size()); for(const auto &b : base) { if (b >= restriction) { filtered.append(b); } } return filtered; } //--------------------------------------------------------------------------------------------------------------------- auto FilterByMaximum(const QVector &base, qreal restriction) -> QVector { if (restriction <= 0) { return base; } QVector filtered; filtered.reserve(base.size()); for(const auto &b : base) { if (b <= restriction) { filtered.append(b); } } return filtered; } } // namespace //--------------------------------------------------------------------------------------------------------------------- DialogRestrictDimension::DialogRestrictDimension(const QList &dimensions, const QMap> &restrictions, bool oneDimesionRestriction, bool fullCircumference, QWidget *parent) : QDialog(parent), ui(new Ui::DialogRestrictDimension), m_oneDimesionRestriction(oneDimesionRestriction), m_fullCircumference(fullCircumference), m_dimensions(dimensions), m_restrictions(restrictions) { ui->setupUi(this); ui->tableWidget->setItemDelegate( new VDecorationAligningDelegate(Qt::AlignHCenter | Qt::AlignCenter, ui->tableWidget)); connect(ui->tableWidget, &QTableWidget::itemSelectionChanged, this, &DialogRestrictDimension::RowSelected); connect(ui->comboBoxDimensionA, QOverload::of(&QComboBox::currentIndexChanged), this, &DialogRestrictDimension::DimensionAChanged); connect(ui->comboBoxMin, QOverload::of(&QComboBox::currentIndexChanged), this, &DialogRestrictDimension::MinRestrictionChanged); connect(ui->comboBoxMax, QOverload::of(&QComboBox::currentIndexChanged), this, &DialogRestrictDimension::MaxRestrictionChanged); InitDimensionsBaseValues(); InitTable(); } //--------------------------------------------------------------------------------------------------------------------- DialogRestrictDimension::~DialogRestrictDimension() { delete ui; } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::changeEvent(QEvent *event) { if (event->type() == QEvent::LanguageChange) { // retranslate designer form (single inheritance approach) ui->retranslateUi(this); auto RetranslateControls = [this](int index, QLabel *name, QComboBox *control) { SCASSERT(name != nullptr) SCASSERT(control != nullptr) if (m_dimensions.size() > index) { MeasurementDimension_p dimension = m_dimensions.at(index); name->setText(VAbstartMeasurementDimension::DimensionName(dimension->Type())+QChar(':')); name->setToolTip(VAbstartMeasurementDimension::DimensionToolTip(dimension->Type(), dimension->IsCircumference(), m_fullCircumference)); InitDimensionGradation(dimension, control); } }; if (not m_oneDimesionRestriction) { RetranslateControls(0, ui->labelDimensionA, ui->comboBoxDimensionA); } } // remember to call base class implementation QDialog::changeEvent(event); } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::RowSelected() { EnableRestrictionControls(false); QTableWidgetItem *item = ui->tableWidget->currentItem(); if (item != nullptr) { qreal base1 = 0; qreal base2 = 0; MeasurementDimension_p dimension; if (m_oneDimesionRestriction) { base1 = item->data(Qt::UserRole).toDouble(); if (m_dimensions.size() > 1) { dimension = m_dimensions.at(1); } } else { base1 = ui->comboBoxDimensionA->currentData().toDouble(); base2 = item->data(Qt::UserRole).toDouble(); if (m_dimensions.size() > 2) { dimension = m_dimensions.at(2); } } QPair restriction = m_restrictions.value(VMeasurement::CorrectionHash(base1, base2), QPair(0, 0)); if (dimension.isNull()) { return; } const QVector bases = dimension->ValidBases(); ui->comboBoxMin->blockSignals(true); ui->comboBoxMin->clear(); QVector filtered = FilterByMaximum(bases, restriction.second); FillBases(filtered, dimension, ui->comboBoxMin); int index = ui->comboBoxMin->findData(restriction.first); ui->comboBoxMin->setCurrentIndex(index != -1 ? index : 0); ui->comboBoxMin->blockSignals(false); ui->comboBoxMax->blockSignals(true); ui->comboBoxMax->clear(); filtered = FilterByMinimum(bases, restriction.first); FillBases(FilterByMinimum(bases, restriction.first), dimension, ui->comboBoxMax); index = ui->comboBoxMax->findData(restriction.second); ui->comboBoxMax->setCurrentIndex(index != -1 ? index : ui->comboBoxMax->count() - 1); ui->comboBoxMax->blockSignals(false); EnableRestrictionControls(true); } } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::DimensionAChanged() { InitTable(); } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::MinRestrictionChanged() { QTableWidgetItem *item = ui->tableWidget->currentItem(); if (item != nullptr) { qreal base1 = 0; qreal base2 = 0; if (m_oneDimesionRestriction) { base1 = item->data(Qt::UserRole).toDouble(); } else { base1 = ui->comboBoxDimensionA->currentData().toDouble(); base2 = item->data(Qt::UserRole).toDouble(); } const QString coordinates = VMeasurement::CorrectionHash(base1, base2); QPair restriction = m_restrictions.value(coordinates, QPair(0, 0)); restriction.first = ui->comboBoxMin->currentData().toDouble(); m_restrictions.insert(coordinates, restriction); const int currentRow = ui->tableWidget->currentRow(); RefreshTable(); ui->tableWidget->blockSignals(true); ui->tableWidget->selectRow(currentRow); ui->tableWidget->blockSignals(false); RowSelected(); } } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::MaxRestrictionChanged() { QTableWidgetItem *item = ui->tableWidget->currentItem(); if (item != nullptr) { qreal base1 = 0; qreal base2 = 0; if (m_oneDimesionRestriction) { base1 = item->data(Qt::UserRole).toDouble(); } else { base1 = ui->comboBoxDimensionA->currentData().toDouble(); base2 = item->data(Qt::UserRole).toDouble(); } const QString coordinates = VMeasurement::CorrectionHash(base1, base2); QPair restriction = m_restrictions.value(coordinates, QPair(0, 0)); restriction.second = ui->comboBoxMax->currentData().toDouble(); m_restrictions.insert(coordinates, restriction); const int currentRow = ui->tableWidget->currentRow(); RefreshTable(); ui->tableWidget->blockSignals(true); ui->tableWidget->selectRow(currentRow); ui->tableWidget->blockSignals(false); RowSelected(); } } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::InitDimensionsBaseValues() { auto DimensionsBaseValue = [this](int index, QLabel *name, QComboBox *control) { SCASSERT(name != nullptr) SCASSERT(control != nullptr) if (m_dimensions.size() > index) { MeasurementDimension_p dimension = m_dimensions.at(index); name->setText(VAbstartMeasurementDimension::DimensionName(dimension->Type())+QChar(':')); name->setToolTip(VAbstartMeasurementDimension::DimensionToolTip(dimension->Type(), dimension->IsCircumference(), m_fullCircumference)); InitDimensionGradation(dimension, control); } }; if (not m_oneDimesionRestriction) { if (not m_dimensions.empty()) { DimensionsBaseValue(0, ui->labelDimensionA, ui->comboBoxDimensionA); } else { ui->labelDimensionA->setVisible(false); ui->comboBoxDimensionA->setVisible(false); } } else { ui->labelDimensionA->setVisible(false); ui->comboBoxDimensionA->setVisible(false); } } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::InitDimensionGradation(const MeasurementDimension_p &dimension, QComboBox *control) { SCASSERT(control != nullptr) qreal current = -1; if (control->currentIndex() != -1) { current = control->currentData().toDouble(); } control->blockSignals(true); control->clear(); FillBases(dimension->ValidBases(), dimension, control); int i = control->findData(current); if (i != -1) { control->setCurrentIndex(i); control->blockSignals(false); } else { control->blockSignals(false); control->setCurrentIndex(0); } } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::InitTable() { ui->tableWidget->blockSignals(true); ui->tableWidget->clear(); auto InitHeaders = [this](int index) { if (m_dimensions.size() > index) { MeasurementDimension_p dimensionA = m_dimensions.at(index-1); const QVector basesA = dimensionA->ValidBases(); ui->tableWidget->setRowCount(basesA.size()); ui->tableWidget->setVerticalHeaderLabels(DimensionLabels(basesA, dimensionA)); MeasurementDimension_p dimensionB = m_dimensions.at(index); const QVector basesB = dimensionB->ValidBases(); ui->tableWidget->setColumnCount(basesB.size()); ui->tableWidget->setHorizontalHeaderLabels(DimensionLabels(basesB, dimensionB)); } }; InitHeaders(m_oneDimesionRestriction ? 1 : 2); ui->tableWidget->blockSignals(false); RefreshTable(); ui->tableWidget->selectRow(StartRow()); } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::RefreshTable() { QVector basesRow; QVector basesColumn; if (m_oneDimesionRestriction) { if (m_dimensions.size() >= 2) { MeasurementDimension_p dimensionA = m_dimensions.at(0); basesRow = dimensionA->ValidBases(); MeasurementDimension_p dimensionB = m_dimensions.at(1); basesColumn = dimensionB->ValidBases(); } else { return; } } else { if (m_dimensions.size() >= 3) { MeasurementDimension_p dimensionB = m_dimensions.at(1); basesRow = dimensionB->ValidBases(); MeasurementDimension_p dimensionC = m_dimensions.at(2); basesColumn = dimensionC->ValidBases(); } else { return; } } ui->tableWidget->blockSignals(true); ui->tableWidget->clearContents(); for(int row=0; row < basesRow.size(); ++row) { for(int column=0; column < basesColumn.size(); ++column) { AddCell(row, column, basesRow.at(row), basesColumn.at(column)); } } ui->tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); ui->tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); ui->tableWidget->blockSignals(false); } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::AddCell(int row, int column, qreal rowValue, qreal columnValue) { auto *item = new QTableWidgetItem(); item->setData(Qt::UserRole, rowValue); qreal base1 = 0; qreal base2 = 0; MeasurementDimension_p dimension; QVector bases; QVector validRows; if (m_oneDimesionRestriction) { base1 = rowValue; if (m_dimensions.size() >= 2) { validRows = m_dimensions.at(0)->ValidBases(); dimension = m_dimensions.at(1); bases = dimension->ValidBases(); } } else { base1 = ui->comboBoxDimensionA->currentData().toDouble(); base2 = rowValue; if (m_dimensions.size() >= 3) { validRows = DimensionRestrictedValues(m_dimensions.at(1)); dimension = m_dimensions.at(2); bases = dimension->ValidBases(); } } QPair restriction = m_restrictions.value(VMeasurement::CorrectionHash(base1, base2), QPair(0, 0)); qreal min = INT32_MIN; qreal max = INT32_MAX; if (not dimension.isNull()) { min = bases.indexOf(restriction.first) != -1 ? restriction.first : dimension->MinValue(); max = bases.indexOf(restriction.second) != -1 ? restriction.second : dimension->MaxValue(); if (max < min) { min = dimension->MinValue(); max = dimension->MaxValue(); } } if (validRows.contains(rowValue)) { const bool leftRestriction = columnValue >= min; const bool rightRestriction = columnValue <= max; if (leftRestriction && rightRestriction) { item->setIcon(QIcon(QStringLiteral("://icon/24x24/star.png"))); } else { item->setIcon(QIcon(QStringLiteral("://icon/24x24/close.png"))); } } else { item->setIcon(QIcon(QStringLiteral("://icon/24x24/close.png"))); Qt::ItemFlags flags = item->flags(); flags &= ~(Qt::ItemIsEnabled); item->setFlags(flags); } // set the item non-editable (view only), and non-selectable Qt::ItemFlags flags = item->flags(); flags &= ~(Qt::ItemIsEditable); // reset/clear the flag item->setFlags(flags); ui->tableWidget->setItem(row, column, item); } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::EnableRestrictionControls(bool enable) { if (not enable) { ui->comboBoxMin->blockSignals(true); ui->comboBoxMin->setCurrentIndex(-1); ui->comboBoxMin->blockSignals(false); ui->comboBoxMax->blockSignals(true); ui->comboBoxMax->setCurrentIndex(-1); ui->comboBoxMax->blockSignals(false); } ui->groupBoxRestriction->setEnabled(enable); } //--------------------------------------------------------------------------------------------------------------------- void DialogRestrictDimension::FillBases(const QVector &bases, const MeasurementDimension_p &dimension, QComboBox *control) const { SCASSERT(control != nullptr) const DimesionLabels labels = dimension->Labels(); const QString units = UnitsToStr(dimension->Units(), true); if (dimension->Type() == MeasurementDimension::X) { for(auto base : bases) { if (labels.contains(base) && not labels.value(base).isEmpty()) { control->addItem(labels.value(base), base); } else { control->addItem(QStringLiteral("%1 %2").arg(base).arg(units), base); } } } else if (dimension->Type() == MeasurementDimension::Y) { for(auto base : bases) { if (labels.contains(base) && not labels.value(base).isEmpty()) { control->addItem(labels.value(base), base); } else { if (dimension->IsCircumference()) { control->addItem(QStringLiteral("%1 %2").arg(m_fullCircumference ? base*2 : base).arg(units), base); } else { control->addItem(QString::number(base), base); } } } } else if (dimension->Type() == MeasurementDimension::W || dimension->Type() == MeasurementDimension::Z) { for(auto base : bases) { if (labels.contains(base) && not labels.value(base).isEmpty()) { control->addItem(labels.value(base), base); } else { control->addItem(QStringLiteral("%1 %2").arg(m_fullCircumference ? base*2 : base).arg(units), base); } } } } //--------------------------------------------------------------------------------------------------------------------- auto DialogRestrictDimension::DimensionLabels(const QVector &bases, const MeasurementDimension_p &dimension) const -> QStringList { const bool showUnits = dimension->IsCircumference() || dimension->Type() == MeasurementDimension::X; const QString units = showUnits ? UnitsToStr(dimension->Units(), true) : QString(); const DimesionLabels dimensionLabels = dimension->Labels(); QStringList labels; if (dimension->Type() == MeasurementDimension::X) { for(auto base : bases) { if (dimensionLabels.contains(base) && not dimensionLabels.value(base).isEmpty()) { labels.append(dimensionLabels.value(base)); } else { labels.append(QStringLiteral("%1 %2").arg(base).arg(units)); } } } else if (dimension->Type() == MeasurementDimension::Y) { for(auto base : bases) { if (dimensionLabels.contains(base) && not dimensionLabels.value(base).isEmpty()) { labels.append(dimensionLabels.value(base)); } else { if (dimension->IsCircumference()) { labels.append(QStringLiteral("%1 %2").arg(m_fullCircumference ? base*2 : base).arg(units)); } else { labels.append(QString::number(base)); } } } } else if (dimension->Type() == MeasurementDimension::W || dimension->Type() == MeasurementDimension::Z) { for(auto base : bases) { if (dimensionLabels.contains(base) && not dimensionLabels.value(base).isEmpty()) { labels.append(dimensionLabels.value(base)); } else { labels.append(QStringLiteral("%1 %2").arg(m_fullCircumference ? base*2 : base).arg(units)); } } } return labels; } //--------------------------------------------------------------------------------------------------------------------- auto DialogRestrictDimension::DimensionRestrictedValues(const MeasurementDimension_p &dimension) const -> QVector { QPair restriction; if (not m_oneDimesionRestriction) { qreal base1 = ui->comboBoxDimensionA->currentData().toDouble(); restriction = m_restrictions.value(VMeasurement::CorrectionHash(base1), QPair(0, 0)); } const QVector bases = dimension->ValidBases(); qreal min = bases.indexOf(restriction.first) != -1 ? restriction.first : dimension->MinValue(); qreal max = bases.indexOf(restriction.second) != -1 ? restriction.second : dimension->MaxValue(); if (min > max) { min = dimension->MinValue(); max = dimension->MaxValue(); } return VAbstartMeasurementDimension::ValidBases(min, max, dimension->Step()); } //--------------------------------------------------------------------------------------------------------------------- auto DialogRestrictDimension::StartRow() const -> int { if (m_oneDimesionRestriction) { return 0; } QVector basesRow; if (m_dimensions.size() >= 3) { MeasurementDimension_p dimensionB = m_dimensions.at(1); basesRow = dimensionB->ValidBases(); QVector validRows = DimensionRestrictedValues(dimensionB); for(int i=0; i < basesRow.size(); ++i) { if (validRows.contains(basesRow.at(i))) { return i; } } } return 0; }