From 252db1fc9fd33ec61949dcf8559391c2e790c05b Mon Sep 17 00:00:00 2001 From: brige Date: Sun, 12 Oct 2025 22:14:16 +0800 Subject: [PATCH] add workspace file entiry --- src/entities/Entity.cpp | 2 + src/translations/Dyt_zh_CN.ts | 383 +++++++++---- src/ui/MainWindow.cpp | 1 - src/ui/Menu/FileManagerMenu.cpp | 106 +++- src/ui/Menu/FileManagerMenu.h | 5 + src/ui/PropertyBrowser.cpp | 20 + src/ui/PropertyBrowser.h | 5 + src/ui/PropertyBrowser/qtpropertymanager.cpp | 505 ++++++++++++++++-- src/ui/PropertyBrowser/qtpropertymanager.h | 3 + .../PropertyBrowser/qtworkspaceattribute.cpp | 30 +- src/ui/PropertyBrowser/qtworkspaceattribute.h | 8 + src/workspace/FileEntry.h | 34 ++ src/workspace/WorkSpace.cpp | 78 +++ src/workspace/WorkSpace.h | 27 +- src/workspace/WorkSpaceXMLParse.cpp | 27 + src/workspace/WorkSpaceXMLParse.h | 1 + src/workspace/WorkSpaceXMLWrite.cpp | 18 + src/workspace/WorkSpaceXMLWrite.h | 11 +- 18 files changed, 1072 insertions(+), 192 deletions(-) create mode 100644 src/workspace/FileEntry.h diff --git a/src/entities/Entity.cpp b/src/entities/Entity.cpp index 4f12b6cb..e91420e8 100644 --- a/src/entities/Entity.cpp +++ b/src/entities/Entity.cpp @@ -1,6 +1,8 @@ #include "entities/Entity.h" #include +// Ensure QVariant can hold and convert Entity* in signals/slots +Q_DECLARE_METATYPE(Entity*) #include "common/SpdLogger.h" #include "entities/SceneComponent.h" diff --git a/src/translations/Dyt_zh_CN.ts b/src/translations/Dyt_zh_CN.ts index 0ec9c791..b1ba8005 100644 --- a/src/translations/Dyt_zh_CN.ts +++ b/src/translations/Dyt_zh_CN.ts @@ -402,30 +402,82 @@ - + Dyt (*.dyt) - + open dyt file - + Dyt (*.dyt);;All files (*.*) - + warning - + workspace is nullptr + + + + + + + + + + + + + + + + + + prompt + + + + + + + + please create workspace first + + + + + + + + up to 9 files allowed for this type + + + + + + + + file already added for this type + + + + + + + + copy file failed + + FitCurveChartView @@ -555,117 +607,117 @@ - + Wave Curve - + Speed Curve - + 3D Curve - + Target number - + Signal-to-noise ratio - + Azimuth line of sight - + Pitch gaze angle - + azimuth - + Pitch angle - + attribute - + Doppler - + course - + Speed - + longitude - + latitude - + distance - + velocity - + Radial dimensions - + Target RCS - + Report Table - + Signal Indicator Lamp - + ParamSetting - + bat File @@ -845,22 +897,23 @@ + WorkSpace - - + + Entity - + ModelBase - + color base @@ -918,6 +971,30 @@ altitude: + + + + Curve[%1] + + + + + + Surface[%1] + + + + + + Table[%1] + + + + + + Light[%1] + + QtBoolEdit @@ -938,12 +1015,12 @@ QtBoolPropertyManager - + True - + False @@ -967,22 +1044,22 @@ QtColorPropertyManager - + Red - + Green - + Blue - + Alpha @@ -990,48 +1067,48 @@ QtConeWaveComponentManager - - + + ConeWaveComponent - + Height - + Radius - + waveCount - + waveSpeed - + baseColor - + waveColor - + ringBrightAlpha - + ringDarkAlpha @@ -1137,28 +1214,28 @@ QtDashedLineComponentManager - - + + DashedLineComponent - + Start - + End - + Radius - + Color @@ -1174,17 +1251,17 @@ QtEntityPropertyManager - + Name - + Visible - + Transform @@ -1205,37 +1282,37 @@ QtFontPropertyManager - + Family - + Point Size - + Bold - + Italic - + Underline - + Strikeout - + Kerning @@ -1243,22 +1320,22 @@ QtLocalePropertyManager - + <Invalid> - + %1, %2 - + Language - + Country @@ -1266,13 +1343,13 @@ QtMeshComponetManager - - + + MeshComponent - + Mesh @@ -1280,27 +1357,27 @@ QtModelBasePropertyManager - + Name - + Description - + Inflow - + InnerBottomElevation - + Visible @@ -1321,13 +1398,13 @@ QtPathComponentManager - - + + PathComponent - + Path @@ -1335,17 +1412,17 @@ QtPointFPropertyManager - + (%1, %2) - + X - + Y @@ -1353,17 +1430,17 @@ QtPointPropertyManager - + (%1, %2) - + X - + Y @@ -1381,12 +1458,12 @@ - + [%1, %2, %3] - + [%1, %2, %3] [%4, %5, %6] [%7, %8, %9] @@ -1394,27 +1471,27 @@ QtRectFPropertyManager - + [(%1, %2), %3 x %4] - + X - + Y - + Width - + Height @@ -1422,27 +1499,27 @@ QtRectPropertyManager - + [(%1, %2), %3 x %4] - + X - + Y - + Width - + Height @@ -1450,17 +1527,17 @@ QtSizeFPropertyManager - + %1 x %2 - + Width - + Height @@ -1468,33 +1545,33 @@ QtSizePolicyPropertyManager - + <Invalid> - + [%1, %2, %3, %4] - + Horizontal Policy - + Vertical Policy - + Horizontal Stretch - + Vertical Stretch @@ -1502,17 +1579,17 @@ QtSizePropertyManager - + %1 x %2 - + Width - + Height @@ -1528,17 +1605,17 @@ QtTransfromPropertyManager - + Location - + Rotation - + Scale @@ -1559,17 +1636,17 @@ QtVec3PropertyManager - + X - + Y - + Z @@ -1577,20 +1654,90 @@ QtWorkspacePropertyManager - + Name - + Description - + Timestep + + + SimMatlab + + + + + MatlabParam + + + + + WavePath + + + + + ReportPath + + + + + RDPath + + + + + Count + + + + + Curve[%1] + + + + + Surface[%1] + + + + + Table[%1] + + + + + Light[%1] + + + + + Curves + + + + + Surfaces + + + + + Tables + + + + + Lights + + SignalIndicatorLampUI diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 08d2594c..a9dcf674 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -86,7 +86,6 @@ void MainWindow::InitUI() { addDockWidget(Qt::LeftDockWidgetArea, viewDock); m_mapDockWidget.insert("MainView", viewDock); - // 左侧模型面板,并与主视图横向并排(类似 VS 左侧工具 + 中间视图) addDockWidget(Qt::LeftDockWidgetArea, model); splitDockWidget(model, viewDock, Qt::Horizontal); diff --git a/src/ui/Menu/FileManagerMenu.cpp b/src/ui/Menu/FileManagerMenu.cpp index aff15399..653ea602 100644 --- a/src/ui/Menu/FileManagerMenu.cpp +++ b/src/ui/Menu/FileManagerMenu.cpp @@ -12,6 +12,7 @@ #include "common/SpdLogger.h" #include "workspace/WorkSpace.h" #include "workspace/WorkSpaceManager.h" +#include "workspace/FileEntry.h" #include "utils/FileUtils.h" #include "ui_FileManagerMenu.h" @@ -32,6 +33,12 @@ void FileManagerMenu::InitConnect() { connect(ui->menu_new_file, &QToolButton::clicked, this, &FileManagerMenu::NewWorkSpace); connect(ui->menu_open_file, &QToolButton::clicked, this, &FileManagerMenu::OpenWorkSpace); connect(ui->menu_save_file, &QToolButton::clicked, this, &FileManagerMenu::SaveWorkSpace); + + // add file entity buttons + connect(ui->menu_wave_file, &QToolButton::clicked, this, &FileManagerMenu::AddWaveFile); + connect(ui->menu_surface_file, &QToolButton::clicked, this, &FileManagerMenu::AddSurfaceFile); + connect(ui->menu_table_file, &QToolButton::clicked, this, &FileManagerMenu::AddTableFile); + connect(ui->menu_light_file, &QToolButton::clicked, this, &FileManagerMenu::AddLightFile); } void FileManagerMenu::NewWorkSpace() { @@ -70,19 +77,94 @@ void FileManagerMenu::SaveWorkSpace() { QString dytFile = workspace->GetPath(); LOG_INFO("save {} dyt file: {}", name.toLocal8Bit().constData(), dytFile.toLocal8Bit().constData()); - /* if (!FileUtils::IsExist(dytFile)) { - QString selfilter = tr("Dyt (*.dyt)"); - const QString workspacePath = Application::GetWorkSpacePath(); - dytFile = QFileDialog::getSaveFileName(&MainFrame::Get(), tr("save dyt file"), workspacePath, - tr("Dyt (*.dyt);;All files (*.*)"), - &selfilter); - LOG_INFO("user select file: {}", dytFile.toLocal8Bit().constData()); - if (dytFile.isEmpty()) { - return; - } - - }*/ bool success = workspace->Save(dytFile); LOG_INFO("save dyt: {}", success); } + +void FileManagerMenu::AddWaveFile() { + auto current = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == current) { + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("please create workspace first")); + return; + } + + switch (current->CreateFileEntry(FileEntryType::Curve)) { + case WorkSpace::FileEntryResult::Ok: + break; + case WorkSpace::FileEntryResult::LimitExceeded: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("up to 9 files allowed for this type")); + break; + case WorkSpace::FileEntryResult::Duplicate: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("file already added for this type")); + break; + case WorkSpace::FileEntryResult::CopyFailed: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("copy file failed")); + break; + } +} + +void FileManagerMenu::AddSurfaceFile() { + auto current = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == current) { + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("please create workspace first")); + return; + } + + switch (current->CreateFileEntry(FileEntryType::Surface)) { + case WorkSpace::FileEntryResult::Ok: + break; + case WorkSpace::FileEntryResult::LimitExceeded: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("up to 9 files allowed for this type")); + break; + case WorkSpace::FileEntryResult::Duplicate: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("file already added for this type")); + break; + case WorkSpace::FileEntryResult::CopyFailed: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("copy file failed")); + break; + } +} + +void FileManagerMenu::AddTableFile() { + auto current = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == current) { + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("please create workspace first")); + return; + } + switch (current->CreateFileEntry(FileEntryType::Table)) { + case WorkSpace::FileEntryResult::Ok: + break; + case WorkSpace::FileEntryResult::LimitExceeded: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("up to 9 files allowed for this type")); + break; + case WorkSpace::FileEntryResult::Duplicate: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("file already added for this type")); + break; + case WorkSpace::FileEntryResult::CopyFailed: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("copy file failed")); + break; + } +} + +void FileManagerMenu::AddLightFile() { + auto current = WorkSpaceManager::Get().GetCurrent(); + if (nullptr == current) { + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("please create workspace first")); + return; + } + + switch (current->CreateFileEntry(FileEntryType::Light)) { + case WorkSpace::FileEntryResult::Ok: + break; + case WorkSpace::FileEntryResult::LimitExceeded: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("up to 9 files allowed for this type")); + break; + case WorkSpace::FileEntryResult::Duplicate: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("file already added for this type")); + break; + case WorkSpace::FileEntryResult::CopyFailed: + QMessageBox::information(&MainFrame::Get(), tr("prompt"), tr("copy file failed")); + break; + } +} diff --git a/src/ui/Menu/FileManagerMenu.h b/src/ui/Menu/FileManagerMenu.h index d0948b5a..ff93612e 100644 --- a/src/ui/Menu/FileManagerMenu.h +++ b/src/ui/Menu/FileManagerMenu.h @@ -24,6 +24,11 @@ private: void OpenWorkSpace(); void SaveWorkSpace(); + void AddWaveFile(); + void AddSurfaceFile(); + void AddTableFile(); + void AddLightFile(); + private: Ui::FileManagerMenu* ui; }; \ No newline at end of file diff --git a/src/ui/PropertyBrowser.cpp b/src/ui/PropertyBrowser.cpp index db566930..2c53dbd0 100644 --- a/src/ui/PropertyBrowser.cpp +++ b/src/ui/PropertyBrowser.cpp @@ -56,6 +56,14 @@ void PropertyBrowser::OnWorkSpaceChange(const QVariant& value) { QWorkspaceAttribute worksapceAttribute(workspace); workSpaceManager_->setValue(property, worksapceAttribute); addProperty(property, tr("WorkSpace")); + + // Track and react to runtime workspace changes + if (currentWorkspace_) { + QObject::disconnect(currentWorkspace_, nullptr, this, nullptr); + } + currentWorkspace_ = workspace; + QObject::connect(currentWorkspace_, &WorkSpace::FilesChanged, + this, &PropertyBrowser::OnWorkspaceFilesChanged); } void PropertyBrowser::OnEntityChange(const QVariant& value) { @@ -152,6 +160,8 @@ void PropertyBrowser::InitPropertyManager() { browser_->setFactoryForManager(sizeManager_->subIntPropertyManager(), spinBoxFactory); browser_->setFactoryForManager(workSpaceManager_->subStringProperyManager(), lineEditFactory); browser_->setFactoryForManager(workSpaceManager_->subFilesProperyManager(), filePathFactory); + // Enable editing for grouped file entry counts + browser_->setFactoryForManager(workSpaceManager_->subIntProperyManager(), spinBoxFactory); browser_->setFactoryForManager(entityManager_->subStringProperyManager(), lineEditFactory); browser_->setFactoryForManager(entityManager_->subBoolProperyManager(), checkBoxFactory); browser_->setFactoryForManager( @@ -165,6 +175,16 @@ void PropertyBrowser::InitPropertyManager() { doubleSpinBoxFactory); } +void PropertyBrowser::OnWorkspaceFilesChanged(FileEntryType /*type*/) { + if (!currentWorkspace_) return; + auto it = idToProperty_.find(tr("WorkSpace")); + if (it == idToProperty_.end()) return; + QtProperty* property = it.value(); + QWorkspaceAttribute worksapceAttribute(currentWorkspace_); + // Refresh the full workspace property tree to reflect new counts/paths + workSpaceManager_->setValue(property, worksapceAttribute); +} + void PropertyBrowser::InitComponentPropertyManager() { QtDoubleSpinBoxFactory* doubleSpinBoxFactory = new QtDoubleSpinBoxFactory(this); QtCheckBoxFactory* checkBoxFactory = new QtCheckBoxFactory(this); diff --git a/src/ui/PropertyBrowser.h b/src/ui/PropertyBrowser.h index 770529c2..9effc06c 100644 --- a/src/ui/PropertyBrowser.h +++ b/src/ui/PropertyBrowser.h @@ -2,6 +2,7 @@ #include #include +#include "workspace/FileEntry.h" class QtProperty; @@ -17,6 +18,7 @@ public: void OnWorkSpaceChange(const QVariant& value); void OnEntityChange(const QVariant& value); + void OnWorkspaceFilesChanged(enum class FileEntryType type); void Test(); @@ -49,5 +51,8 @@ private: QMap idToExpanded_; QMap componetManager_; + + // Track current workspace for real-time refresh + class WorkSpace* currentWorkspace_{ nullptr }; }; diff --git a/src/ui/PropertyBrowser/qtpropertymanager.cpp b/src/ui/PropertyBrowser/qtpropertymanager.cpp index 7c6ca7cf..0de1686e 100644 --- a/src/ui/PropertyBrowser/qtpropertymanager.cpp +++ b/src/ui/PropertyBrowser/qtpropertymanager.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -7893,35 +7894,84 @@ void QtModelBasePropertyManager::uninitializeProperty(QtProperty* property) { #pragma region QtWorkspacePropertyManager class QtWorkspacePropertyManagerPrivate { - QtWorkspacePropertyManager* q_ptr; - Q_DECLARE_PUBLIC(QtWorkspacePropertyManager) + QtWorkspacePropertyManager* q_ptr; + Q_DECLARE_PUBLIC(QtWorkspacePropertyManager) public: - void slotStringChanged(QtProperty* property, QString value); + void slotStringChanged(QtProperty* property, QString value); + void slotIntChanged(QtProperty* property, int value); - void slotPropertyDestroyed(QtProperty* property); + void slotPropertyDestroyed(QtProperty* property); - typedef QMap PropertyValueMap; - PropertyValueMap m_values; + typedef QMap PropertyValueMap; + PropertyValueMap m_values; - QtStringPropertyManager* m_stringProperyManager; - QtFilesPropertyManager* m_filesProperyManager; + QtStringPropertyManager* m_stringProperyManager; + QtFilesPropertyManager* m_filesProperyManager; + QtIntPropertyManager* m_intProperyManager{ nullptr }; + QtGroupPropertyManager* m_groupProperyManager{ nullptr }; - QMap m_properyToName; - QMap m_properyToDescription; - QMap m_properyToTimestep; + QMap m_properyToName; + QMap m_properyToDescription; + QMap m_properyToTimestep; + QMap m_properyToSimMatlab; + QMap m_properyToMatlabParam; + QMap m_properyToWavePath; + QMap m_properyToReportPath; + QMap m_properyToRDPath; - QMap m_nameToPropery; - QMap m_descriptionToPropery; - QMap m_timestepToPropery; + QMap m_nameToPropery; + QMap m_descriptionToPropery; + QMap m_timestepToPropery; + QMap m_simMatlabToPropery; + QMap m_matlabParamToPropery; + QMap m_wavePathToPropery; + QMap m_reportPathToPropery; + QMap m_rdPathToPropery; + + // Grouped file entries: Curve + QMap m_properyToCurveGroup; + QMap m_curveGroupToPropery; + QMap m_properyToCurveCount; + QMap m_curveCountToPropery; + QMap> m_properyToCurvePaths; + QMap m_curvePathToPropery; + QMap m_curvePathIndex; + + // Surface + QMap m_properyToSurfaceGroup; + QMap m_surfaceGroupToPropery; + QMap m_properyToSurfaceCount; + QMap m_surfaceCountToPropery; + QMap> m_properyToSurfacePaths; + QMap m_surfacePathToPropery; + QMap m_surfacePathIndex; + + // Table + QMap m_properyToTableGroup; + QMap m_tableGroupToPropery; + QMap m_properyToTableCount; + QMap m_tableCountToPropery; + QMap> m_properyToTablePaths; + QMap m_tablePathToPropery; + QMap m_tablePathIndex; + + // Light + QMap m_properyToLightGroup; + QMap m_lightGroupToPropery; + QMap m_properyToLightCount; + QMap m_lightCountToPropery; + QMap> m_properyToLightPaths; + QMap m_lightPathToPropery; + QMap m_lightPathIndex; }; void QtWorkspacePropertyManagerPrivate::slotStringChanged(QtProperty* property, QString value) { - if (QtProperty* prop = m_nameToPropery.value(property, 0)) { - QWorkspaceAttribute c = m_values[prop]; - c.SetName(value); - q_ptr->setValue(prop, c); + if (QtProperty* prop = m_nameToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetName(value); + q_ptr->setValue(prop, c); } else if (QtProperty* prop = m_descriptionToPropery.value(property, 0)) { QWorkspaceAttribute c = m_values[prop]; c.SetDescription(value); @@ -7930,9 +7980,102 @@ void QtWorkspacePropertyManagerPrivate::slotStringChanged(QtProperty* property, QWorkspaceAttribute c = m_values[prop]; c.SetTimeStep(value); q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_simMatlabToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetSimMatlab(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_matlabParamToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetMatlabParam(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_wavePathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetWavePath(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_reportPathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetReportPath(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_rdPathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetRDPath(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_curvePathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + int idx = m_curvePathIndex.value(property, 0); + c.SetFileEntryPath(FileEntryType::Curve, idx, value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_surfacePathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + int idx = m_surfacePathIndex.value(property, 0); + c.SetFileEntryPath(FileEntryType::Surface, idx, value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_tablePathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + int idx = m_tablePathIndex.value(property, 0); + c.SetFileEntryPath(FileEntryType::Table, idx, value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_lightPathToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + int idx = m_lightPathIndex.value(property, 0); + c.SetFileEntryPath(FileEntryType::Light, idx, value); + q_ptr->setValue(prop, c); } } +void QtWorkspacePropertyManagerPrivate::slotIntChanged(QtProperty* property, int value) { + // Determine which type this count property belongs to + auto handleType = [&](FileEntryType type, + QMap& countToProp, + QMap>& propToPaths, + QMap& pathToProp, + QMap& pathIndex, + QMap& propToGroup) { + if (QtProperty* root = countToProp.value(property, nullptr)) { + // Adjust UI path properties to match new count + QVector& paths = propToPaths[root]; + int current = paths.size(); + // Add new path properties + if (value > current) { + QtProperty* group = propToGroup.value(root, nullptr); + for (int i = current; i < value; ++i) { + QtProperty* p = m_filesProperyManager->addProperty(); + QString title; + switch (type) { + case FileEntryType::Curve: title = QObject::tr("Curve[%1]").arg(i + 1); break; + case FileEntryType::Surface: title = QObject::tr("Surface[%1]").arg(i + 1); break; + case FileEntryType::Table: title = QObject::tr("Table[%1]").arg(i + 1); break; + case FileEntryType::Light: title = QObject::tr("Light[%1]").arg(i + 1); break; + } + p->setPropertyName(title); + group->addSubProperty(p); + paths.append(p); + pathToProp[p] = root; + pathIndex[p] = i; + } + } else if (value < current) { + // Remove excess path properties + for (int i = current - 1; i >= value; --i) { + QtProperty* p = paths.at(i); + pathIndex.remove(p); + pathToProp.remove(p); + paths.remove(i); + delete p; + } + } + // Update underlying model count + QWorkspaceAttribute c = m_values[root]; + c.SetFileEntryCount(type, value); + q_ptr->setValue(root, c); + } + }; + + handleType(FileEntryType::Curve, m_curveCountToPropery, m_properyToCurvePaths, m_curvePathToPropery, m_curvePathIndex, m_properyToCurveGroup); + handleType(FileEntryType::Surface, m_surfaceCountToPropery, m_properyToSurfacePaths, m_surfacePathToPropery, m_surfacePathIndex, m_properyToSurfaceGroup); + handleType(FileEntryType::Table, m_tableCountToPropery, m_properyToTablePaths, m_tablePathToPropery, m_tablePathIndex, m_properyToTableGroup); + handleType(FileEntryType::Light, m_lightCountToPropery, m_properyToLightPaths, m_lightPathToPropery, m_lightPathIndex, m_properyToLightGroup); +} + void QtWorkspacePropertyManagerPrivate::slotPropertyDestroyed(QtProperty* property) { if (QtProperty* subProp = m_nameToPropery.value(property, nullptr)) { m_nameToPropery[subProp] = 0; @@ -7943,23 +8086,48 @@ void QtWorkspacePropertyManagerPrivate::slotPropertyDestroyed(QtProperty* proper m_descriptionToPropery[subProp] = 0; m_descriptionToPropery.remove(property); } - - if (QtProperty* subProp = m_timestepToPropery.value(property, nullptr)) { - m_timestepToPropery[subProp] = 0; - m_timestepToPropery.remove(property); - } + + if (QtProperty* subProp = m_timestepToPropery.value(property, nullptr)) { + m_timestepToPropery[subProp] = 0; + m_timestepToPropery.remove(property); + } + + if (QtProperty* subProp = m_simMatlabToPropery.value(property, nullptr)) { + m_simMatlabToPropery[subProp] = 0; + m_simMatlabToPropery.remove(property); + } + if (QtProperty* subProp = m_matlabParamToPropery.value(property, nullptr)) { + m_matlabParamToPropery[subProp] = 0; + m_matlabParamToPropery.remove(property); + } + if (QtProperty* subProp = m_wavePathToPropery.value(property, nullptr)) { + m_wavePathToPropery[subProp] = 0; + m_wavePathToPropery.remove(property); + } + if (QtProperty* subProp = m_reportPathToPropery.value(property, nullptr)) { + m_reportPathToPropery[subProp] = 0; + m_reportPathToPropery.remove(property); + } + if (QtProperty* subProp = m_rdPathToPropery.value(property, nullptr)) { + m_rdPathToPropery[subProp] = 0; + m_rdPathToPropery.remove(property); + } } QtWorkspacePropertyManager::QtWorkspacePropertyManager(QObject* parent) - : QtAbstractPropertyManager(parent), d_ptr(new QtWorkspacePropertyManagerPrivate) { - d_ptr->q_ptr = this; + : QtAbstractPropertyManager(parent), d_ptr(new QtWorkspacePropertyManagerPrivate) { + d_ptr->q_ptr = this; - d_ptr->m_stringProperyManager = new QtStringPropertyManager(this); - connect(d_ptr->m_stringProperyManager, SIGNAL(valueChanged(QtProperty*, QString)), - this, SLOT(slotStringChanged(QtProperty*, QString))); + d_ptr->m_stringProperyManager = new QtStringPropertyManager(this); + connect(d_ptr->m_stringProperyManager, SIGNAL(valueChanged(QtProperty*, QString)), + this, SLOT(slotStringChanged(QtProperty*, QString))); d_ptr->m_filesProperyManager = new QtFilesPropertyManager(this); connect(d_ptr->m_filesProperyManager, SIGNAL(valueChanged(QtProperty*, QString)), this, SLOT(slotStringChanged(QtProperty*, QString))); + d_ptr->m_intProperyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intProperyManager, SIGNAL(valueChanged(QtProperty*, int)), + this, SLOT(slotIntChanged(QtProperty*, int))); + d_ptr->m_groupProperyManager = new QtGroupPropertyManager(this); } /*! @@ -7982,11 +8150,19 @@ QWorkspaceAttribute QtWorkspacePropertyManager::value(const QtProperty* property } QtStringPropertyManager* QtWorkspacePropertyManager::subStringProperyManager() const { - return d_ptr->m_stringProperyManager; + return d_ptr->m_stringProperyManager; } QtFilesPropertyManager* QtWorkspacePropertyManager::subFilesProperyManager() const { - return d_ptr->m_filesProperyManager; + return d_ptr->m_filesProperyManager; +} + +QtIntPropertyManager* QtWorkspacePropertyManager::subIntProperyManager() const { + return d_ptr->m_intProperyManager; +} + +QtGroupPropertyManager* QtWorkspacePropertyManager::subGroupProperyManager() const { + return d_ptr->m_groupProperyManager; } /*! @@ -8019,30 +8195,88 @@ QIcon QtWorkspacePropertyManager::valueIcon(const QtProperty* property) const { \sa value(), valueChanged() */ void QtWorkspacePropertyManager::setValue(QtProperty* property, const QWorkspaceAttribute& value) { - const QtWorkspacePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); - if (it == d_ptr->m_values.end()) - return; + const QtWorkspacePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; - if (it.value() == value) - return; + if (it.value() == value) + return; - it.value() = value; + it.value() = value; - d_ptr->m_stringProperyManager->setValue(d_ptr->m_properyToName[property], value.GetName()); - d_ptr->m_stringProperyManager->setValue(d_ptr->m_properyToDescription[property], value.GetDescription()); - d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToTimestep[property], value.GetTimeStep()); + d_ptr->m_stringProperyManager->setValue(d_ptr->m_properyToName[property], value.GetName()); + d_ptr->m_stringProperyManager->setValue(d_ptr->m_properyToDescription[property], value.GetDescription()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToTimestep[property], value.GetTimeStep()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToSimMatlab[property], value.GetSimMatlab()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToMatlabParam[property], value.GetMatlabParam()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToWavePath[property], value.GetWavePath()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToReportPath[property], value.GetReportPath()); + d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToRDPath[property], value.GetRDPath()); - emit propertyChanged(property); - emit valueChanged(property, value); + auto syncGroup = [&](FileEntryType type, + QMap& propToGroup, + QMap& propToCount, + QMap>& propToPaths, + QMap& pathToProp, + QMap& pathIndex) { + QtProperty* group = propToGroup.value(property, nullptr); + if (!group) return; + auto entries = value.GetFileEntries(type); + int count = static_cast(entries.size()); + // update count without triggering slot + QtProperty* countProp = propToCount.value(property, nullptr); + if (countProp) d_ptr->m_intProperyManager->setValueOnly(countProp, count); + QVector& paths = propToPaths[property]; + int current = paths.size(); + // expand + if (count > current) { + for (int i = current; i < count; ++i) { + QtProperty* p = d_ptr->m_filesProperyManager->addProperty(); + QString title; + switch (type) { + case FileEntryType::Curve: title = QObject::tr("Curve[%1]").arg(i + 1); break; + case FileEntryType::Surface: title = QObject::tr("Surface[%1]").arg(i + 1); break; + case FileEntryType::Table: title = QObject::tr("Table[%1]").arg(i + 1); break; + case FileEntryType::Light: title = QObject::tr("Light[%1]").arg(i + 1); break; + } + p->setPropertyName(title); + group->addSubProperty(p); + paths.append(p); + pathToProp[p] = property; + pathIndex[p] = i; + } + } else if (count < current) { + for (int i = current - 1; i >= count; --i) { + QtProperty* p = paths.at(i); + pathIndex.remove(p); + pathToProp.remove(p); + paths.remove(i); + delete p; + } + } + // set values + for (int i = 0; i < count; ++i) { + const QString absPath = value.GetFileEntryAbsPath(type, i); + d_ptr->m_filesProperyManager->setValueOnly(paths.at(i), absPath); + } + }; + + syncGroup(FileEntryType::Curve, d_ptr->m_properyToCurveGroup, d_ptr->m_properyToCurveCount, d_ptr->m_properyToCurvePaths, d_ptr->m_curvePathToPropery, d_ptr->m_curvePathIndex); + syncGroup(FileEntryType::Surface, d_ptr->m_properyToSurfaceGroup, d_ptr->m_properyToSurfaceCount, d_ptr->m_properyToSurfacePaths, d_ptr->m_surfacePathToPropery, d_ptr->m_surfacePathIndex); + syncGroup(FileEntryType::Table, d_ptr->m_properyToTableGroup, d_ptr->m_properyToTableCount, d_ptr->m_properyToTablePaths, d_ptr->m_tablePathToPropery, d_ptr->m_tablePathIndex); + syncGroup(FileEntryType::Light, d_ptr->m_properyToLightGroup, d_ptr->m_properyToLightCount, d_ptr->m_properyToLightPaths, d_ptr->m_lightPathToPropery, d_ptr->m_lightPathIndex); + + emit propertyChanged(property); + emit valueChanged(property, value); } /*! \reimp */ void QtWorkspacePropertyManager::initializeProperty(QtProperty* property) { - QWorkspaceAttribute val; - d_ptr->m_values[property] = val; + QWorkspaceAttribute val; + d_ptr->m_values[property] = val; QtProperty* prop = d_ptr->m_stringProperyManager->addProperty(); prop->setPropertyName(tr("Name")); @@ -8064,18 +8298,113 @@ void QtWorkspacePropertyManager::initializeProperty(QtProperty* property) { d_ptr->m_properyToTimestep[property] = prop; d_ptr->m_timestepToPropery[prop] = property; property->addSubProperty(prop); + + prop = d_ptr->m_filesProperyManager->addProperty(); + prop->setPropertyName(tr("SimMatlab")); + d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetSimMatlab()); + d_ptr->m_properyToSimMatlab[property] = prop; + d_ptr->m_simMatlabToPropery[prop] = property; + property->addSubProperty(prop); + + prop = d_ptr->m_filesProperyManager->addProperty(); + prop->setPropertyName(tr("MatlabParam")); + d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetMatlabParam()); + d_ptr->m_properyToMatlabParam[property] = prop; + d_ptr->m_matlabParamToPropery[prop] = property; + property->addSubProperty(prop); + + prop = d_ptr->m_filesProperyManager->addProperty(); + prop->setPropertyName(tr("WavePath")); + d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetWavePath()); + d_ptr->m_properyToWavePath[property] = prop; + d_ptr->m_wavePathToPropery[prop] = property; + property->addSubProperty(prop); + + prop = d_ptr->m_filesProperyManager->addProperty(); + prop->setPropertyName(tr("ReportPath")); + d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetReportPath()); + d_ptr->m_properyToReportPath[property] = prop; + d_ptr->m_reportPathToPropery[prop] = property; + property->addSubProperty(prop); + + prop = d_ptr->m_filesProperyManager->addProperty(); + prop->setPropertyName(tr("RDPath")); + d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetRDPath()); + d_ptr->m_properyToRDPath[property] = prop; + d_ptr->m_rdPathToPropery[prop] = property; + property->addSubProperty(prop); + + // Add grouped file sections + auto addGroup = [&](FileEntryType type, const QString& groupName, + QMap& propToGroup, + QMap& groupToProp, + QMap& propToCount, + QMap& countToProp, + QMap>& propToPaths, + QMap& pathToProp, + QMap& pathIndex) { + QtProperty* group = d_ptr->m_groupProperyManager->addProperty(); + group->setPropertyName(groupName); + property->addSubProperty(group); + propToGroup[property] = group; + groupToProp[group] = property; + + // Count property + QtProperty* countProp = d_ptr->m_intProperyManager->addProperty(); + countProp->setPropertyName(tr("Count")); + d_ptr->m_intProperyManager->setRange(countProp, 0, 1024); + // initial count from workspace + int initialCount = static_cast(val.GetFileEntries(type).size()); + d_ptr->m_intProperyManager->setValueOnly(countProp, initialCount); + propToCount[property] = countProp; + countToProp[countProp] = property; + group->addSubProperty(countProp); + + // initial paths + auto entries = val.GetFileEntries(type); + QVector& paths = propToPaths[property]; + for (int i = 0; i < static_cast(entries.size()); ++i) { + QtProperty* p = d_ptr->m_filesProperyManager->addProperty(); + QString title; + switch (type) { + case FileEntryType::Curve: title = tr("Curve[%1]").arg(i + 1); break; + case FileEntryType::Surface: title = tr("Surface[%1]").arg(i + 1); break; + case FileEntryType::Table: title = tr("Table[%1]").arg(i + 1); break; + case FileEntryType::Light: title = tr("Light[%1]").arg(i + 1); break; + } + p->setPropertyName(title); + d_ptr->m_filesProperyManager->setValueOnly(p, val.GetFileEntryAbsPath(type, i)); + group->addSubProperty(p); + paths.append(p); + pathToProp[p] = property; + pathIndex[p] = i; + } + }; + + addGroup(FileEntryType::Curve, tr("Curves"), d_ptr->m_properyToCurveGroup, d_ptr->m_curveGroupToPropery, + d_ptr->m_properyToCurveCount, d_ptr->m_curveCountToPropery, + d_ptr->m_properyToCurvePaths, d_ptr->m_curvePathToPropery, d_ptr->m_curvePathIndex); + addGroup(FileEntryType::Surface, tr("Surfaces"), d_ptr->m_properyToSurfaceGroup, d_ptr->m_surfaceGroupToPropery, + d_ptr->m_properyToSurfaceCount, d_ptr->m_surfaceCountToPropery, + d_ptr->m_properyToSurfacePaths, d_ptr->m_surfacePathToPropery, d_ptr->m_surfacePathIndex); + addGroup(FileEntryType::Table, tr("Tables"), d_ptr->m_properyToTableGroup, d_ptr->m_tableGroupToPropery, + d_ptr->m_properyToTableCount, d_ptr->m_tableCountToPropery, + d_ptr->m_properyToTablePaths, d_ptr->m_tablePathToPropery, d_ptr->m_tablePathIndex); + addGroup(FileEntryType::Light, tr("Lights"), d_ptr->m_properyToLightGroup, d_ptr->m_lightGroupToPropery, + d_ptr->m_properyToLightCount, d_ptr->m_lightCountToPropery, + d_ptr->m_properyToLightPaths, d_ptr->m_lightPathToPropery, d_ptr->m_lightPathIndex); } /*! \reimp */ void QtWorkspacePropertyManager::uninitializeProperty(QtProperty* property) { - QtProperty* prop = d_ptr->m_nameToPropery[property]; - if (prop) { - d_ptr->m_nameToPropery.remove(prop); - delete prop; - } - d_ptr->m_properyToName.remove(property); + QtProperty* prop = d_ptr->m_nameToPropery[property]; + if (prop) { + d_ptr->m_nameToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToName.remove(property); prop = d_ptr->m_descriptionToPropery[property]; if (prop) { @@ -8090,6 +8419,84 @@ void QtWorkspacePropertyManager::uninitializeProperty(QtProperty* property) { delete prop; } d_ptr->m_properyToTimestep.remove(property); + + prop = d_ptr->m_simMatlabToPropery[property]; + if (prop) { + d_ptr->m_simMatlabToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToSimMatlab.remove(property); + + prop = d_ptr->m_matlabParamToPropery[property]; + if (prop) { + d_ptr->m_matlabParamToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToMatlabParam.remove(property); + + prop = d_ptr->m_wavePathToPropery[property]; + if (prop) { + d_ptr->m_wavePathToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToWavePath.remove(property); + + prop = d_ptr->m_reportPathToPropery[property]; + if (prop) { + d_ptr->m_reportPathToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToReportPath.remove(property); + + prop = d_ptr->m_rdPathToPropery[property]; + if (prop) { + d_ptr->m_rdPathToPropery.remove(prop); + delete prop; + } + d_ptr->m_properyToRDPath.remove(property); + + // Cleanup grouped file properties + auto cleanupGroup = [&](QMap& propToGroup, + QMap& groupToProp, + QMap& propToCount, + QMap& countToProp, + QMap>& propToPaths, + QMap& pathToProp, + QMap& pathIndex) { + QtProperty* group = propToGroup.value(property, nullptr); + if (group) { + propToGroup.remove(property); + groupToProp.remove(group); + } + QtProperty* countProp = propToCount.value(property, nullptr); + if (countProp) { + countToProp.remove(countProp); + propToCount.remove(property); + delete countProp; + } + QVector& paths = propToPaths[property]; + for (QtProperty* p : paths) { + pathIndex.remove(p); + pathToProp.remove(p); + delete p; + } + paths.clear(); + propToPaths.remove(property); + if (group) delete group; + }; + + cleanupGroup(d_ptr->m_properyToCurveGroup, d_ptr->m_curveGroupToPropery, + d_ptr->m_properyToCurveCount, d_ptr->m_curveCountToPropery, + d_ptr->m_properyToCurvePaths, d_ptr->m_curvePathToPropery, d_ptr->m_curvePathIndex); + cleanupGroup(d_ptr->m_properyToSurfaceGroup, d_ptr->m_surfaceGroupToPropery, + d_ptr->m_properyToSurfaceCount, d_ptr->m_surfaceCountToPropery, + d_ptr->m_properyToSurfacePaths, d_ptr->m_surfacePathToPropery, d_ptr->m_surfacePathIndex); + cleanupGroup(d_ptr->m_properyToTableGroup, d_ptr->m_tableGroupToPropery, + d_ptr->m_properyToTableCount, d_ptr->m_tableCountToPropery, + d_ptr->m_properyToTablePaths, d_ptr->m_tablePathToPropery, d_ptr->m_tablePathIndex); + cleanupGroup(d_ptr->m_properyToLightGroup, d_ptr->m_lightGroupToPropery, + d_ptr->m_properyToLightCount, d_ptr->m_lightCountToPropery, + d_ptr->m_properyToLightPaths, d_ptr->m_lightPathToPropery, d_ptr->m_lightPathIndex); } #pragma endregion diff --git a/src/ui/PropertyBrowser/qtpropertymanager.h b/src/ui/PropertyBrowser/qtpropertymanager.h index 1c9edfee..c1fabc4e 100644 --- a/src/ui/PropertyBrowser/qtpropertymanager.h +++ b/src/ui/PropertyBrowser/qtpropertymanager.h @@ -1027,6 +1027,8 @@ public: QtStringPropertyManager* subStringProperyManager() const; QtFilesPropertyManager* subFilesProperyManager() const; + QtIntPropertyManager* subIntProperyManager() const; + QtGroupPropertyManager* subGroupProperyManager() const; public Q_SLOTS: void setValue(QtProperty* property, const QWorkspaceAttribute& val); @@ -1042,6 +1044,7 @@ private: Q_DECLARE_PRIVATE(QtWorkspacePropertyManager) Q_DISABLE_COPY_MOVE(QtWorkspacePropertyManager) Q_PRIVATE_SLOT(d_func(), void slotStringChanged(QtProperty*, QString)) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty*, int)) }; #pragma endregion diff --git a/src/ui/PropertyBrowser/qtworkspaceattribute.cpp b/src/ui/PropertyBrowser/qtworkspaceattribute.cpp index 1b1ddc29..f374558d 100644 --- a/src/ui/PropertyBrowser/qtworkspaceattribute.cpp +++ b/src/ui/PropertyBrowser/qtworkspaceattribute.cpp @@ -163,7 +163,7 @@ void QWorkspaceAttribute::SetRDPath(const QString& path) workspace_->SetRDPath(path); } -const QString QWorkspaceAttribute::GetRDPath() const +const QString QWorkspaceAttribute::GetRDPath() const { if (nullptr == workspace_) { return ""; @@ -171,6 +171,34 @@ const QString QWorkspaceAttribute::GetRDPath() const return workspace_->GetRDPath(); } +std::vector QWorkspaceAttribute::GetFileEntries(FileEntryType type) const { + if (nullptr == workspace_) { + return {}; + } + return workspace_->GetFileEntries(type); +} + +void QWorkspaceAttribute::SetFileEntryCount(FileEntryType type, int count) { + if (nullptr == workspace_) { + return; + } + workspace_->SetFileEntryCount(type, count); +} + +void QWorkspaceAttribute::SetFileEntryPath(FileEntryType type, int index, const QString& path) { + if (nullptr == workspace_) { + return; + } + workspace_->SetFileEntryPath(type, index, path); +} + +QString QWorkspaceAttribute::GetFileEntryAbsPath(FileEntryType type, int index) const { + if (nullptr == workspace_) { + return QString(); + } + return workspace_->GetFileEntryAbsPath(type, index); +} + QTransformAttribute::QTransformAttribute(class Transform* obj) : object_(obj) { diff --git a/src/ui/PropertyBrowser/qtworkspaceattribute.h b/src/ui/PropertyBrowser/qtworkspaceattribute.h index 7cd45748..e274565b 100644 --- a/src/ui/PropertyBrowser/qtworkspaceattribute.h +++ b/src/ui/PropertyBrowser/qtworkspaceattribute.h @@ -43,6 +43,8 @@ #include #include #include +#include "workspace/FileEntry.h" +#include class QWorkspaceAttribute { public: @@ -76,6 +78,12 @@ public: void SetRDPath(const QString& path); const QString GetRDPath() const; + + // Grouped files API + std::vector GetFileEntries(FileEntryType type) const; + void SetFileEntryCount(FileEntryType type, int count); + void SetFileEntryPath(FileEntryType type, int index, const QString& path); + QString GetFileEntryAbsPath(FileEntryType type, int index) const; private: class WorkSpace* workspace_{ nullptr }; }; diff --git a/src/workspace/FileEntry.h b/src/workspace/FileEntry.h new file mode 100644 index 00000000..51495ff6 --- /dev/null +++ b/src/workspace/FileEntry.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +enum class FileEntryType { + Curve, + Surface, + Table, + Light +}; + +struct FileEntry { + FileEntryType type; + QString fileName; // relative file name under workspace dir; may be empty +}; + +inline const char* FileEntryTypeToString(FileEntryType t) { + switch (t) { + case FileEntryType::Curve: return "curve"; + case FileEntryType::Surface: return "surface"; + case FileEntryType::Table: return "table"; + case FileEntryType::Light: return "light"; + } + return "unknown"; +} + +inline bool FileEntryTypeFromString(const char* s, FileEntryType& out) { + if (!s) return false; + if (0 == strcmp(s, "curve")) { out = FileEntryType::Curve; return true; } + if (0 == strcmp(s, "surface")) { out = FileEntryType::Surface; return true; } + if (0 == strcmp(s, "table")) { out = FileEntryType::Table; return true; } + if (0 == strcmp(s, "light")) { out = FileEntryType::Light; return true; } + return false; +} \ No newline at end of file diff --git a/src/workspace/WorkSpace.cpp b/src/workspace/WorkSpace.cpp index e78296d9..72cc419b 100644 --- a/src/workspace/WorkSpace.cpp +++ b/src/workspace/WorkSpace.cpp @@ -119,6 +119,84 @@ void WorkSpace::SetRDPath(const QString& path) rdFile_ = fileInfo.fileName(); } +std::vector WorkSpace::GetFileEntries(FileEntryType type) const { + auto it = files_.find(type); + if (it == files_.end()) { + return {}; + } + return it->second; +} + +WorkSpace::FileEntryResult WorkSpace::CreateFileEntry(FileEntryType type) { + auto& vec = files_[type]; + if (vec.size() >= 9) { + return FileEntryResult::LimitExceeded; + } + // push a placeholder; actual filename may be set elsewhere via SetWavePath/SetRDPath/etc + vec.push_back(FileEntry{ type, QString() }); + // Notify listeners (e.g., PropertyBrowser) to refresh workspace properties + emit FilesChanged(type); + return FileEntryResult::Ok; +} + +bool WorkSpace::SetFileEntryCount(FileEntryType type, int count) { + if (count < 0) count = 0; + if (count > 9) count = 9; + auto& vec = files_[type]; + if (static_cast(vec.size()) == count) { + return true; + } + if (static_cast(vec.size()) < count) { + int toAdd = count - static_cast(vec.size()); + for (int i = 0; i < toAdd; ++i) { + vec.push_back(FileEntry{ type, QString() }); + } + } else { + vec.resize(count); + } + emit FilesChanged(type); + return true; +} + +bool WorkSpace::SetFileEntryPath(FileEntryType type, int index, const QString& path) { + auto& vec = files_[type]; + if (index < 0 || index >= static_cast(vec.size())) { + return false; + } + QFileInfo fileInfo(path); + if (!fileInfo.exists()) { + return false; + } + QString dirPath = QString("%1/%2").arg(GetDir(), fileInfo.fileName()); + bool sucess = FileUtils::CopyFileToPath(path, dirPath, true); + LOG_INFO("copy grouped file {}: {} to {}", + path.toLocal8Bit().data(), + dirPath.toLocal8Bit().data(), + sucess); + if (!sucess) { + return false; + } + vec[index].fileName = fileInfo.fileName(); + emit FilesChanged(type); + return true; +} + +QString WorkSpace::GetFileEntryAbsPath(FileEntryType type, int index) const { + auto it = files_.find(type); + if (it == files_.end()) { + return QString(); + } + const auto& vec = it->second; + if (index < 0 || index >= static_cast(vec.size())) { + return QString(); + } + const QString& name = vec[index].fileName; + if (name.isEmpty()) { + return QString(); + } + return QString("%1/%2").arg(GetDir(), name); +} + const QString WorkSpace::GetRDPath() const { QString path = QString("%1/%2").arg(GetDir(), rdFile_); diff --git a/src/workspace/WorkSpace.h b/src/workspace/WorkSpace.h index ff39f11a..39a7f1eb 100644 --- a/src/workspace/WorkSpace.h +++ b/src/workspace/WorkSpace.h @@ -9,6 +9,7 @@ #include "scene/OEScene.h" #include "config.h" #include "common/SpdLogger.h" +#include "workspace/FileEntry.h" //#include "../ui/chartPlot/DYTChart.h" @@ -69,6 +70,16 @@ public: void SetRDPath(const QString& path); const QString GetRDPath() const; + // Files list API (per-type, max 9 per type) + enum class FileEntryResult { Ok, LimitExceeded, Duplicate, CopyFailed }; + FileEntryResult CreateFileEntry(FileEntryType type); + std::vector GetFileEntries(FileEntryType type) const; + + // Manage grouped file entries + bool SetFileEntryCount(FileEntryType type, int count); + bool SetFileEntryPath(FileEntryType type, int index, const QString& path); + QString GetFileEntryAbsPath(FileEntryType type, int index) const; + inline void SetHomeViewpoint(const osgEarth::Viewpoint& viewpoint) { homeViewpoint_ = viewpoint; homeViewpoint_.setHeading(0.0); // Ensure heading is set to 0.0 @@ -123,10 +134,12 @@ public: void OnLoaded(); Q_SIGNALS: - void EntityAdded(class Entity* entity); - void EntityRemoved(class Entity* entity); - void TimestepChanged(class Timestep* timestep); - void LampStatusChanged(class LampStatus* lampStatus); + void EntityAdded(class Entity* entity); + void EntityRemoved(class Entity* entity); + void TimestepChanged(class Timestep* timestep); + void LampStatusChanged(class LampStatus* lampStatus); + // Emitted when grouped file entries change (count or path or creation) + void FilesChanged(FileEntryType type); protected: const QString& GetSimMatlabName() const { @@ -152,8 +165,10 @@ private: std::vector entities_; OEScene* scene_{ nullptr }; class Timestep* timestep_{ nullptr }; - class LampStatus* lampStatus_{ nullptr }; - class Entity* trackedEntity_{ nullptr }; + class LampStatus* lampStatus_{ nullptr }; + class Entity* trackedEntity_{ nullptr }; + // Stored as file entries under workspace dir, keyed by type + std::map> files_; friend class WorkSpaceXMLWrite; }; diff --git a/src/workspace/WorkSpaceXMLParse.cpp b/src/workspace/WorkSpaceXMLParse.cpp index e3321cfc..f4089e24 100644 --- a/src/workspace/WorkSpaceXMLParse.cpp +++ b/src/workspace/WorkSpaceXMLParse.cpp @@ -84,6 +84,30 @@ bool WorkSpaceXMLParse::ParseLamp(const tinyxml2::XMLElement* element) { return workSpace_->SetLampPath(path); } +bool WorkSpaceXMLParse::ParseFiles(const tinyxml2::XMLElement* element) { + if (nullptr == element) { + LOG_WARN("element is nullptr"); + return false; + } + + const tinyxml2::XMLElement* typeElement = element->FirstChildElement("type"); + while (nullptr != typeElement) { + const char* name = typeElement->Attribute("name"); + int count = 0; + typeElement->QueryIntAttribute("count", &count); + if (nullptr != name && count > 0) { + FileEntryType enumType; + if (FileEntryTypeFromString(name, enumType)) { + for (int i = 0; i < count; ++i) { + workSpace_->CreateFileEntry(enumType); + } + } + } + typeElement = typeElement->NextSiblingElement("type"); + } + return true; +} + bool WorkSpaceXMLParse::ParseEntities(const tinyxml2::XMLElement* element) { if (nullptr == element) { LOG_WARN("element is nullptr"); @@ -238,6 +262,9 @@ bool WorkSpaceXMLParse::Load(const QString& dyt) { else if (0 == strcmp(name, "SimMatlab")) { ParseSimMatlab(xmlElement); } + else if (0 == strcmp(name, "files")) { + ParseFiles(xmlElement); + } xmlElement = xmlElement->NextSiblingElement(); } diff --git a/src/workspace/WorkSpaceXMLParse.h b/src/workspace/WorkSpaceXMLParse.h index 60f4b468..626af5b2 100644 --- a/src/workspace/WorkSpaceXMLParse.h +++ b/src/workspace/WorkSpaceXMLParse.h @@ -35,6 +35,7 @@ private: bool ParseChart(const tinyxml2::XMLElement* element); bool ParseReport(const tinyxml2::XMLElement* element); bool ParseSimMatlab(const tinyxml2::XMLElement* element); + bool ParseFiles(const tinyxml2::XMLElement* element); private: QString name_; diff --git a/src/workspace/WorkSpaceXMLWrite.cpp b/src/workspace/WorkSpaceXMLWrite.cpp index 917be532..dc4e2625 100644 --- a/src/workspace/WorkSpaceXMLWrite.cpp +++ b/src/workspace/WorkSpaceXMLWrite.cpp @@ -10,6 +10,7 @@ #include "utils/StringUtils.h" #include "workspace/WorkSpaceManager.h" +#include "workspace/FileEntry.h" WorkSpaceXMLWrite::WorkSpaceXMLWrite(WorkSpace* workspace, QObject* parent) noexcept : QObject(parent) @@ -35,6 +36,7 @@ bool WorkSpaceXMLWrite::Save(const QString& path) { SaveChart(scene, &doc); SaveTimeStep(scene); SaveLamp(scene); + SaveFiles(scene, &doc); tinyxml2::XMLElement* entitiesXml = scene->InsertNewChildElement("entities"); std::vector& entities = workSpace_->GetEntities(); @@ -120,3 +122,19 @@ bool WorkSpaceXMLWrite::SaveChart(tinyxml2::XMLElement* scene, tinyxml2::XMLDocu return true; } + +bool WorkSpaceXMLWrite::SaveFiles(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc) { + // Persist multi-file entries per type as counts + tinyxml2::XMLElement* files = doc->NewElement("files"); + scene->LinkEndChild(files); + + for (const auto& kv : workSpace_->files_) { + const FileEntryType type = kv.first; + const auto& vec = kv.second; + tinyxml2::XMLElement* typeElem = doc->NewElement("type"); + typeElem->SetAttribute("name", FileEntryTypeToString(type)); + typeElem->SetAttribute("count", static_cast(vec.size())); + files->LinkEndChild(typeElem); + } + return true; +} diff --git a/src/workspace/WorkSpaceXMLWrite.h b/src/workspace/WorkSpaceXMLWrite.h index c3cf6ab9..2e3785e8 100644 --- a/src/workspace/WorkSpaceXMLWrite.h +++ b/src/workspace/WorkSpaceXMLWrite.h @@ -16,11 +16,12 @@ public: bool Save(const QString& path); protected: - bool SaveScene(tinyxml2::XMLElement* scene); - bool SaveTimeStep(tinyxml2::XMLElement* scene); - bool SaveLamp(tinyxml2::XMLElement* scene); - bool SaveEntities(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc); - bool SaveChart(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc); + bool SaveScene(tinyxml2::XMLElement* scene); + bool SaveTimeStep(tinyxml2::XMLElement* scene); + bool SaveLamp(tinyxml2::XMLElement* scene); + bool SaveEntities(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc); + bool SaveChart(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc); + bool SaveFiles(tinyxml2::XMLElement* scene, tinyxml2::XMLDocument* doc); private: WorkSpace* workSpace_;