#include "AddCurveFileDlg.h" #include #include #include #include #include #include "ui_AddCurveFileDlg.h" AddCurveFileDlg::AddCurveFileDlg(QWidget* parent) : BaseAddFileDlg(FileEntryType::Curve, parent) , ui(new Ui::AddCurveFileDlg) , currentCurveIndex_(-1) , selectedColor_(255, 0, 0) { // Default to red color SetupUI(ui); SetTitle(getDialogTitle()); setupSpecificUI(); setupConnections(); } AddCurveFileDlg::~AddCurveFileDlg() { delete ui; } void AddCurveFileDlg::setupSpecificUI() { // Initialize curve properties group as disabled enableCurveProperties(false); // Initialize color preview updateColorPreview(selectedColor_); // Add a default curve CurveProperties defaultCurve; defaultCurve.name = generateCurveName(); defaultCurve.color = generateCurveColor(); defaultCurve.start = 1; defaultCurve.stop = 241; curves_.append(defaultCurve); addCurveToList(defaultCurve); // Select the first curve if (ui->curveListWidget->count() > 0) { ui->curveListWidget->setCurrentRow(0); onCurveSelectionChanged(); } } void AddCurveFileDlg::setupConnections() { // File selection connections connect(ui->selectFileBtn, &QToolButton::clicked, this, &AddCurveFileDlg::onSelectFileClicked); connect(ui->filePathEdit, &QLineEdit::textChanged, this, &AddCurveFileDlg::onFilePathChanged); // Data format connections connect(ui->separatorComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &AddCurveFileDlg::onDelimiterChanged); connect(ui->hasHeaderCheckBox, &QCheckBox::toggled, this, &AddCurveFileDlg::onHeaderToggled); // Curve management connections connect(ui->addCurveBtn, &QPushButton::clicked, this, &AddCurveFileDlg::onAddCurveClicked); connect(ui->removeCurveBtn, &QPushButton::clicked, this, &AddCurveFileDlg::onRemoveCurveClicked); connect(ui->curveListWidget, &QListWidget::currentRowChanged, this, &AddCurveFileDlg::onCurveSelectionChanged); // Curve properties connections connect(ui->colorButton, &QPushButton::clicked, this, &AddCurveFileDlg::onColorButtonClicked); connect(ui->curveNameEdit, &QLineEdit::textChanged, this, &AddCurveFileDlg::onCurveNameChanged); connect(ui->dataStartSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &AddCurveFileDlg::onCurveDataChanged); connect(ui->dataStopSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &AddCurveFileDlg::onCurveDataChanged); // Dialog buttons connect(ui->addBtn, &QPushButton::clicked, this, &AddCurveFileDlg::onSure); connect(ui->cancelBtn, &QPushButton::clicked, this, &QDialog::reject); } void AddCurveFileDlg::onSelectFileClicked() { QString fileName = QFileDialog::getOpenFileName( this, getDialogTitle(), QString(), getFileFilter() ); if (!fileName.isEmpty()) { ui->filePathEdit->setText(fileName); updateFileInfo(fileName); } } void AddCurveFileDlg::updateFileInfo(const QString& filePath) { QFileInfo fileInfo(filePath); if (fileInfo.exists()) { ui->fileNameValue->setText(fileInfo.fileName()); ui->fileSizeValue->setText(QString::number(fileInfo.size()) + " bytes"); } else { ui->fileNameValue->setText("-"); ui->fileSizeValue->setText("-"); } } void AddCurveFileDlg::onFilePathChanged() { QString filePath = ui->filePathEdit->text(); if (!filePath.isEmpty()) { updateFileInfo(filePath); } } void AddCurveFileDlg::onAddCurveClicked() { // Save current curve properties if any curve is selected if (currentCurveIndex_ >= 0) { saveCurveProperties(); } // Create new curve with default properties CurveProperties newCurve; newCurve.name = generateCurveName(); newCurve.color = generateCurveColor(); newCurve.start = 1; newCurve.stop = 241; // Add to curves list and UI curves_.append(newCurve); addCurveToList(newCurve); // Select the new curve ui->curveListWidget->setCurrentRow(curves_.size() - 1); } void AddCurveFileDlg::onRemoveCurveClicked() { int currentRow = ui->curveListWidget->currentRow(); if (currentRow < 0 || currentRow >= curves_.size()) { return; } // Don't allow removing the last curve if (curves_.size() <= 1) { QMessageBox::information(this, "Information", "At least one curve must remain."); return; } // Remove from curves list and UI curves_.removeAt(currentRow); delete ui->curveListWidget->takeItem(currentRow); // Update current index if (currentRow >= curves_.size()) { currentRow = curves_.size() - 1; } if (currentRow >= 0) { ui->curveListWidget->setCurrentRow(currentRow); } else { currentCurveIndex_ = -1; clearCurveProperties(); enableCurveProperties(false); } } void AddCurveFileDlg::onCurveSelectionChanged() { int currentRow = ui->curveListWidget->currentRow(); // Save previous curve properties if (currentCurveIndex_ >= 0 && currentCurveIndex_ < curves_.size()) { saveCurveProperties(); } currentCurveIndex_ = currentRow; if (currentRow >= 0 && currentRow < curves_.size()) { // Load selected curve properties updateCurveProperties(); enableCurveProperties(true); } else { clearCurveProperties(); enableCurveProperties(false); } } void AddCurveFileDlg::onCurveNameChanged() { if (currentCurveIndex_ >= 0 && currentCurveIndex_ < curves_.size()) { QString newName = ui->curveNameEdit->text(); curves_[currentCurveIndex_].name = newName; // Update list item text QListWidgetItem* item = ui->curveListWidget->item(currentCurveIndex_); if (item) { item->setText(QString("%1 [%2,%3] (%4,%5,%6)") .arg(newName) .arg(curves_[currentCurveIndex_].start) .arg(curves_[currentCurveIndex_].stop) .arg(curves_[currentCurveIndex_].color.red()) .arg(curves_[currentCurveIndex_].color.green()) .arg(curves_[currentCurveIndex_].color.blue())); } } } void AddCurveFileDlg::onCurveDataChanged() { if (currentCurveIndex_ >= 0 && currentCurveIndex_ < curves_.size()) { curves_[currentCurveIndex_].start = ui->dataStartSpinBox->value(); curves_[currentCurveIndex_].stop = ui->dataStopSpinBox->value(); // Update list item text QListWidgetItem* item = ui->curveListWidget->item(currentCurveIndex_); if (item) { item->setText(QString("%1 [%2,%3] (%4,%5,%6)") .arg(curves_[currentCurveIndex_].name) .arg(curves_[currentCurveIndex_].start) .arg(curves_[currentCurveIndex_].stop) .arg(curves_[currentCurveIndex_].color.red()) .arg(curves_[currentCurveIndex_].color.green()) .arg(curves_[currentCurveIndex_].color.blue())); } } } void AddCurveFileDlg::onColorButtonClicked() { if (currentCurveIndex_ >= 0 && currentCurveIndex_ < curves_.size()) { QColor color = QColorDialog::getColor(curves_[currentCurveIndex_].color, this, "Select Curve Color"); if (color.isValid()) { curves_[currentCurveIndex_].color = color; selectedColor_ = color; updateColorPreview(color); // Update list item text QListWidgetItem* item = ui->curveListWidget->item(currentCurveIndex_); if (item) { item->setText(QString("%1 [%2,%3] (%4,%5,%6)") .arg(curves_[currentCurveIndex_].name) .arg(curves_[currentCurveIndex_].start) .arg(curves_[currentCurveIndex_].stop) .arg(color.red()) .arg(color.green()) .arg(color.blue())); } } } } void AddCurveFileDlg::addCurveToList(const CurveProperties& curve) { QString itemText = QString("%1 [%2,%3] (%4,%5,%6)") .arg(curve.name) .arg(curve.start) .arg(curve.stop) .arg(curve.color.red()) .arg(curve.color.green()) .arg(curve.color.blue()); ui->curveListWidget->addItem(itemText); } void AddCurveFileDlg::updateCurveProperties() { if (currentCurveIndex_ >= 0 && currentCurveIndex_ < curves_.size()) { const CurveProperties& curve = curves_[currentCurveIndex_]; ui->curveNameEdit->setText(curve.name); ui->dataStartSpinBox->setValue(curve.start); ui->dataStopSpinBox->setValue(curve.stop); selectedColor_ = curve.color; updateColorPreview(curve.color); } } void AddCurveFileDlg::saveCurveProperties() { if (currentCurveIndex_ >= 0 && currentCurveIndex_ < curves_.size()) { curves_[currentCurveIndex_].name = ui->curveNameEdit->text(); curves_[currentCurveIndex_].start = ui->dataStartSpinBox->value(); curves_[currentCurveIndex_].stop = ui->dataStopSpinBox->value(); curves_[currentCurveIndex_].color = selectedColor_; } } void AddCurveFileDlg::clearCurveProperties() { ui->curveNameEdit->clear(); ui->dataStartSpinBox->setValue(1); ui->dataStopSpinBox->setValue(241); selectedColor_ = QColor(255, 0, 0); updateColorPreview(selectedColor_); } void AddCurveFileDlg::enableCurveProperties(bool enabled) { ui->curvePropertiesGroupBox->setEnabled(enabled); } QString AddCurveFileDlg::generateCurveName() { return tr("Curve %1").arg(curves_.size() + 1); } QColor AddCurveFileDlg::generateCurveColor() const { // Generate different colors for each curve static const QColor colors[] = { QColor(255, 0, 0), // Red QColor(0, 255, 0), // Green QColor(0, 0, 255), // Blue QColor(255, 255, 0), // Yellow QColor(255, 0, 255), // Magenta QColor(0, 255, 255), // Cyan QColor(255, 128, 0), // Orange QColor(128, 0, 255), // Purple }; int colorIndex = curves_.size() % (sizeof(colors) / sizeof(colors[0])); return colors[colorIndex]; } void AddCurveFileDlg::updateColorPreview(const QColor& color) { QString styleSheet = QString("background-color: rgb(%1, %2, %3); border: 1px solid black;") .arg(color.red()) .arg(color.green()) .arg(color.blue()); ui->colorPreview->setStyleSheet(styleSheet); } bool AddCurveFileDlg::validateSpecificParams() { // File path validation if (ui->filePathEdit->text().isEmpty()) { QMessageBox::warning(this, tr("Validation Error"), tr("Please select a data file.")); return false; } // File existence validation QFileInfo fileInfo(ui->filePathEdit->text()); if (!fileInfo.exists()) { QMessageBox::warning(this, tr("Validation Error"), tr("Selected file does not exist.")); return false; } // File readability validation if (!fileInfo.isReadable()) { QMessageBox::warning(this, tr("Validation Error"), tr("Selected file is not readable. Please check file permissions.")); return false; } // File size validation (avoid memory issues with large files) if (fileInfo.size() > 100 * 1024 * 1024) { // 100MB limit QMessageBox::warning(this, tr("Validation Error"), tr("File is too large (over 100MB). Please select a smaller file.")); return false; } // Curve count validation if (curves_.isEmpty()) { QMessageBox::warning(this, tr("Validation Error"), tr("At least one curve must be defined.")); return false; } // Save current curve properties if (currentCurveIndex_ >= 0) { saveCurveProperties(); } // Curve name uniqueness validation QStringList curveNames; for (int i = 0; i < curves_.size(); ++i) { const CurveProperties& curve = curves_[i]; if (curve.name.isEmpty()) { QMessageBox::warning(this, tr("Validation Error"), tr("Curve %1 name cannot be empty.").arg(i + 1)); return false; } if (curveNames.contains(curve.name)) { QMessageBox::warning(this, tr("Validation Error"), tr("Curve name '%1' is duplicated. Please use different names.").arg(curve.name)); return false; } curveNames.append(curve.name); // Curve name length validation if (curve.name.length() > 50) { QMessageBox::warning(this, tr("Validation Error"), tr("Curve name '%1' is too long. Please limit to 50 characters.").arg(curve.name)); return false; } // Data range validation if (curve.start < 1 || curve.stop < 1) { QMessageBox::warning(this, tr("Validation Error"), tr("Curve '%1' start and stop values must be greater than 0.").arg(curve.name)); return false; } if (curve.start > curve.stop) { QMessageBox::warning(this, tr("Validation Error"), tr("Curve '%1' start value cannot be greater than stop value.").arg(curve.name)); return false; } // Data range reasonableness validation if (curve.stop - curve.start < 1) { QMessageBox::warning(this, tr("Validation Error"), tr("Curve '%1' data range is too small. At least 2 data points are required.").arg(curve.name)); return false; } if (curve.stop > 1000000) { QMessageBox::warning(this, tr("Validation Error"), tr("Curve '%1' stop value is too large. Please ensure it does not exceed 1000000.").arg(curve.name)); return false; } } // Chart properties validation if (ui->chartNameEdit->text().isEmpty()) { QMessageBox::warning(this, tr("Validation Error"), tr("Chart name cannot be empty.")); return false; } if (ui->chartNameEdit->text().length() > 100) { QMessageBox::warning(this, tr("Validation Error"), tr("Chart name is too long. Please limit to 100 characters.")); return false; } // Axis title validation if (ui->xTitleEdit->text().length() > 50) { QMessageBox::warning(this, tr("Validation Error"), tr("X axis title is too long. Please limit to 50 characters.")); return false; } if (ui->yTitleEdit->text().length() > 50) { QMessageBox::warning(this, tr("Validation Error"), tr("Y axis title is too long. Please limit to 50 characters.")); return false; } // Axis range validation double xMin = ui->xMinSpinBox->value(); double xMax = ui->xMaxSpinBox->value(); double yMin = ui->yMinSpinBox->value(); double yMax = ui->yMaxSpinBox->value(); if (xMin >= xMax) { QMessageBox::warning(this, tr("Validation Error"), tr("X axis minimum value must be less than maximum value.")); return false; } if (yMin >= yMax) { QMessageBox::warning(this, tr("Validation Error"), tr("Y axis minimum value must be less than maximum value.")); return false; } // Data column validation int xColumn = ui->xColumnSpinBox->value(); int yColumn = ui->yColumnSpinBox->value(); if (xColumn == yColumn) { QMessageBox::warning(this, tr("Validation Error"), tr("X column and Y column cannot be the same.")); return false; } if (xColumn < 1 || yColumn < 1) { QMessageBox::warning(this, tr("Validation Error"), tr("Data column indices must be greater than 0.")); return false; } // Time parameter validation double timeParam = ui->timeParamSpinBox->value(); if (timeParam < 0) { QMessageBox::warning(this, tr("Validation Error"), tr("Time parameter cannot be negative.")); return false; } // X axis tick count validation int xTickCount = ui->xCountSpinBox->value(); if (xTickCount < 2) { QMessageBox::warning(this, tr("Validation Error"), tr("X axis tick count must be at least 2.")); return false; } // Description length validation if (ui->descriptionEdit->toPlainText().length() > 500) { QMessageBox::warning(this, tr("Validation Error"), tr("Description is too long. Please limit to 500 characters.")); return false; } return true; } QString AddCurveFileDlg::getFileFilter() const { return "Data Files (*.txt *.csv *.dat);;All Files (*.*)"; } QString AddCurveFileDlg::getDialogTitle() const { return "Add Curve Data File"; } AddCurveFileDlg::CurveParams AddCurveFileDlg::getCurveParams() const { CurveParams params; params.chart = getChartProperties(); params.curves = getCurveProperties(); params.format = getDataFormatParams(); return params; } AddCurveFileDlg::ChartProperties AddCurveFileDlg::getChartProperties() const { ChartProperties chart; chart.name = ui->chartNameEdit->text(); chart.path = ui->filePathEdit->text(); chart.xTitle = ui->xTitleEdit->text(); chart.yTitle = ui->yTitleEdit->text(); chart.xMin = ui->xMinSpinBox->value(); chart.xMax = ui->xMaxSpinBox->value(); chart.xCount = ui->xCountSpinBox->value(); chart.yMin = ui->yMinSpinBox->value(); chart.yMax = ui->yMaxSpinBox->value(); chart.timeParam = ui->timeParamSpinBox->value(); return chart; } QList AddCurveFileDlg::getCurveProperties() const { return curves_; } AddCurveFileDlg::DataFormatParams AddCurveFileDlg::getDataFormatParams() const { DataFormatParams format; // Get delimiter based on combo box selection QString delimiterText = ui->separatorComboBox->currentText(); if (delimiterText.contains("Comma")) { format.delimiter = ","; } else if (delimiterText.contains("Tab")) { format.delimiter = "\t"; } else if (delimiterText.contains("Space")) { format.delimiter = " "; } else if (delimiterText.contains("Semicolon")) { format.delimiter = ";"; } else { format.delimiter = ","; // Default } format.hasHeader = ui->hasHeaderCheckBox->isChecked(); format.xColumn = ui->xColumnSpinBox->value(); format.yColumn = ui->yColumnSpinBox->value(); format.description = ui->descriptionEdit->toPlainText(); return format; } void AddCurveFileDlg::onDelimiterChanged() { // This slot can be used for future delimiter-related logic } void AddCurveFileDlg::onHeaderToggled(bool hasHeader) { // This slot can be used for future header-related logic } void AddCurveFileDlg::onSure() { if (validateSpecificParams()) { accept(); } }