From 7ba9b380b74ec15ae1a37db24967ed2f7e2840dd Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Sat, 10 Oct 2020 19:31:23 +0300 Subject: [PATCH] Redesign measurements import. --- .../dialogs/dialogmeasurementscsvcolumns.cpp | 916 ++++++++++++++++++ .../dialogs/dialogmeasurementscsvcolumns.h | 149 +++ .../dialogs/dialogmeasurementscsvcolumns.ui | 204 ++++ src/app/tape/tape.pri | 3 + src/app/tape/tmainwindow.cpp | 125 ++- src/app/tape/tmainwindow.h | 4 +- 6 files changed, 1371 insertions(+), 30 deletions(-) create mode 100644 src/app/tape/dialogs/dialogmeasurementscsvcolumns.cpp create mode 100644 src/app/tape/dialogs/dialogmeasurementscsvcolumns.h create mode 100644 src/app/tape/dialogs/dialogmeasurementscsvcolumns.ui diff --git a/src/app/tape/dialogs/dialogmeasurementscsvcolumns.cpp b/src/app/tape/dialogs/dialogmeasurementscsvcolumns.cpp new file mode 100644 index 000000000..44d496507 --- /dev/null +++ b/src/app/tape/dialogs/dialogmeasurementscsvcolumns.cpp @@ -0,0 +1,916 @@ +/************************************************************************ + ** + ** @file dialogmeasurementscsvcolumns.cpp + ** @author Roman Telezhynskyi + ** @date 9 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 "dialogmeasurementscsvcolumns.h" +#include "ui_dialogmeasurementscsvcolumns.h" + +#include "../vmisc/qxtcsvmodel.h" +#include "../vtools/dialogs/dialogtoolbox.h" + +#include +#include + +//--------------------------------------------------------------------------------------------------------------------- +DialogMeasurementsCSVColumns::DialogMeasurementsCSVColumns(const QString &filename, MeasurementsType type, + QWidget *parent) : + QDialog(parent), + ui(new Ui::DialogMeasurementsCSVColumns), + m_fileName{filename}, + m_type(type) +{ + ui->setupUi(this); + + HackColumnControls(); +} + +//--------------------------------------------------------------------------------------------------------------------- +DialogMeasurementsCSVColumns::DialogMeasurementsCSVColumns(const QString &filename, MeasurementsType type, + const QList &dimensions, + QWidget *parent) : + QDialog(parent), + ui(new Ui::DialogMeasurementsCSVColumns), + m_fileName{filename}, + m_type(type), + m_dimensions{dimensions} +{ + ui->setupUi(this); + + HackColumnControls(); +} + +//--------------------------------------------------------------------------------------------------------------------- +DialogMeasurementsCSVColumns::~DialogMeasurementsCSVColumns() +{ + qDeleteAll(m_hackedWidgets); + delete ui; +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::changeEvent(QEvent *event) +{ + if (event->type() == QEvent::LanguageChange) + { + // retranslate designer form (single inheritance approach) + ui->retranslateUi(this); + + RetranslateLabels(); + InitColumnsControls(); + InitImportHeaders(); + ShowImportPreview(); + CheckStatus(); + } + + // remember to call base class implementation + QDialog::changeEvent(event); +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::showEvent(QShowEvent *event) +{ + QDialog::showEvent( event ); + if ( event->spontaneous() ) + { + return; + } + + if (m_isInitialized) + { + return; + } + // do your init stuff here + + if (not m_fileName.isEmpty()) + { + InitColumnsMap(); + ShowInputPreview(); + InitColumnsControls(); + RetranslateLabels(); + InitColumnsControls(); + SetDefaultColumns(); + InitImportHeaders(); + ShowImportPreview(); + + connect(ui->comboBoxName, QOverload::of(&QComboBox::currentIndexChanged), + this, &DialogMeasurementsCSVColumns::ColumnChanged); + connect(ui->comboBoxValue, QOverload::of(&QComboBox::currentIndexChanged), + this, &DialogMeasurementsCSVColumns::ColumnChanged); + + if (m_type == MeasurementsType::Multisize) + { + if (m_dimensions.size() > 0) + { + connect(ui->comboBoxShiftA, QOverload::of(&QComboBox::currentIndexChanged), + this, &DialogMeasurementsCSVColumns::ColumnChanged); + } + + if (m_dimensions.size() > 1) + { + connect(ui->comboBoxShiftB, QOverload::of(&QComboBox::currentIndexChanged), + this, &DialogMeasurementsCSVColumns::ColumnChanged); + } + + if (m_dimensions.size() > 2) + { + connect(ui->comboBoxShiftC, QOverload::of(&QComboBox::currentIndexChanged), + this, &DialogMeasurementsCSVColumns::ColumnChanged); + } + } + + + connect(ui->comboBoxFullName, QOverload::of(&QComboBox::currentIndexChanged), + this, &DialogMeasurementsCSVColumns::ColumnChanged); + connect(ui->comboBoxDescription, QOverload::of(&QComboBox::currentIndexChanged), + this, &DialogMeasurementsCSVColumns::ColumnChanged); + + CheckStatus(); + } + + m_isInitialized = true;//first show windows are held +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::ColumnChanged() +{ + auto *control = qobject_cast(sender()); + + auto SaveColum = [this, control](int column) + { + m_columnsMap[column] = control->currentData().toInt(); + ShowImportPreview(); + CheckStatus(); + }; + + if (control == ui->comboBoxName) + { + if (m_type == MeasurementsType::Individual) + { + SaveColum(static_cast(IndividualMeasurementsColumns::Name)); + } + else + { + SaveColum(static_cast(MultisizeMeasurementsColumns::Name)); + } + } + else if (control == ui->comboBoxValue) + { + if (m_type == MeasurementsType::Individual) + { + SaveColum(static_cast(IndividualMeasurementsColumns::Value)); + } + else + { + SaveColum(static_cast(MultisizeMeasurementsColumns::BaseValue)); + } + } + else if (control == ui->comboBoxShiftA) + { + SaveColum(static_cast(MultisizeMeasurementsColumns::ShiftA)); + } + else if (control == ui->comboBoxShiftB) + { + SaveColum(static_cast(MultisizeMeasurementsColumns::ShiftB)); + } + else if (control == ui->comboBoxShiftC) + { + SaveColum(static_cast(MultisizeMeasurementsColumns::ShiftC)); + } + else if (control == ui->comboBoxFullName) + { + if (m_type == MeasurementsType::Individual) + { + SaveColum(static_cast(IndividualMeasurementsColumns::FullName)); + } + else + { + SaveColum(static_cast(MultisizeMeasurementsColumns::FullName)); + } + } + else if (control == ui->comboBoxDescription) + { + if (m_type == MeasurementsType::Individual) + { + SaveColum(static_cast(IndividualMeasurementsColumns::Description)); + } + else + { + SaveColum(static_cast(MultisizeMeasurementsColumns::Description)); + } + } +} + +//--------------------------------------------------------------------------------------------------------------------- +bool DialogMeasurementsCSVColumns::ColumnMandatory(int column) const +{ + if (m_type == MeasurementsType::Individual) + { + return column < static_cast(IndividualMeasurementsColumns::FullName); + } + else + { + int mandatory = 3; + + if (m_dimensions.size() > 1) + { + mandatory += qMin(m_dimensions.size(), 2); + } + + return static_cast(column) < mandatory; + } +} + +//--------------------------------------------------------------------------------------------------------------------- +QString DialogMeasurementsCSVColumns::ColumnHeader(int column) const +{ + if (m_type == MeasurementsType::Individual) + { + const auto individualColumn = static_cast(column); + switch(individualColumn) + { + case IndividualMeasurementsColumns::Name: + return tr("Name"); + case IndividualMeasurementsColumns::Value: + return tr("Value"); + case IndividualMeasurementsColumns::FullName: + return tr("Full name"); + case IndividualMeasurementsColumns::Description: + return tr("Description"); + default: + return QString(); + } + } + else + { + const auto multisizeColumn = static_cast(column); + switch(multisizeColumn) + { + case MultisizeMeasurementsColumns::Name: + return tr("Name"); + case MultisizeMeasurementsColumns::BaseValue: + return tr("Base value"); + case MultisizeMeasurementsColumns::ShiftA: + if (m_dimensions.size() > 0) + { + MeasurementDimension_p dimension = m_dimensions.at(0); + return tr("Shift (%1):").arg(VAbstartMeasurementDimension::DimensionName(dimension->Type())); + } + else + { + return "Shift A"; + } + case MultisizeMeasurementsColumns::ShiftB: + if (m_dimensions.size() > 1) + { + MeasurementDimension_p dimension = m_dimensions.at(1); + return tr("Shift (%1):").arg(VAbstartMeasurementDimension::DimensionName(dimension->Type())); + } + else + { + return "Shift B"; + } + case MultisizeMeasurementsColumns::ShiftC: + if (m_dimensions.size() > 2) + { + MeasurementDimension_p dimension = m_dimensions.at(2); + return tr("Shift (%1):").arg(VAbstartMeasurementDimension::DimensionName(dimension->Type())); + } + else + { + return "Shift C"; + } + case MultisizeMeasurementsColumns::FullName: + return tr("Full name"); + case MultisizeMeasurementsColumns::Description: + return tr("Description"); + default: + return QString(); + } + } +} + +//--------------------------------------------------------------------------------------------------------------------- +int DialogMeasurementsCSVColumns::ImportColumnCount() const +{ + if (m_type == MeasurementsType::Individual) + { + return static_cast(IndividualMeasurementsColumns::LAST_DO_NOT_USE); + } + else + { + return static_cast(MultisizeMeasurementsColumns::LAST_DO_NOT_USE); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +int DialogMeasurementsCSVColumns::MinimumColumns() const +{ + if (m_type == MeasurementsType::Individual) + { + return 2; + } + else + { + int mandatory = 3; + + if (m_dimensions.size() > 1) + { + mandatory += qMin(m_dimensions.size(), 2); + } + + return mandatory; + } +} + +//--------------------------------------------------------------------------------------------------------------------- +bool DialogMeasurementsCSVColumns::ColumnsValid() +{ + ChangeColor(ui->labelName, OkColor(this)); + ChangeColor(ui->labelValue, OkColor(this)); + if (m_type == MeasurementsType::Multisize) + { + if (m_dimensions.size() > 0) + { + ChangeColor(ui->labelShiftA, OkColor(this)); + } + + if (m_dimensions.size() > 1) + { + ChangeColor(ui->labelShiftB, OkColor(this)); + } + + if (m_dimensions.size() > 2) + { + ChangeColor(ui->labelShiftC, OkColor(this)); + } + } + ChangeColor(ui->labelFullName, OkColor(this)); + ChangeColor(ui->labelDescription, OkColor(this)); + + auto ColumnValid = [this](int column) + { + int value = m_columnsMap.at(column); + + if (value == -1 && not ColumnMandatory(column)) + { + return true; + } + + for (int c=0; c < m_columnsMap.size(); ++c) + { + if (c == column) + { + continue; + } + + if (value == m_columnsMap.at(c)) + { + return false; + } + } + + return true; + }; + + bool columnNameFlag = true; + bool columnValueFlag = true; + bool columnShiftAFlag = true; + bool columnShiftBFlag = true; + bool columnShiftCFlag = true; + bool columnFullNameFlag = true; + bool columnDescriptionFlag = true; + + const QColor errorColor = Qt::red; + + + if (m_type == MeasurementsType::Multisize) + { + if (not ColumnValid(static_cast(MultisizeMeasurementsColumns::Name))) + { + ChangeColor(ui->labelName, errorColor); + columnNameFlag = false; + } + } + else + { + if (not ColumnValid(static_cast(IndividualMeasurementsColumns::Name))) + { + ChangeColor(ui->labelName, errorColor); + columnNameFlag = false; + } + } + + if (m_type == MeasurementsType::Multisize) + { + if (not ColumnValid(static_cast(MultisizeMeasurementsColumns::BaseValue))) + { + ChangeColor(ui->labelValue, errorColor); + columnValueFlag = false; + } + } + else + { + if (not ColumnValid(static_cast(IndividualMeasurementsColumns::Value))) + { + ChangeColor(ui->labelValue, errorColor); + columnValueFlag = false; + } + } + + if (m_type == MeasurementsType::Multisize) + { + if (m_dimensions.size() > 0) + { + if (not ColumnValid(static_cast(MultisizeMeasurementsColumns::ShiftA))) + { + ChangeColor(ui->labelShiftA, errorColor); + columnShiftAFlag = false; + } + } + + if (m_dimensions.size() > 1) + { + if (not ColumnValid(static_cast(MultisizeMeasurementsColumns::ShiftB))) + { + ChangeColor(ui->labelShiftB, errorColor); + columnShiftBFlag = false; + } + } + + if (m_dimensions.size() > 2) + { + if (not ColumnValid(static_cast(MultisizeMeasurementsColumns::ShiftC))) + { + ChangeColor(ui->labelShiftC, errorColor); + columnShiftCFlag = false; + } + } + } + + if (not ColumnValid(m_type == MeasurementsType::Multisize + ? static_cast(MultisizeMeasurementsColumns::FullName) + : static_cast(IndividualMeasurementsColumns::FullName))) + { + ChangeColor(ui->labelFullName, errorColor); + columnFullNameFlag = false; + } + + if (not ColumnValid(m_type == MeasurementsType::Multisize + ? static_cast(MultisizeMeasurementsColumns::Description) + : static_cast(IndividualMeasurementsColumns::Description))) + { + ChangeColor(ui->labelDescription, errorColor); + columnDescriptionFlag = false; + } + + return columnNameFlag && columnValueFlag && columnShiftAFlag && columnShiftBFlag && columnShiftCFlag && + columnFullNameFlag && columnDescriptionFlag; +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::InitColumnsMap() +{ + QSharedPointer csv = DialogMeasurementsCSVColumns::CSVModel(); + m_columnsMap.clear(); + + auto InitColumn = [this, csv](int column, bool forceSkip=false) + { + if (forceSkip) + { + m_columnsMap[column] = -1; + } + else + { + if (ColumnMandatory(column)) + { + m_columnsMap[column] = column; + } + else + { + m_columnsMap[column] = csv->columnCount() >= column ? column : -1; + } + } + }; + + if (m_type == MeasurementsType::Individual) + { + m_columnsMap.resize(static_cast(IndividualMeasurementsColumns::LAST_DO_NOT_USE)); + + for(int column = 0; column < static_cast(IndividualMeasurementsColumns::LAST_DO_NOT_USE); ++column) + { + InitColumn(column); + } + } + else + { + m_columnsMap.resize(static_cast(MultisizeMeasurementsColumns::LAST_DO_NOT_USE)); + + InitColumn(static_cast(MultisizeMeasurementsColumns::Name)); + InitColumn(static_cast(MultisizeMeasurementsColumns::BaseValue)); + InitColumn(static_cast(MultisizeMeasurementsColumns::ShiftA)); + InitColumn(static_cast(MultisizeMeasurementsColumns::ShiftB), m_dimensions.size() < 2); + InitColumn(static_cast(MultisizeMeasurementsColumns::ShiftC), m_dimensions.size() < 3); + InitColumn(static_cast(MultisizeMeasurementsColumns::FullName)); + InitColumn(static_cast(MultisizeMeasurementsColumns::Description)); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::InitColumnsControls() +{ + const int inputColumnCount = CSVModel()->columnCount(); + + auto InitControl = [this, inputColumnCount](QComboBox *control, int column) + { + SCASSERT(control != nullptr) + + int currentColumn = -2; + int index = control->currentIndex(); + if (index != -1) + { + currentColumn = control->currentData().toInt(); + } + + control->blockSignals(true); + control->clear(); + + for(int i=0; i < inputColumnCount; ++i) + { + control->addItem(QString::number(i+1), i); + } + + if (not ColumnMandatory(column)) + { + control->addItem(tr("Skip"), -1); + } + + control->setCurrentIndex(-1); + + index = control->findData(currentColumn); + if (index != -1) + { + control->setCurrentIndex(index); + control->blockSignals(false); + } + else + { + control->blockSignals(false); + control->setCurrentIndex(0); + } + }; + + if (m_type == MeasurementsType::Individual) + { + InitControl(ui->comboBoxName, static_cast(IndividualMeasurementsColumns::Name)); + InitControl(ui->comboBoxValue, static_cast(IndividualMeasurementsColumns::Value)); + InitControl(ui->comboBoxFullName, static_cast(IndividualMeasurementsColumns::FullName)); + InitControl(ui->comboBoxDescription, static_cast(IndividualMeasurementsColumns::Description)); + } + else + { + InitControl(ui->comboBoxName, static_cast(MultisizeMeasurementsColumns::Name)); + InitControl(ui->comboBoxValue, static_cast(MultisizeMeasurementsColumns::BaseValue)); + + if (m_dimensions.size() > 0) + { + InitControl(ui->comboBoxShiftA, static_cast(MultisizeMeasurementsColumns::ShiftA)); + } + + if (m_dimensions.size() > 1) + { + InitControl(ui->comboBoxShiftB, static_cast(MultisizeMeasurementsColumns::ShiftB)); + } + + if (m_dimensions.size() > 2) + { + InitControl(ui->comboBoxShiftC, static_cast(MultisizeMeasurementsColumns::ShiftC)); + } + + InitControl(ui->comboBoxFullName, static_cast(MultisizeMeasurementsColumns::FullName)); + InitControl(ui->comboBoxDescription, static_cast(MultisizeMeasurementsColumns::Description)); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::InitImportHeaders() +{ + if (m_fileName.isEmpty()) + { + return; + } + + const int columns = ImportColumnCount(); + + ui->tableWidgetImport->clear(); + ui->tableWidgetImport->setColumnCount(columns); + + auto AddHeader = [this](int column, bool visible=true) + { + QTableWidgetItem *header = new QTableWidgetItem(ColumnHeader(column)); + ui->tableWidgetImport->setHorizontalHeaderItem(column, header); + ui->tableWidgetImport->setColumnHidden(column, not visible); + }; + + if (m_type == MeasurementsType::Individual) + { + AddHeader(static_cast(IndividualMeasurementsColumns::Name)); + AddHeader(static_cast(IndividualMeasurementsColumns::Value)); + AddHeader(static_cast(IndividualMeasurementsColumns::FullName)); + AddHeader(static_cast(IndividualMeasurementsColumns::Description)); + } + else + { + AddHeader(static_cast(MultisizeMeasurementsColumns::Name)); + AddHeader(static_cast(MultisizeMeasurementsColumns::BaseValue)); + AddHeader(static_cast(MultisizeMeasurementsColumns::ShiftA), m_dimensions.size() > 0); + AddHeader(static_cast(MultisizeMeasurementsColumns::ShiftB), m_dimensions.size() > 1); + AddHeader(static_cast(MultisizeMeasurementsColumns::ShiftC), m_dimensions.size() > 2); + AddHeader(static_cast(MultisizeMeasurementsColumns::FullName)); + AddHeader(static_cast(MultisizeMeasurementsColumns::Description)); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +QSharedPointer DialogMeasurementsCSVColumns::CSVModel() const +{ + return QSharedPointer::create(m_fileName, nullptr, m_withHeader, m_separator, m_codec); +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::ShowInputPreview() +{ + if (m_fileName.isEmpty()) + { + return; + } + + QSharedPointer csv = DialogMeasurementsCSVColumns::CSVModel(); + + const int columns = csv->columnCount(); + const int rows = csv->rowCount(); + + ui->tableWidgetInput->clear(); + ui->tableWidgetInput->setColumnCount(columns); + ui->tableWidgetInput->setRowCount(rows); + + ui->tableWidgetInput->horizontalHeader()->setVisible(m_withHeader); + if (m_withHeader) + { + for(int column=0; columnheaderText(column)); + header->setToolTip(QString::number(column+1)); + ui->tableWidgetInput->setHorizontalHeaderItem(column, header); + } + } + + for (int row=0; row < rows; ++row) + { + for(int column=0; columntext(row, column); + QTableWidgetItem *item = new QTableWidgetItem(text); + item->setToolTip(text); + + // 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->tableWidgetInput->setItem(row, column, item); + } + } + + ui->tableWidgetInput->resizeColumnsToContents(); + ui->tableWidgetInput->resizeRowsToContents(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::ShowImportPreview() +{ + if (m_fileName.isEmpty()) + { + return; + } + + QSharedPointer csv = DialogMeasurementsCSVColumns::CSVModel(); + + const int importColumns = ImportColumnCount(); + const int columns = csv->columnCount(); + const int rows = csv->rowCount(); + + ui->tableWidgetImport->clearContents(); + ui->tableWidgetImport->setRowCount(rows); + + for (int row=0; row < rows; ++row) + { + for(int column=0; column < importColumns; ++column) + { + const int tableColumn = m_columnsMap.at(column); + if (tableColumn >= 0 && tableColumn < columns) + { + const QString text = csv->text(row, tableColumn); + QTableWidgetItem *item = new QTableWidgetItem(text); + item->setToolTip(text); + + // 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->tableWidgetImport->setItem(row, column, item); + } + } + } + + ui->tableWidgetImport->resizeColumnsToContents(); + ui->tableWidgetImport->resizeRowsToContents(); +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::HackColumnControls() +{ + if (m_type == MeasurementsType::Individual) + { + HackWidget(&ui->labelShiftA); + HackWidget(&ui->labelShiftB); + HackWidget(&ui->labelShiftC); + + HackWidget(&ui->comboBoxShiftA); + HackWidget(&ui->comboBoxShiftB); + HackWidget(&ui->comboBoxShiftC); + } + else + { + if (m_dimensions.size() < 2) + { + HackWidget(&ui->labelShiftB); + HackWidget(&ui->comboBoxShiftB); + } + + if (m_dimensions.size() < 3) + { + HackWidget(&ui->labelShiftC); + HackWidget(&ui->comboBoxShiftC); + } + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::RetranslateLabels() +{ + ui->labelName->setText(tr("Name") + "*:"); + + if (m_type == MeasurementsType::Individual) + { + ui->labelValue->setText(tr("Value") + "*:"); + } + else + { + ui->labelValue->setText(tr("Base value") + "*:"); + + if (m_dimensions.size() > 0) + { + MeasurementDimension_p dimension = m_dimensions.at(0); + ui->labelShiftA->setText(tr("Shift (%1)*:") + .arg(VAbstartMeasurementDimension::DimensionName(dimension->Type()))); + } + + if (m_dimensions.size() > 1) + { + MeasurementDimension_p dimension = m_dimensions.at(1); + ui->labelShiftB->setText(tr("Shift (%1)*:") + .arg(VAbstartMeasurementDimension::DimensionName(dimension->Type()))); + } + + if (m_dimensions.size() > 2) + { + MeasurementDimension_p dimension = m_dimensions.at(2); + ui->labelShiftC->setText(tr("Shift (%1)*:") + .arg(VAbstartMeasurementDimension::DimensionName(dimension->Type()))); + } + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::SetDefaultColumns() +{ + auto SetDefault = [this](QComboBox *control, int column) + { + SCASSERT(control != nullptr) + + int index = control->findData(m_columnsMap.at(column)); + if (index != -1) + { + control->setCurrentIndex(index); + } + }; + + if (m_type == MeasurementsType::Individual) + { + SetDefault(ui->comboBoxName, static_cast(IndividualMeasurementsColumns::Name)); + SetDefault(ui->comboBoxValue, static_cast(IndividualMeasurementsColumns::Value)); + SetDefault(ui->comboBoxFullName, static_cast(IndividualMeasurementsColumns::FullName)); + SetDefault(ui->comboBoxDescription, static_cast(IndividualMeasurementsColumns::Description)); + } + else + { + SetDefault(ui->comboBoxName, static_cast(MultisizeMeasurementsColumns::Name)); + SetDefault(ui->comboBoxValue, static_cast(MultisizeMeasurementsColumns::BaseValue)); + + if (m_dimensions.size() > 0) + { + SetDefault(ui->comboBoxShiftA, static_cast(MultisizeMeasurementsColumns::ShiftA)); + } + + if (m_dimensions.size() > 1) + { + SetDefault(ui->comboBoxShiftB, static_cast(MultisizeMeasurementsColumns::ShiftB)); + } + + if (m_dimensions.size() > 2) + { + SetDefault(ui->comboBoxShiftC, static_cast(MultisizeMeasurementsColumns::ShiftC)); + } + + SetDefault(ui->comboBoxFullName, static_cast(MultisizeMeasurementsColumns::FullName)); + SetDefault(ui->comboBoxDescription, static_cast(MultisizeMeasurementsColumns::Description)); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +void DialogMeasurementsCSVColumns::CheckStatus() +{ + auto SetStatus = [this](bool status) + { + QPushButton *bOk = ui->buttonBox->button(QDialogButtonBox::Ok); + SCASSERT(bOk != nullptr) + bOk->setEnabled(status); + }; + + if (m_fileName.isEmpty()) + { + SetStatus(false); + ui->labelStatus->setText(tr("File path is empty")); + return; + } + + QSharedPointer csv = DialogMeasurementsCSVColumns::CSVModel(); + + const int columns = csv->columnCount(); + if (columns < MinimumColumns()) + { + SetStatus(false); + ui->labelStatus->setText(tr("Not enough columns")); + return; + } + + const int rows = csv->rowCount(); + if (rows < 1) + { + SetStatus(false); + ui->labelStatus->setText(tr("Not enough data to import")); + return; + } + + if (not ColumnsValid()) + { + SetStatus(false); + ui->labelStatus->setText(tr("Please, select unique number for each column")); + return; + } + + SetStatus(true); + ui->labelStatus->setText(tr("Ready")); +} + +//--------------------------------------------------------------------------------------------------------------------- +template +void DialogMeasurementsCSVColumns::HackWidget(T **widget) +{ + delete *widget; + *widget = new T(); + m_hackedWidgets.append(*widget); +} diff --git a/src/app/tape/dialogs/dialogmeasurementscsvcolumns.h b/src/app/tape/dialogs/dialogmeasurementscsvcolumns.h new file mode 100644 index 000000000..5745476e9 --- /dev/null +++ b/src/app/tape/dialogs/dialogmeasurementscsvcolumns.h @@ -0,0 +1,149 @@ +/************************************************************************ + ** + ** @file dialogmeasurementscsvcolumns.h + ** @author Roman Telezhynskyi + ** @date 9 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 . + ** + *************************************************************************/ +#ifndef DIALOGMEASUREMENTSCSVCOLUMNS_H +#define DIALOGMEASUREMENTSCSVCOLUMNS_H + +#include + +#include "../vformat/vdimensions.h" + +class QxtCsvModel; + +enum class IndividualMeasurementsColumns: qint8 +{ + Name = 0, + Value = 1, + FullName = 2, // optional + Description = 3, // optional + LAST_DO_NOT_USE = 4 +}; + +enum class MultisizeMeasurementsColumns: qint8 +{ + Name = 0, + BaseValue = 1, + ShiftA = 2, + ShiftB = 3, // optional if not required + ShiftC = 4, // optional if not required + FullName = 5, // optional + Description = 6, // optional + LAST_DO_NOT_USE = 7 +}; + + +namespace Ui { +class DialogMeasurementsCSVColumns; +} + +class DialogMeasurementsCSVColumns : public QDialog +{ + Q_OBJECT + +public: + DialogMeasurementsCSVColumns(const QString &filename, MeasurementsType type, QWidget *parent = nullptr); + DialogMeasurementsCSVColumns(const QString &filename, MeasurementsType type, + const QList &dimensions, QWidget *parent = nullptr); + virtual ~DialogMeasurementsCSVColumns(); + + QVector ColumnsMap() const; + + void SetWithHeader(bool withHeader); + void SetSeparator(const QChar &separator); + void SetCodec(QTextCodec *codec); + +protected: + virtual void changeEvent(QEvent* event) override; + virtual void showEvent(QShowEvent *event) override; + +private slots: + void ColumnChanged(); + +private: + Q_DISABLE_COPY(DialogMeasurementsCSVColumns) + Ui::DialogMeasurementsCSVColumns *ui; + bool m_isInitialized{false}; + QString m_fileName; + bool m_withHeader{false}; + QChar m_separator{','}; + QTextCodec *m_codec{nullptr}; + QVector m_columnsMap{}; + MeasurementsType m_type; + QList m_dimensions{}; + QVector m_hackedWidgets{}; + + bool ColumnMandatory(int column) const; + QString ColumnHeader(int column) const; + int ImportColumnCount() const; + int MinimumColumns() const; + bool ColumnsValid(); + + void InitColumnsMap(); + void InitColumnsControls(); + void InitImportHeaders(); + + QSharedPointer CSVModel() const; + + void ShowInputPreview(); + void ShowImportPreview(); + + template + void HackWidget(T **widget); + void HackColumnControls(); + + void RetranslateLabels(); + + void SetDefaultColumns(); + + void CheckStatus(); +}; + +//--------------------------------------------------------------------------------------------------------------------- +inline QVector DialogMeasurementsCSVColumns::ColumnsMap() const +{ + return m_columnsMap; +} + +//--------------------------------------------------------------------------------------------------------------------- +inline void DialogMeasurementsCSVColumns::SetWithHeader(bool withHeader) +{ + m_withHeader = withHeader; +} + +//--------------------------------------------------------------------------------------------------------------------- +inline void DialogMeasurementsCSVColumns::SetSeparator(const QChar &separator) +{ + m_separator = separator; +} + +//--------------------------------------------------------------------------------------------------------------------- +inline void DialogMeasurementsCSVColumns::SetCodec(QTextCodec *codec) +{ + m_codec = codec; +} + +#endif // DIALOGMEASUREMENTSCSVCOLUMNS_H diff --git a/src/app/tape/dialogs/dialogmeasurementscsvcolumns.ui b/src/app/tape/dialogs/dialogmeasurementscsvcolumns.ui new file mode 100644 index 000000000..e3aa17d38 --- /dev/null +++ b/src/app/tape/dialogs/dialogmeasurementscsvcolumns.ui @@ -0,0 +1,204 @@ + + + DialogMeasurementsCSVColumns + + + + 0 + 0 + 908 + 703 + + + + Setup columns + + + + :/tapeicon/64x64/logo.png:/tapeicon/64x64/logo.png + + + + + + + + Preview + + + + + + Input + + + + + + false + + + + + + + + + + Import + + + + + + false + + + + + + + + + + + + + Columns + + + + 6 + + + + + Name: + + + + + + + + + + Value: + + + + + + + + + + ShiftA: + + + + + + + + + + ShiftB: + + + + + + + + + + ShiftC: + + + + + + + + + + Full name: + + + + + + + + + + Description: + + + + + + + + + + + + + + + Ready + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + DialogMeasurementsCSVColumns + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DialogMeasurementsCSVColumns + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/app/tape/tape.pri b/src/app/tape/tape.pri index f84d8133b..32d965def 100644 --- a/src/app/tape/tape.pri +++ b/src/app/tape/tape.pri @@ -3,6 +3,7 @@ SOURCES += \ $$PWD/dialogs/dialogdimensionlabels.cpp \ + $$PWD/dialogs/dialogmeasurementscsvcolumns.cpp \ $$PWD/dialogs/dialogrestrictdimension.cpp \ $$PWD/main.cpp \ $$PWD/tmainwindow.cpp \ @@ -20,6 +21,7 @@ SOURCES += \ HEADERS += \ $$PWD/dialogs/dialogdimensionlabels.h \ + $$PWD/dialogs/dialogmeasurementscsvcolumns.h \ $$PWD/dialogs/dialogrestrictdimension.h \ $$PWD/tmainwindow.h \ $$PWD/stable.h \ @@ -36,6 +38,7 @@ HEADERS += \ FORMS += \ $$PWD/dialogs/dialogdimensionlabels.ui \ + $$PWD/dialogs/dialogmeasurementscsvcolumns.ui \ $$PWD/dialogs/dialogrestrictdimension.ui \ $$PWD/tmainwindow.ui \ $$PWD/dialogs/dialogabouttape.ui \ diff --git a/src/app/tape/tmainwindow.cpp b/src/app/tape/tmainwindow.cpp index 0bf555e76..b9e172c81 100644 --- a/src/app/tape/tmainwindow.cpp +++ b/src/app/tape/tmainwindow.cpp @@ -35,6 +35,7 @@ #include "dialogs/dialogsetupmultisize.h" #include "dialogs/dialogrestrictdimension.h" #include "dialogs/dialogdimensionlabels.h" +#include "dialogs/dialogmeasurementscsvcolumns.h" #include "../vpatterndb/vcontainer.h" #include "../vpatterndb/calculator.h" #include "../vpatterndb/pmsystems.h" @@ -1020,16 +1021,34 @@ void TMainWindow::ImportDataFromCSV() qApp->Settings()->SetCSVCodec(dialog.GetSelectedMib()); qApp->Settings()->SetCSVWithHeader(dialog.IsWithHeader()); - QxtCsvModel csv(fileName, nullptr, dialog.IsWithHeader(), dialog.GetSeparator(), - QTextCodec::codecForMib(dialog.GetSelectedMib())); - - if (m->Type() == MeasurementsType::Individual) + QSharedPointer columns; + if (m->Type() == MeasurementsType::Multisize) { - ImportIndividualMeasurements(csv); + const QList dimensions = m->Dimensions().values(); + columns = QSharedPointer::create(fileName, m->Type(), dimensions, this); } else { - ImportMultisizeMeasurements(csv); + columns = QSharedPointer::create(fileName, m->Type(), this); + } + columns->SetWithHeader(dialog.IsWithHeader()); + columns->SetSeparator(dialog.GetSeparator()); + columns->SetCodec(QTextCodec::codecForMib(dialog.GetSelectedMib())); + + if (columns->exec() == QDialog::Accepted) + { + QxtCsvModel csv(fileName, nullptr, dialog.IsWithHeader(), dialog.GetSeparator(), + QTextCodec::codecForMib(dialog.GetSelectedMib())); + const QVector map = columns->ColumnsMap(); + + if (m->Type() == MeasurementsType::Individual) + { + ImportIndividualMeasurements(csv, map); + } + else + { + ImportMultisizeMeasurements(csv, map); + } } } } @@ -3740,7 +3759,7 @@ void TMainWindow::RefreshDataAfterImport() } //--------------------------------------------------------------------------------------------------------------------- -void TMainWindow::ImportIndividualMeasurements(const QxtCsvModel &csv) +void TMainWindow::ImportIndividualMeasurements(const QxtCsvModel &csv, const QVector &map) { const int columns = csv.columnCount(); const int rows = csv.rowCount(); @@ -3773,7 +3792,8 @@ void TMainWindow::ImportIndividualMeasurements(const QxtCsvModel &csv) { try { - const QString name = csv.text(i, 0).simplified(); + const int nameColumn = map.at(static_cast(IndividualMeasurementsColumns::Name)); + const QString name = csv.text(i, nameColumn).simplified(); if (name.isEmpty()) { ShowError(tr("Error in row %1. Measurement name is empty.").arg(i)); @@ -3784,17 +3804,28 @@ void TMainWindow::ImportIndividualMeasurements(const QxtCsvModel &csv) const QString mName = CheckMName(qApp->TrVars()->MFromUser(name), importedNames); importedNames.insert(mName); measurement.name = mName; - measurement.value = VTranslateVars::TryFormulaFromUser(csv.text(i, 1), qApp->Settings()->GetOsSeparator()); - const bool custom = csv.text(i, 0).simplified().startsWith(CustomMSign); + const int valueColumn = map.at(static_cast(IndividualMeasurementsColumns::Value)); + measurement.value = VTranslateVars::TryFormulaFromUser(csv.text(i, valueColumn), + qApp->Settings()->GetOsSeparator()); + + const bool custom = name.startsWith(CustomMSign); if (columns > 2 && custom) { - measurement.fullName = csv.text(i, 2).simplified(); + const int fullNameColumn = map.at(static_cast(IndividualMeasurementsColumns::FullName)); + if (fullNameColumn >= 0) + { + measurement.fullName = csv.text(i, fullNameColumn).simplified(); + } } if (columns > 3 && custom) { - measurement.description = csv.text(i, 3).simplified(); + const int descriptionColumn = map.at(static_cast(IndividualMeasurementsColumns::Description)); + if (descriptionColumn >= 0) + { + measurement.description = csv.text(i, descriptionColumn).simplified(); + } } measurements.append(measurement); @@ -3825,7 +3856,7 @@ void TMainWindow::ImportIndividualMeasurements(const QxtCsvModel &csv) } //--------------------------------------------------------------------------------------------------------------------- -void TMainWindow::ImportMultisizeMeasurements(const QxtCsvModel &csv) +void TMainWindow::ImportMultisizeMeasurements(const QxtCsvModel &csv, const QVector &map) { const int columns = csv.columnCount(); const int rows = csv.rowCount(); @@ -3838,9 +3869,10 @@ void TMainWindow::ImportMultisizeMeasurements(const QxtCsvModel &csv) auto ConverToDouble = [](QString text, const QString &error) { + text.replace(" ", QString()); text = VTranslateVars::TryFormulaFromUser(text, qApp->Settings()->GetOsSeparator()); bool ok = false; - QLocale::c(); + const qreal value = QLocale::c().toDouble(text, &ok); if (not ok) { @@ -3859,8 +3891,9 @@ void TMainWindow::ImportMultisizeMeasurements(const QxtCsvModel &csv) QString name; qreal base{0}; - qreal heightIncrease{0}; - qreal sizeIncrease{0}; + qreal shiftA{0}; + qreal shiftB{0}; + qreal shiftC{0}; QString fullName; QString description; }; @@ -3868,11 +3901,14 @@ void TMainWindow::ImportMultisizeMeasurements(const QxtCsvModel &csv) QVector measurements; QSet importedNames; + const QMap dimensions = m->Dimensions(); + for(int i=0; i < rows; ++i) { try { - const QString name = csv.text(i, 0).simplified(); + const int nameColumn = map.at(static_cast(MultisizeMeasurementsColumns::Name)); + const QString name = csv.text(i, nameColumn).simplified(); if (name.isEmpty()) { ShowError(tr("Error in row %1. Measurement name is empty.").arg(i)); @@ -3884,24 +3920,48 @@ void TMainWindow::ImportMultisizeMeasurements(const QxtCsvModel &csv) importedNames.insert(mName); measurement.name = mName; - measurement.base = ConverToDouble(csv.text(i, 1), - tr("Cannot convert base size value to double in column 2.")); + const int baseValueColumn = map.at(static_cast(MultisizeMeasurementsColumns::BaseValue)); + measurement.base = ConverToDouble(csv.text(i, baseValueColumn), + tr("Cannot convert base value to double in column 2.")); - measurement.heightIncrease = ConverToDouble(csv.text(i, 2), - tr("Cannot convert height increase value to double in column 3.")); + const int shiftAColumn = map.at(static_cast(MultisizeMeasurementsColumns::ShiftA)); + measurement.shiftA = ConverToDouble(csv.text(i, shiftAColumn), + tr("Cannot convert shift value to double in column %1.") + .arg(shiftAColumn)); - measurement.sizeIncrease = ConverToDouble(csv.text(i, 3), - tr("Cannot convert size increase value to double in column 4.")); + if (dimensions.size() > 1) + { + const int shiftBColumn = map.at(static_cast(MultisizeMeasurementsColumns::ShiftB)); + measurement.shiftB = ConverToDouble(csv.text(i, shiftBColumn), + tr("Cannot convert shift value to double in column %1.") + .arg(shiftBColumn)); + } - const bool custom = csv.text(i, 0).simplified().startsWith(CustomMSign); + if (dimensions.size() > 2) + { + const int shiftCColumn = map.at(static_cast(MultisizeMeasurementsColumns::ShiftC)); + measurement.shiftC = ConverToDouble(csv.text(i, shiftCColumn), + tr("Cannot convert shift value to double in column %1.") + .arg(shiftCColumn)); + } + + const bool custom = name.startsWith(CustomMSign); if (columns > 4 && custom) { - measurement.fullName = csv.text(i, 4).simplified(); + const int fullNameColumn = map.at(static_cast(MultisizeMeasurementsColumns::FullName)); + if (fullNameColumn >= 0) + { + measurement.fullName = csv.text(i, fullNameColumn).simplified(); + } } if (columns > 5 && custom) { - measurement.description = csv.text(i, 5).simplified(); + const int descriptionColumn = map.at(static_cast(MultisizeMeasurementsColumns::Description)); + if (descriptionColumn >= 0) + { + measurement.description = csv.text(i, descriptionColumn).simplified(); + } } measurements.append(measurement); @@ -3917,8 +3977,17 @@ void TMainWindow::ImportMultisizeMeasurements(const QxtCsvModel &csv) { m->AddEmpty(mm.name); m->SetMBaseValue(mm.name, mm.base); - m->SetMShiftB(mm.name, mm.sizeIncrease); - m->SetMShiftA(mm.name, mm.heightIncrease); + m->SetMShiftA(mm.name, mm.shiftA); + + if (dimensions.size() > 1) + { + m->SetMShiftB(mm.name, mm.shiftB); + } + + if (dimensions.size() > 2) + { + m->SetMShiftC(mm.name, mm.shiftC); + } if (not mm.fullName.isEmpty()) { diff --git a/src/app/tape/tmainwindow.h b/src/app/tape/tmainwindow.h index ec29eae4f..7d9f29f13 100644 --- a/src/app/tape/tmainwindow.h +++ b/src/app/tape/tmainwindow.h @@ -237,8 +237,8 @@ private: void ShowError(const QString &text); void RefreshDataAfterImport(); - void ImportIndividualMeasurements(const QxtCsvModel &csv); - void ImportMultisizeMeasurements(const QxtCsvModel &csv); + void ImportIndividualMeasurements(const QxtCsvModel &csv, const QVector &map); + void ImportMultisizeMeasurements(const QxtCsvModel &csv, const QVector &map); void SetCurrentPatternUnit();