From 3be753a6a7e4493ab9b4f7b918a349f014e84a01 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Fri, 3 May 2019 16:55:58 +0200 Subject: [PATCH] Allow to update model directories --- src/blackcore/db/databaseutils.cpp | 50 +++++++++++++ src/blackcore/db/databaseutils.h | 4 + src/blackgui/menus/aircraftmodelmenus.cpp | 73 +++++++++++++++++-- src/blackgui/menus/aircraftmodelmenus.h | 6 +- src/blackmisc/simulation/aircraftmodel.cpp | 27 +++++-- src/blackmisc/simulation/aircraftmodel.h | 19 +++-- .../simulation/aircraftmodellist.cpp | 4 +- .../fscommon/aircraftcfgentries.cpp | 2 +- src/blackmisc/simulation/simulatedaircraft.h | 2 +- 9 files changed, 163 insertions(+), 24 deletions(-) diff --git a/src/blackcore/db/databaseutils.cpp b/src/blackcore/db/databaseutils.cpp index ebaecf7bf..322be0c94 100644 --- a/src/blackcore/db/databaseutils.cpp +++ b/src/blackcore/db/databaseutils.cpp @@ -200,6 +200,56 @@ namespace BlackCore return consolidatedModels; } + CAircraftModelList CDatabaseUtils::updateModelsDirectoriesAllowsGuiRefresh(const CAircraftModelList &models, const CAircraftModelList &simulatorModels, QStringList &removedModelStrings, bool processEvents) + { + if (models.isEmpty() || simulatorModels.isEmpty()) { return models; } + + QTime timer; + timer.start(); + const QSet allOwnModelsModelStrings = simulatorModels.getModelStringSet(); + CAircraftModelList consolidatedModels; + removedModelStrings.clear(); + + int c = 0; + for (const CAircraftModel &model : models) + { + c++; + if (processEvents && c % 125 == 0) + { + if (!sApp || sApp->isShuttingDown()) { return models; } + sApp->processEventsFor(25); + } + + const QString ms(model.getModelString()); + if (ms.isEmpty()) { continue; } + if (!allOwnModelsModelStrings.contains(ms)) + { + removedModelStrings.push_back(ms); + continue; + } + CAircraftModel consolidated = simulatorModels.findFirstByModelStringAliasOrDefault(ms); + if (consolidated.hasModelString()) + { + if (consolidated.hasExistingCorrespondingFile()) + { + // update by existing one + consolidatedModels.push_back(consolidated); + } + else + { + // keep + consolidatedModels.push_back(model); + } + } + else + { + consolidatedModels.push_back(model); + } + } + CLogMessage(static_cast(nullptr)).info(u"Updated directories %1 vs. %2 in %3 ms") << models.size() << simulatorModels.size() << timer.elapsed() << "ms"; + return consolidatedModels; + } + int CDatabaseUtils::consolidateModelsWithDbDataAllowsGuiRefresh(CAircraftModelList &models, bool force, bool processEvents) { QTime timer; diff --git a/src/blackcore/db/databaseutils.h b/src/blackcore/db/databaseutils.h index 6983659ea..1553da076 100644 --- a/src/blackcore/db/databaseutils.h +++ b/src/blackcore/db/databaseutils.h @@ -38,6 +38,10 @@ namespace BlackCore //! \remark kept here with the other consolidate functions, but actually DB independent static BlackMisc::Simulation::CAircraftModelList consolidateModelsWithSimulatorModelsAllowsGuiRefresh(const BlackMisc::Simulation::CAircraftModelList &models, const BlackMisc::Simulation::CAircraftModelList &simulatorModels, QStringList &removedModelStrings, bool processEvents); + //! Update directories in models with simulator model data (aka "models on disk") + //! \remark kept here with the other consolidate functions, but actually DB independent + static BlackMisc::Simulation::CAircraftModelList updateModelsDirectoriesAllowsGuiRefresh(const BlackMisc::Simulation::CAircraftModelList &models, const BlackMisc::Simulation::CAircraftModelList &simulatorModels, QStringList &removedModelStrings, bool processEvents); + //! Consolidate own (aircraft) model data with DB data static BlackMisc::Simulation::CAircraftModel consolidateOwnAircraftModelWithDbData(const BlackMisc::Simulation::CAircraftModel &model, bool force, bool *modified = nullptr); diff --git a/src/blackgui/menus/aircraftmodelmenus.cpp b/src/blackgui/menus/aircraftmodelmenus.cpp index f2c8e1f52..4c3faa302 100644 --- a/src/blackgui/menus/aircraftmodelmenus.cpp +++ b/src/blackgui/menus/aircraftmodelmenus.cpp @@ -100,7 +100,7 @@ namespace BlackGui } } - if (m_messageFrame && !model.getIconPath().isEmpty()) + if (m_messageFrame && !model.getIconFile().isEmpty()) { added = true; menuActions.addMenuSimulator(); @@ -133,7 +133,7 @@ namespace BlackGui const CAircraftModelView *mv = modelView(); if (!mv->hasSingleSelectedRow()) { return; } const CAircraftModel model(mv->selectedObject()); - if (model.getIconPath().isEmpty()) { return; } + if (model.getIconFile().isEmpty()) { return; } CStatusMessage msg(this); const CPixmap pm(model.loadIcon(msg)); if (msg.isSuccess()) @@ -288,11 +288,14 @@ namespace BlackGui menuActions.addMenuConsolidateModels(); + // consolidate m_consolidateAll = menuActions.addAction(m_consolidateAll, CIcons::appModels16(), "All with simulator models", CMenuAction::pathModelConsolidate(), { this, &CConsolidateWithSimulatorModels::consolidateData }); - if (mv->hasSelection()) - { - m_consolidateSelected = menuActions.addAction(m_consolidateSelected, CIcons::appModels16(), "Selected with simulator models", CMenuAction::pathModelConsolidate(), { this, &CConsolidateWithSimulatorModels::consolidateSelectedData }); - } + if (mv->hasSelection()) { m_consolidateSelected = menuActions.addAction(m_consolidateSelected, CIcons::appModels16(), "Selected with simulator models", CMenuAction::pathModelConsolidate(), { this, &CConsolidateWithSimulatorModels::consolidateSelectedData }); } + + // update directories + m_updateDirsAll = menuActions.addAction(m_updateDirsAll, CIcons::disk16(), "Update all directories", CMenuAction::pathModelConsolidate(), { this, &CConsolidateWithSimulatorModels::updateDirectoryData }); + if (mv->hasSelection()) { m_updateDirsSelected = menuActions.addAction(m_updateDirsSelected, CIcons::disk16(), "Update directories for selected", CMenuAction::pathModelConsolidate(), { this, &CConsolidateWithSimulatorModels::updateDirectorySelectedData }); } + this->nestedCustomMenu(menuActions); } @@ -354,6 +357,64 @@ namespace BlackGui } } + void CConsolidateWithSimulatorModels::updateDirectoryData() + { + bool filtered = false; + const CAircraftModelList models(this->getAllOrAllFilteredAircraftModels(&filtered)); + if (models.isEmpty()) { return; } + QStringList removedModelStrings; + const int i = this->modelView()->showLoadIndicator(); + const CAircraftModelList consolidated = CDatabaseUtils::updateModelsDirectoriesAllowsGuiRefresh(models, this->getSimulatorModels(), removedModelStrings, true); + const CSimulatorInfo sim(this->getSimulator()); + + if (!filtered) + { + this->modelsTargetSetable()->setModelsForSimulator(consolidated, sim); + } + else + { + if (!this->modelsTargetUpdatable()) + { + CLogMessage(this).warning(u"No updatable target"); + } + else + { + this->modelsTargetUpdatable()->updateModelsForSimulator(consolidated, sim); + } + } + this->modelView()->hideLoadIndicator(i); + if (!removedModelStrings.isEmpty() && this->getMappingComponent()) + { + const CStatusMessage m = CStatusMessage(this).info(u"Removed %1 model(s)") << removedModelStrings.size(); + this->getMappingComponent()->showOverlayMessage(m, 5000); + } + } + + void CConsolidateWithSimulatorModels::updateDirectorySelectedData() + { + Q_ASSERT_X(sGui, Q_FUNC_INFO, "Missing sGui"); + const CAircraftModelList models(this->getSelectedAircraftModels()); + if (models.isEmpty()) { return; } + if (!this->modelsTargetUpdatable()) + { + CLogMessage(this).warning(u"No updatable target"); + return; + } + + QStringList removedModelStrings; + const int i = this->modelView()->showLoadIndicator(); + const CAircraftModelList consolidated = CDatabaseUtils::updateModelsDirectoriesAllowsGuiRefresh(models, this->getSimulatorModels(), removedModelStrings, true); + const CSimulatorInfo sim(this->getSimulator()); + + this->modelsTargetUpdatable()->updateModelsForSimulator(consolidated, sim); + this->modelView()->hideLoadIndicator(i); + if (!removedModelStrings.isEmpty() && this->getMappingComponent()) + { + const CStatusMessage m = CStatusMessage(this).info(u"Removed %1 model(s)") << removedModelStrings.size(); + this->getMappingComponent()->showOverlayMessage(m, 5000); + } + } + CAircraftModelList CConsolidateWithSimulatorModels::getSimulatorModels() const { CDbMappingComponent *mc = this->getMappingComponent(); diff --git a/src/blackgui/menus/aircraftmodelmenus.h b/src/blackgui/menus/aircraftmodelmenus.h index b9e9718c0..a4451bfc0 100644 --- a/src/blackgui/menus/aircraftmodelmenus.h +++ b/src/blackgui/menus/aircraftmodelmenus.h @@ -112,7 +112,7 @@ namespace BlackGui QAction *m_consolidateSelected = nullptr; //!< consolidate data with DB (selected) }; - //! Merge with simulator models (e.g. remove no longer existing models) + //! Merge/update with simulator models (e.g. remove no longer existing models) class CConsolidateWithSimulatorModels : public IAircraftModelViewMenu { Q_OBJECT @@ -136,6 +136,8 @@ namespace BlackGui private: void consolidateData(); void consolidateSelectedData(); + void updateDirectoryData(); + void updateDirectorySelectedData(); //! Get models BlackMisc::Simulation::CAircraftModelList getSimulatorModels() const; @@ -151,6 +153,8 @@ namespace BlackGui QObject *m_modelsTarget = nullptr; //!< optional target for setting/updating the models QAction *m_consolidateAll = nullptr; //!< consolidate data with DB (all) QAction *m_consolidateSelected = nullptr; //!< consolidate data with DB (selected) + QAction *m_updateDirsAll = nullptr; //!< consolidate file name/dir (all) + QAction *m_updateDirsSelected = nullptr; //!< consolidate file name/dir (selected) }; } // ns } // ns diff --git a/src/blackmisc/simulation/aircraftmodel.cpp b/src/blackmisc/simulation/aircraftmodel.cpp index 048f371f6..5eb9f76e6 100644 --- a/src/blackmisc/simulation/aircraftmodel.cpp +++ b/src/blackmisc/simulation/aircraftmodel.cpp @@ -258,7 +258,7 @@ namespace BlackMisc case IndexSupportedParts: return CVariant(m_supportedParts); case IndexFileTimestamp: return CVariant::fromValue(this->getFileTimestamp()); case IndexFileTimestampFormattedYmdhms: return CVariant::fromValue(this->getFormattedFileTimestampYmdhms()); - case IndexIconPath: return CVariant(m_iconPath); + case IndexIconPath: return CVariant(m_iconFile); case IndexAircraftIcaoCode: return m_aircraftIcao.propertyByIndex(index.copyFrontRemoved()); case IndexLivery: return m_livery.propertyByIndex(index.copyFrontRemoved()); case IndexCallsign: return m_callsign.propertyByIndex(index.copyFrontRemoved()); @@ -284,7 +284,7 @@ namespace BlackMisc case IndexDescription: m_description = variant.toQString(); break; case IndexSimulatorInfo: m_simulator.setPropertyByIndex(index.copyFrontRemoved(), variant); break; case IndexName: m_name = variant.toQString(); break; - case IndexIconPath: m_iconPath = variant.toQString(); break; + case IndexIconPath: m_iconFile = variant.toQString(); break; case IndexCG: m_cg.setPropertyByIndex(index.copyFrontRemoved(), variant); break; case IndexSupportedParts: this->setSupportedParts(variant.toQString()); break; case IndexModelType: m_modelType = variant.value(); break; @@ -345,7 +345,7 @@ namespace BlackMisc case IndexName: return m_name.compare(compareValue.getName(), Qt::CaseInsensitive); case IndexCallsign: return m_callsign.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getCallsign()); case IndexFileName: return m_fileName.compare(compareValue.getFileName(), Qt::CaseInsensitive); - case IndexIconPath: return m_iconPath.compare(compareValue.getIconPath(), Qt::CaseInsensitive); + case IndexIconPath: return m_iconFile.compare(compareValue.getIconFile(), Qt::CaseInsensitive); case IndexCG: return m_cg.comparePropertyByIndex(index.copyFrontRemoved(), compareValue.getCG()); case IndexSupportedParts: return m_supportedParts.compare(compareValue.getSupportedParts()); case IndexModelTypeAsString: @@ -567,10 +567,10 @@ namespace BlackMisc CPixmap CAircraftModel::loadIcon(CStatusMessage &success) const { static const CStatusMessage noIcon(this, CStatusMessage::SeverityInfo, u"no icon"); - if (m_iconPath.isEmpty()) { success = noIcon; return CPixmap(); } + if (m_iconFile.isEmpty()) { success = noIcon; return CPixmap(); } // load from file - const CPixmap pm(CPixmap::loadFromFile(m_iconPath, success)); + const CPixmap pm(CPixmap::loadFromFile(m_iconFile, success)); return pm; } @@ -659,6 +659,12 @@ namespace BlackMisc m_distributor.updateMissingParts(otherModel.getDistributor()); } + void CAircraftModel::updateByExistingDirectories(const CAircraftModel &otherModel) + { + if (otherModel.hasExistingCorrespondingFile()) { this->setFileName(otherModel.getFileName()); } + if (otherModel.hasExistingIconFile()) { this->setIconFile(otherModel.getIconFile()); } + } + bool CAircraftModel::hasQueriedModelString() const { return m_modelType == TypeQueriedFromNetwork && this->hasModelString(); @@ -710,13 +716,13 @@ namespace BlackMisc { // other local, priority this->setFileName(model.getFileName()); - this->setIconPath(model.getIconPath()); + this->setIconFile(model.getIconFile()); return; } // both not local, override empty values if (m_fileName.isEmpty()) { this->setFileName(model.getFileName()); } - if (m_iconPath.isEmpty()) { this->setIconPath(model.getIconPath()); } + if (m_iconFile.isEmpty()) { this->setIconFile(model.getIconFile()); } } bool CAircraftModel::adjustLocalFileNames(const QString &newModelDir, const QString &stripModelDirIndicator) @@ -760,6 +766,13 @@ namespace BlackMisc return this->getFileDirectory().absolutePath(); } + bool CAircraftModel::hasExistingIconFile() const + { + if (m_iconFile.isEmpty()) { return false; } + const QFileInfo fi(m_iconFile); + return fi.exists(); + } + bool CAircraftModel::matchesModelString(const QString &modelString, Qt::CaseSensitivity sensitivity) const { return stringCompare(modelString, m_modelString, sensitivity); diff --git a/src/blackmisc/simulation/aircraftmodel.h b/src/blackmisc/simulation/aircraftmodel.h index 9b7b2be60..99a47967c 100644 --- a/src/blackmisc/simulation/aircraftmodel.h +++ b/src/blackmisc/simulation/aircraftmodel.h @@ -366,6 +366,10 @@ namespace BlackMisc //! Update missing parts from another model void updateMissingParts(const CAircraftModel &otherModel, bool dbModelPriority = true); + //! Update the directories from other model + //! \sa updateLocalFileNames + void updateByExistingDirectories(const CAircraftModel &otherModel); + //! Queried model string? bool hasQueriedModelString() const; @@ -450,11 +454,14 @@ namespace BlackMisc //! File name void setFileName(const QString &fileName) { m_fileName = fileName; } - //! File representing model - const QString &getIconPath() const { return m_iconPath; } + //! Icon file representing model + const QString &getIconFile() const { return m_iconFile; } - //! File representing model - void setIconPath(const QString &iconFile) { m_iconPath = iconFile; } + //! Icon file representing model + void setIconFile(const QString &iconFile) { m_iconFile = iconFile; } + + //! Is the icon file existing? + bool hasExistingIconFile() const; //! Get timestamp QDateTime getFileTimestamp() const; @@ -544,7 +551,7 @@ namespace BlackMisc QString m_name; //!< Model name QString m_description; //!< descriptive text QString m_fileName; //!< file name - QString m_iconPath; //!< a file representing the aircraft as icon + QString m_iconFile; //!< a file representing the aircraft as icon QString m_supportedParts; //!< supported parts qint64 m_fileTimestamp = -1; //!< file timestamp of originating file (if applicable) ModelType m_modelType = TypeUnknown; //!< model string is coming representing ...? @@ -568,7 +575,7 @@ namespace BlackMisc BLACK_METAMEMBER(name), BLACK_METAMEMBER(description, 0, DisabledForComparison), BLACK_METAMEMBER(fileName, 0, DisabledForComparison), - BLACK_METAMEMBER(iconPath, 0, DisabledForComparison), + BLACK_METAMEMBER(iconFile, 0, DisabledForComparison), BLACK_METAMEMBER(fileTimestamp, 0, DisabledForComparison), BLACK_METAMEMBER(modelType), BLACK_METAMEMBER(modelMode) diff --git a/src/blackmisc/simulation/aircraftmodellist.cpp b/src/blackmisc/simulation/aircraftmodellist.cpp index 7b22b2226..ae56b1a00 100644 --- a/src/blackmisc/simulation/aircraftmodellist.cpp +++ b/src/blackmisc/simulation/aircraftmodellist.cpp @@ -491,14 +491,14 @@ namespace BlackMisc { if (modelString.isEmpty()) { return {}; } const CAircraftModel m(findFirstByModelStringOrDefault(modelString, Qt::CaseInsensitive)); - return m.getIconPath(); + return m.getIconFile(); } QString CAircraftModelList::findModelIconPathByCallsign(const CCallsign &callsign) const { if (callsign.isEmpty()) { return {}; } const CAircraftModel m(findFirstByCallsignOrDefault(callsign)); - return m.getIconPath(); + return m.getIconFile(); } CAircraftModelList CAircraftModelList::findModelsWithoutExistingFile() const diff --git a/src/blackmisc/simulation/fscommon/aircraftcfgentries.cpp b/src/blackmisc/simulation/fscommon/aircraftcfgentries.cpp index 6f42b4ed3..e4d489cd5 100644 --- a/src/blackmisc/simulation/fscommon/aircraftcfgentries.cpp +++ b/src/blackmisc/simulation/fscommon/aircraftcfgentries.cpp @@ -133,7 +133,7 @@ namespace BlackMisc model.setName(this->getSimName()); model.setMSecsSinceEpoch(m_timestampMSecsSinceEpoch); // aircraft.cfg file timestamp model.setFileTimestamp(m_timestampMSecsSinceEpoch); - model.setIconPath(this->getThumbnailFileNameChecked()); + model.setIconFile(this->getThumbnailFileNameChecked()); const QString designator(CAircraftIcaoCode::normalizeDesignator(this->getAtcModel())); CAircraftIcaoCode aircraft( diff --git a/src/blackmisc/simulation/simulatedaircraft.h b/src/blackmisc/simulation/simulatedaircraft.h index 97fd21888..3b709deba 100644 --- a/src/blackmisc/simulation/simulatedaircraft.h +++ b/src/blackmisc/simulation/simulatedaircraft.h @@ -381,7 +381,7 @@ namespace BlackMisc QString getNetworkModelLiveryDifference() const; //! \copydoc BlackMisc::Simulation::CAircraftModel::getIconPath - const QString &getIconPath() const { return m_models[CurrentModel].getIconPath(); } + const QString &getIconFile() const { return m_models[CurrentModel].getIconFile(); } //! Get model string const QString &getModelString() const { return m_models[CurrentModel].getModelString(); }