#include "AddCurveFileDlg.h" #include #include #include #include #include #include "workspace/WorkSpace.h" #include "workspace/WorkSpaceManager.h" #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()); // Initialize chart type combo box ui->chartTypeComboBox->addItem("Wave", static_cast(ChartType::Wave)); ui->chartTypeComboBox->addItem("Report", static_cast(ChartType::Report)); ui->chartTypeComboBox->setCurrentIndex(0); // Default to Wave // Initialize chart properties with default values chartProperties_.chartType = ChartType::Wave; setupConnections(); updateCurvePropertiesUI(); // Initialize UI based on default chart type } AddCurveFileDlg::~AddCurveFileDlg() { delete ui; } void AddCurveFileDlg::setupConnections() { // File selection connections connect(ui->selectFileBtn, &QToolButton::clicked, this, &AddCurveFileDlg::OnSelectFile); // Chart type connection connect(ui->chartTypeComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &AddCurveFileDlg::onChartTypeChanged); // 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); connect(ui->curveListWidget, &QListWidget::itemClicked, this, &AddCurveFileDlg::onCurveListWidgetItemClicked); // 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); connect(ui->xValueSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AddCurveFileDlg::onCurveDataChanged); connect(ui->yValueSpinBox, QOverload::of(&QDoubleSpinBox::valueChanged), this, &AddCurveFileDlg::onCurveDataChanged); // Dialog buttons connect(ui->addBtn, &QPushButton::clicked, this, &AddCurveFileDlg::onSure); connect(ui->cancelBtn, &QPushButton::clicked, this, &QDialog::reject); } void AddCurveFileDlg::updateFileInfo(const QString& filePath) { QFileInfo fileInfo(filePath); if (fileInfo.exists()) { ui->fileNameValue->setText(fileInfo.fileName()); qint64 size = fileInfo.size(); QString sizeText; if (size < 1024) { sizeText = QString("%1 B").arg(size); } else if (size < 1024 * 1024) { sizeText = QString("%1 KB").arg(size / 1024.0, 0, 'f', 1); } else { sizeText = QString("%1 MB").arg(size / (1024.0 * 1024.0), 0, 'f', 1); } ui->fileSizeValue->setText(sizeText); } else { ui->fileNameValue->setText("-"); ui->fileSizeValue->setText("-"); } ui->filePathEdit->setText(filePath); } void AddCurveFileDlg::onAddCurveClicked() { // Save current curve properties if any curve is selected if (currentCurveIndex_ >= 0) { saveCurveProperties(); } // Create new curve with default properties based on chart type FileEntryCurve::CurveProperty newCurve; newCurve.name = generateCurveName(); newCurve.color = generateCurveColor(); // Set default values based on chart type if (chartProperties_.chartType == ChartType::Wave) { newCurve.data.wave.start = 1; newCurve.data.wave.stop = 241; } else { newCurve.data.report.x = 0.0; newCurve.data.report.y = 0.0; } // Add to curves list and UI curves_.append(newCurve); // Add to UI list widget with appropriate display format QString displayText; if (chartProperties_.chartType == ChartType::Wave) { displayText = QString("%1 [%2,%3] (%4,%5,%6)") .arg(newCurve.name) .arg(newCurve.data.wave.start) .arg(newCurve.data.wave.stop) .arg(newCurve.color.red()) .arg(newCurve.color.green()) .arg(newCurve.color.blue()); } else { displayText = QString("%1 (%.2f,%.2f) (%4,%5,%6)") .arg(newCurve.name) .arg(newCurve.data.report.x) .arg(newCurve.data.report.y) .arg(newCurve.color.red()) .arg(newCurve.color.green()) .arg(newCurve.color.blue()); } QListWidgetItem* item = new QListWidgetItem(displayText); ui->curveListWidget->addItem(item); ++currentCurveIndex_; // 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() <= 0) { 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::onCurveListWidgetItemClicked(QListWidgetItem* item) { if (!item) { return; } int clickedIndex = ui->curveListWidget->row(item); if (clickedIndex == currentCurveIndex_) { ui->curveNameEdit->setText(curves_[currentCurveIndex_].name); if (chartProperties_.chartType == ChartType::Wave) { ui->dataStartSpinBox->setValue(curves_[currentCurveIndex_].data.wave.start); ui->dataStopSpinBox->setValue(curves_[currentCurveIndex_].data.wave.stop); } else { ui->xValueSpinBox->setValue(curves_[currentCurveIndex_].data.report.x); ui->yValueSpinBox->setValue(curves_[currentCurveIndex_].data.report.y); } updateColorPreview(curves_[currentCurveIndex_].color); enableCurveProperties(true); ui->curveNameEdit->setFocus(); ui->curveNameEdit->selectAll(); } else { onCurveSelectionChanged(); } } 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 with appropriate format QListWidgetItem* item = ui->curveListWidget->item(currentCurveIndex_); if (item) { QString displayText; if (chartProperties_.chartType == ChartType::Wave) { displayText = QString("%1 [%2,%3] (%4,%5,%6)") .arg(newName) .arg(curves_[currentCurveIndex_].data.wave.start) .arg(curves_[currentCurveIndex_].data.wave.stop) .arg(curves_[currentCurveIndex_].color.red()) .arg(curves_[currentCurveIndex_].color.green()) .arg(curves_[currentCurveIndex_].color.blue()); } else { displayText = QString("%1 (%.2f,%.2f) (%4,%5,%6)") .arg(newName) .arg(curves_[currentCurveIndex_].data.report.x) .arg(curves_[currentCurveIndex_].data.report.y) .arg(curves_[currentCurveIndex_].color.red()) .arg(curves_[currentCurveIndex_].color.green()) .arg(curves_[currentCurveIndex_].color.blue()); } item->setText(displayText); } } } void AddCurveFileDlg::onCurveDataChanged() { if (currentCurveIndex_ >= 0 && currentCurveIndex_ < curves_.size()) { // Update curve data based on chart type if (chartProperties_.chartType == ChartType::Wave) { curves_[currentCurveIndex_].data.wave.start = ui->dataStartSpinBox->value(); curves_[currentCurveIndex_].data.wave.stop = ui->dataStopSpinBox->value(); } else { curves_[currentCurveIndex_].data.report.x = ui->xValueSpinBox->value(); curves_[currentCurveIndex_].data.report.y = ui->yValueSpinBox->value(); } // Update display text in list widget QListWidgetItem* item = ui->curveListWidget->item(currentCurveIndex_); if (item) { QString itemText; if (chartProperties_.chartType == ChartType::Wave) { itemText = QString("%1 [%2,%3] (%4,%5,%6)") .arg(curves_[currentCurveIndex_].name) .arg(curves_[currentCurveIndex_].data.wave.start) .arg(curves_[currentCurveIndex_].data.wave.stop) .arg(curves_[currentCurveIndex_].color.red()) .arg(curves_[currentCurveIndex_].color.green()) .arg(curves_[currentCurveIndex_].color.blue()); } else { itemText = QString("%1 (%2,%3) (%4,%5,%6)") .arg(curves_[currentCurveIndex_].name) .arg(curves_[currentCurveIndex_].data.report.x) .arg(curves_[currentCurveIndex_].data.report.y) .arg(curves_[currentCurveIndex_].color.red()) .arg(curves_[currentCurveIndex_].color.green()) .arg(curves_[currentCurveIndex_].color.blue()); } item->setText(itemText); } } } 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) { QString itemText; if (chartProperties_.chartType == ChartType::Wave) { itemText = QString("%1 [%2,%3] (%4,%5,%6)") .arg(curves_[currentCurveIndex_].name) .arg(curves_[currentCurveIndex_].data.wave.start) .arg(curves_[currentCurveIndex_].data.wave.stop) .arg(color.red()) .arg(color.green()) .arg(color.blue()); } else { itemText = QString("%1 (%2,%3) (%4,%5,%6)") .arg(curves_[currentCurveIndex_].name) .arg(curves_[currentCurveIndex_].data.report.x) .arg(curves_[currentCurveIndex_].data.report.y) .arg(color.red()) .arg(color.green()) .arg(color.blue()); } item->setText(itemText); } } } } void AddCurveFileDlg::addCurveToList(const FileEntryCurve::CurveProperty& curve) { QString itemText; if (chartProperties_.chartType == ChartType::Wave) { itemText = QString("%1 [%2,%3] (%4,%5,%6)") .arg(curve.name) .arg(curve.data.wave.start) .arg(curve.data.wave.stop) .arg(curve.color.red()) .arg(curve.color.green()) .arg(curve.color.blue()); } else { itemText = QString("%1 (%2,%3) (%4,%5,%6)") .arg(curve.name) .arg(curve.data.report.x) .arg(curve.data.report.y) .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 FileEntryCurve::CurveProperty& curve = curves_[currentCurveIndex_]; ui->curveNameEdit->setText(curve.name); // Update properties based on chart type if (chartProperties_.chartType == ChartType::Wave) { ui->dataStartSpinBox->setValue(curve.data.wave.start); ui->dataStopSpinBox->setValue(curve.data.wave.stop); } else { ui->xValueSpinBox->setValue(curve.data.report.x); ui->yValueSpinBox->setValue(curve.data.report.y); } selectedColor_ = curve.color; updateColorPreview(curve.color); } } void AddCurveFileDlg::saveCurveProperties() { if (currentCurveIndex_ >= 0 && currentCurveIndex_ < curves_.size()) { curves_[currentCurveIndex_].name = ui->curveNameEdit->text(); // Save properties based on chart type if (chartProperties_.chartType == ChartType::Wave) { curves_[currentCurveIndex_].data.wave.start = ui->dataStartSpinBox->value(); curves_[currentCurveIndex_].data.wave.stop = ui->dataStopSpinBox->value(); } else { curves_[currentCurveIndex_].data.report.x = ui->xValueSpinBox->value(); curves_[currentCurveIndex_].data.report.y = ui->yValueSpinBox->value(); } curves_[currentCurveIndex_].color = selectedColor_; } } void AddCurveFileDlg::clearCurveProperties() { ui->curveNameEdit->clear(); // Clear properties based on chart type if (chartProperties_.chartType == ChartType::Wave) { ui->dataStartSpinBox->setValue(1); ui->dataStopSpinBox->setValue(241); } else { ui->xValueSpinBox->setValue(0.0); ui->yValueSpinBox->setValue(0.0); } 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 const QString& selectFilePath = getSelectedFilePath(); if (selectFilePath.isEmpty()) { QMessageBox::warning(this, tr("Validation Error"), tr("Please select a data file.")); return false; } // File existence validation QFileInfo fileInfo(selectFilePath); 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 FileEntryCurve::CurveProperty& 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 based on chart type if (chartProperties_.chartType == ChartType::Wave) { if (curve.data.wave.start < 1 || curve.data.wave.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.data.wave.start > curve.data.wave.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.data.wave.stop - curve.data.wave.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.data.wave.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; } } else { // Report type validation - ensure x and y values are reasonable if (curve.data.report.x < -1000000 || curve.data.report.x > 1000000) { QMessageBox::warning(this, tr("Validation Error"), tr("Curve '%1' X value is out of range. Please ensure it is between -1000000 and 1000000.").arg(curve.name)); return false; } if (curve.data.report.y < -1000000 || curve.data.report.y > 1000000) { QMessageBox::warning(this, tr("Validation Error"), tr("Curve '%1' Y value is out of range. Please ensure it is between -1000000 and 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; } // 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; } return true; } QString AddCurveFileDlg::getFileFilter() const { return "Data Files (*.txt *.csv *.dat);;All Files (*.*)"; } QString AddCurveFileDlg::getDialogTitle() const { return "Add Curve Data File"; } void AddCurveFileDlg::onSure() { if (validateSpecificParams()) { // Create FileEntryCurve object using factory function auto fileEntryCurve = CreateFileEntryCurve(getSelectedFilePath()); if (!fileEntryCurve) { QMessageBox::warning(this, tr("Error"), tr("Failed to create file entry")); return; } // Set curve properties fileEntryCurve->SetName(ui->chartNameEdit->text()); // Set chart properties FileEntryCurve::ChartProperties chartProps; chartProps.chartType = chartProperties_.chartType; // Set chart type chartProps.xCount = ui->xCountSpinBox->value(); chartProps.yCount = ui->yCountSpinBox->value(); chartProps.xTitle = ui->xTitleEdit->text(); chartProps.yTitle = ui->yTitleEdit->text(); chartProps.xMin = ui->xMinSpinBox->value(); chartProps.xMax = ui->xMaxSpinBox->value(); chartProps.yMin = ui->yMinSpinBox->value(); chartProps.yMax = ui->yMaxSpinBox->value(); chartProps.timeParam = ui->timeParamSpinBox->value(); fileEntryCurve->SetChartProperties(chartProps); // Add curve properties for (const auto& curve : curves_) { fileEntryCurve->AddCurveProperty(curve); } // Get current workspace WorkSpace* workspace = WorkSpaceManager::Get().GetCurrent(); if (!workspace) { QMessageBox::warning(this, tr("Error"), tr("Unable to get current workspace")); return; } // Add FileEntryCurve to workspace using new SetFileEntry method auto result = workspace->SetFileEntry(fileEntryCurve); if (result != WorkSpace::FileEntryResult::Ok) { QString errorMsg; switch (result) { case WorkSpace::FileEntryResult::LimitExceeded: errorMsg = tr("Curve file count has reached the limit (9 files)"); break; case WorkSpace::FileEntryResult::Duplicate: errorMsg = tr("File already exists"); break; case WorkSpace::FileEntryResult::CopyFailed: errorMsg = tr("File copy failed"); break; case WorkSpace::FileEntryResult::InvalidFile: errorMsg = tr("Invalid file"); break; default: errorMsg = tr("Failed to add file"); break; } QMessageBox::warning(this, tr("Error"), errorMsg); return; } accept(); } } void AddCurveFileDlg::onChartTypeChanged() { // Save current curve properties if any curve is selected if (currentCurveIndex_ >= 0) { saveCurveProperties(); } // Update chart type based on combo box selection int selectedIndex = ui->chartTypeComboBox->currentIndex(); ChartType newChartType = static_cast(ui->chartTypeComboBox->itemData(selectedIndex).toInt()); chartProperties_.chartType = newChartType; // Update UI to show appropriate curve property controls updateCurvePropertiesUI(); // Update current curve properties display if a curve is selected if (currentCurveIndex_ >= 0) { updateCurveProperties(); } } void AddCurveFileDlg::updateCurvePropertiesUI() { bool isWaveType = (chartProperties_.chartType == ChartType::Wave); // Show/hide Wave type controls (Data Start/Stop) ui->dataStartLabel->setVisible(isWaveType); ui->dataStartSpinBox->setVisible(isWaveType); ui->dataStopLabel->setVisible(isWaveType); ui->dataStopSpinBox->setVisible(isWaveType); // Show/hide Report type controls (X/Y Value) ui->xValueLabel->setVisible(!isWaveType); ui->xValueSpinBox->setVisible(!isWaveType); ui->yValueLabel->setVisible(!isWaveType); ui->yValueSpinBox->setVisible(!isWaveType); }