diff --git a/src/blackgui/components/dbloaddatadialog.cpp b/src/blackgui/components/dbloaddatadialog.cpp new file mode 100644 index 000000000..5a9cdc3b7 --- /dev/null +++ b/src/blackgui/components/dbloaddatadialog.cpp @@ -0,0 +1,192 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "dbloaddatadialog.h" +#include "ui_dbloaddatadialog.h" +#include "blackgui/guiapplication.h" +#include "blackcore/webdataservices.h" +#include "blackcore/db/databaseutils.h" +#include "blackmisc/simulation/aircraftmodellist.h" +#include +#include + +using namespace BlackMisc::Network; +using namespace BlackMisc::Simulation; +using namespace BlackMisc::Simulation::Data; +using namespace BlackCore; +using namespace BlackCore::Db; + +namespace BlackGui +{ + namespace Components + { + CDbLoadDataDialog::CDbLoadDataDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::CDbLoadDataDialog) + { + Q_ASSERT_X(sGui, Q_FUNC_INFO, "Need sGui"); + ui->setupUi(this); + this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); + QStringListModel *lvm = new QStringListModel(ui->lv_Entities); + ui->comp_SimulatorSelector->setMode(CSimulatorSelector::RadioButtons); + ui->lv_Entities->setModel(lvm); + ui->bb_loadDataDialog->button(QDialogButtonBox::Apply)->setText("Load"); + ui->wi_WorkStatus->setVisible(false); + ui->wi_Consolidate->setVisible(false); + ui->comp_SimulatorSelector->setValue(m_sets.getCurrentSimulator()); + connect(sGui->getWebDataServices(), &CWebDataServices::dataRead, this, &CDbLoadDataDialog::onDataRead, Qt::QueuedConnection); + connect(ui->bb_loadDataDialog, &QDialogButtonBox::clicked, this, &CDbLoadDataDialog::onButtonClicked); + connect(ui->pb_Consolidate, &QPushButton::clicked, this, &CDbLoadDataDialog::consolidate); + connect(this, &CDbLoadDataDialog::rejected, this, &CDbLoadDataDialog::onRejected); + connect(ui->comp_SimulatorSelector, &CSimulatorSelector::changed, &m_sets, &CModelSetCaches::setCurrentSimulator, Qt::QueuedConnection); + connect(ui->comp_SimulatorSelector, &CSimulatorSelector::changed, &m_models, &CModelCaches::setCurrentSimulator, Qt::QueuedConnection); + } + + CDbLoadDataDialog::~CDbLoadDataDialog() + { } + + bool CDbLoadDataDialog::newerOrEmptyEntitiesDetected(CEntityFlags::Entity loadEntities) + { + this->show(); + if (m_consolidating) { return false; } + if (m_pendingEntities != CEntityFlags::NoEntity) { return false; } // already loading + if (loadEntities == CEntityFlags::NoEntity) { return false; } + m_autoConsolidate = false; + const QStringList entitiesStringList = CEntityFlags::entitiesToStringList(loadEntities); + this->entitiesModel()->setStringList(entitiesStringList); + ui->lv_Entities->selectAll(); + return true; + } + + QStringListModel *CDbLoadDataDialog::entitiesModel() const + { + return qobject_cast(ui->lv_Entities->model()); + } + + QStringList CDbLoadDataDialog::selectedEntities() const + { + const QModelIndexList indexes = ui->lv_Entities->selectionModel()->selectedIndexes(); + QStringList entities; + for (const QModelIndex &index : indexes) + { + entities.append(index.data(Qt::DisplayRole).toString()); + } + return entities; + } + + void CDbLoadDataDialog::onButtonClicked(QAbstractButton *button) + { + if (!button) { return; } + if (button == ui->bb_loadDataDialog->button(QDialogButtonBox::Apply)) + { + const QStringList entityList = this->selectedEntities(); + if (entityList.isEmpty()) { return; } + const CEntityFlags::Entity loadEntities = CEntityFlags::multipleEntitiesByNames(entityList); + m_pendingEntities = sGui->getWebDataServices()->triggerLoadingDirectlyFromSharedFiles(loadEntities, false); + const int pending = CEntityFlags::numberOfEntities(m_pendingEntities); + m_pendingEntitiesCount = sGui->getWebDataServices()->getDbInfoObjectsCount(loadEntities); + ui->pb_Loading->setMaximum(m_pendingEntitiesCount > 0 ? m_pendingEntitiesCount : pending); + ui->pb_Loading->setValue(0); + ui->wi_WorkStatus->setVisible(pending > 0); + ui->wi_Consolidate->setVisible(false); + ui->le_Info->setText("Loading started ..."); + } + } + + void CDbLoadDataDialog::onDataRead(CEntityFlags::Entity entity, CEntityFlags::ReadState state, int number) + { + if (m_pendingEntities == CEntityFlags::NoEntity) { return; } // no triggered from here + if (state == CEntityFlags::StartRead) { return; } + if (!m_pendingEntities.testFlag(CEntityFlags::entityToEntityFlag(entity))) { return; } + + m_pendingEntities &= ~entity; + const int pending = CEntityFlags::numberOfEntities(m_pendingEntities); + const int max = ui->pb_Loading->maximum(); + if (m_pendingEntitiesCount < 0) + { + ui->pb_Loading->setValue(max - pending); + } + else + { + m_pendingEntitiesCount -= number; + ui->pb_Loading->setValue(max - m_pendingEntitiesCount); + } + const QString e = CEntityFlags::entitiesToString(entity); + ui->le_Info->setText(e); + if (pending < 1) + { + m_pendingEntitiesCount = -1; + const bool defaultConsolidate = !ui->cb_AllModels->isChecked() && ui->cb_ModelSet->isChecked(); + + QTimer::singleShot(2000, this, [ = ] + { + ui->wi_Consolidate->setVisible(true); + ui->wi_WorkStatus->setVisible(false); + if (defaultConsolidate) + { + m_autoConsolidate = true; + QTimer::singleShot(1000, this, &CDbLoadDataDialog::consolidate); + } + }); + } + } + + void CDbLoadDataDialog::onRejected() + { + m_pendingEntities = CEntityFlags::NoEntity; + m_pendingEntitiesCount = -1; + m_autoConsolidate = false; + ui->pb_Loading->setVisible(false); + } + + void CDbLoadDataDialog::consolidate() + { + const bool set = ui->cb_ModelSet->isChecked(); + const bool all = ui->cb_AllModels->isChecked(); + if (m_consolidating) { return; } + if (!set && !all) { return; } + ui->wi_WorkStatus->setVisible(true); + ui->pb_Loading->setValue(0); + ui->pb_Loading->setMaximum(0); // 0/0 causing busy indicator + + do + { + if (set) + { + ui->le_Info->setText("Model set"); + CAircraftModelList models = m_sets.getCurrentCachedModels(); + const int c = CDatabaseUtils::consolidateModelsWithDbDataAllowsGuiRefresh(models, true, true); + if (c > 0) { m_sets.setCachedModels(models, m_sets.getCurrentSimulator()); } + } + + if (!this->isVisible()) { break; } // dialog closed? + if (all) + { + ui->le_Info->setText("All models"); + CAircraftModelList models = m_models.getCurrentCachedModels(); + const int c = CDatabaseUtils::consolidateModelsWithDbDataAllowsGuiRefresh(models, true, true); + if (c > 0) { m_models.setCachedModels(models, m_models.getCurrentSimulator()); } + } + } + while (false); + + m_consolidating = false; + QTimer::singleShot(2000, this, [ = ] + { + ui->pb_Loading->setMaximum(100); + ui->wi_WorkStatus->setVisible(false); + if (m_autoConsolidate) + { + m_autoConsolidate = false; + this->accept(); + } + }); + } + } // ns +} // ns diff --git a/src/blackgui/components/dbloaddatadialog.h b/src/blackgui/components/dbloaddatadialog.h new file mode 100644 index 000000000..1a66de2cd --- /dev/null +++ b/src/blackgui/components/dbloaddatadialog.h @@ -0,0 +1,75 @@ +/* Copyright (C) 2018 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef DBLOADDATADIALOG_H +#define DBLOADDATADIALOG_H + +#include "blackgui/blackguiexport.h" +#include "blackmisc/network/entityflags.h" +#include "blackmisc/simulation/data/modelcaches.h" +#include +#include +#include +#include + +namespace Ui { class CDbLoadDataDialog; } +namespace BlackGui +{ + namespace Components + { + /** + * Load data from DB as dialog + */ + class BLACKGUI_EXPORT CDbLoadDataDialog : public QDialog + { + Q_OBJECT + + public: + //! Constructor + explicit CDbLoadDataDialog(QWidget *parent = nullptr); + + //! Destructor + virtual ~CDbLoadDataDialog(); + + //! Newer or empty entities detected + bool newerOrEmptyEntitiesDetected(BlackMisc::Network::CEntityFlags::Entity loadEntities); + + private: + QScopedPointer ui; + BlackMisc::Network::CEntityFlags::Entity m_pendingEntities = BlackMisc::Network::CEntityFlags::NoEntity; + BlackMisc::Simulation::Data::CModelSetCaches m_sets { true, this }; //!< caches + BlackMisc::Simulation::Data::CModelCaches m_models { true, this }; //!< models + int m_pendingEntitiesCount = -1; + bool m_consolidating = false; //! currently consolidating + bool m_autoConsolidate = false; + + //! The string list model + QStringListModel *entitiesModel() const; + + //! All selected items + QStringList selectedEntities() const; + + //! Button clicked + void onButtonClicked(QAbstractButton *button); + + //! Data have been read + void onDataRead(BlackMisc::Network::CEntityFlags::Entity entity, BlackMisc::Network::CEntityFlags::ReadState state, int number); + + //! Dialog rejected + void onRejected(); + + //! Consolidate + void consolidate(); + }; + } // ns +} // ns + +#endif // guard diff --git a/src/blackgui/components/dbloaddatadialog.ui b/src/blackgui/components/dbloaddatadialog.ui new file mode 100644 index 000000000..62509b580 --- /dev/null +++ b/src/blackgui/components/dbloaddatadialog.ui @@ -0,0 +1,189 @@ + + + CDbLoadDataDialog + + + + 0 + 0 + 400 + 300 + + + + + 400 + 300 + + + + Load new DB data? + + + + + + Entities + + + + + + New DB data. Load the given entities? + + + + + + + QAbstractItemView::MultiSelection + + + QAbstractItemView::SelectRows + + + true + + + + + + + + + + + + + true + + + + + + + 50 + + + + + + + + + + + + + + 125 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Model set + + + Set + + + true + + + + + + + all models + + + All + + + + + + + consolidate + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel + + + + + + + + BlackGui::Components::CSimulatorSelector + QFrame +
blackgui/components/simulatorselector.h
+ 1 +
+
+ + + + bb_loadDataDialog + accepted() + CDbLoadDataDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + bb_loadDataDialog + rejected() + CDbLoadDataDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/src/blackgui/share/qss/stdwidget.qss b/src/blackgui/share/qss/stdwidget.qss index 0b8484066..731e9e25f 100644 --- a/src/blackgui/share/qss/stdwidget.qss +++ b/src/blackgui/share/qss/stdwidget.qss @@ -417,7 +417,8 @@ QMenuBar::item { border-radius: 4px; } -QMenuBar::item:selected { /* when selected using mouse or keyboard */ +/* when selected using mouse or keyboard */ +QMenuBar::item:selected { background: black; } @@ -588,6 +589,11 @@ QTableView::item:selected { color: white; } +QListView::item:selected { + background-color: blue; + color: white; +} + QTreeView { show-decoration-selected: 1; } diff --git a/src/swiftguistandard/swiftguistd.cpp b/src/swiftguistandard/swiftguistd.cpp index 9fcba20af..1639a469b 100644 --- a/src/swiftguistandard/swiftguistd.cpp +++ b/src/swiftguistandard/swiftguistd.cpp @@ -16,6 +16,7 @@ #include "blackcore/corefacadeconfig.h" #include "blackgui/components/infobarstatuscomponent.h" #include "blackgui/components/logcomponent.h" +#include "blackgui/components/dbloaddatadialog.h" #include "blackgui/components/settingscomponent.h" #include "blackgui/copyxswiftbusdialog.h" #include "blackgui/guiapplication.h" @@ -440,61 +441,15 @@ void SwiftGuiStd::checkDbDataLoaded() if (!sGui || sGui->isShuttingDown()) { return; } Q_ASSERT_X(sGui->hasWebDataServices(), Q_FUNC_INFO, "Missing web services"); Q_ASSERT_X(CThreadUtils::isCurrentThreadApplicationThread(), Q_FUNC_INFO, "Wrong thread, needs to run in main thread"); - CEntityFlags::Entity loadEntities = sGui->getWebDataServices()->getEntitiesWithNewerSharedFile(CEntityFlags::AllDbEntities); - const CEntityFlags::Entity checkForEmpty = CEntityFlags::entityFlagToEntity(CEntityFlags::AllDbEntitiesNoInfoObjects) & ~loadEntities; - - // it can happen the timestamps are not newer, but the data are empty - // - can happen if caches are copied and the TS does not represent the DB timestamp - // - cache files have been deleted - // - sync all DB entities - // - fast if there are no data - // - no impact if already synced - // - slow if newer synced before and all has to be done now - if (!m_dbDataLoading) { sGui->getWebDataServices()->synchronizeDbCaches(checkForEmpty); } - - // we have no newer timestamps, but incomplete data - loadEntities |= sGui->getWebDataServices()->getEmptyEntities(); + CEntityFlags::Entity loadEntities = sGui->getWebDataServices()->getSychronizedEntitiesWithNewerSharedFileOrEmpty(!m_dbDataLoading); if (loadEntities == CEntityFlags::NoEntity) { m_dbDataLoading = false; return; } - CStatusMessage sm = m_dbDataLoading ? - CStatusMessage(this).info("Loading in progress:") : - CStatusMessage(this).info("New data for shared files:"); - CStatusMessageList sms(sm); - const QSet newSingleEntities = CEntityFlags::asSingleEntities(loadEntities); - const QString m = m_dbDataLoading ? QStringLiteral("Loading data for '%1'") : QStringLiteral("Load data for '%1'?"); - for (CEntityFlags::Entity newSingleEntity : newSingleEntities) - { - sm = CStatusMessage(this).info(m) << CEntityFlags::flagToString(newSingleEntity); - sms.push_back(sm); - } - - constexpr int checkAgain = 5000; - if (m_dbDataLoading) - { - // already loading - ui->fr_CentralFrameInside->showOverlayMessages(sms, false, 1.2 * checkAgain); - return; - } - - // data need to be loaded - constexpr int delay = 2500; // allow to init GUI completely before showing overlays - QTimer::singleShot(delay, this, [ = ] - { - // delayed call - auto lambda = [ = ]() - { - sGui->getWebDataServices()->triggerLoadingDirectlyFromSharedFiles(loadEntities, false); - m_dbDataLoading = true; - - // re-check - QTimer::singleShot(checkAgain, this, &SwiftGuiStd::checkDbDataLoaded); - }; - ui->fr_CentralFrameInside->showOverlayMessagesWithConfirmation(sms, false, "Load DB data?", lambda); - }); + if (!m_dbLoadDialog) { m_dbLoadDialog.reset(new CDbLoadDataDialog(this)); } + m_dbLoadDialog->newerOrEmptyEntitiesDetected(loadEntities); } void SwiftGuiStd::playNotifcationSound(CNotificationSounds::Notification notification) const diff --git a/src/swiftguistandard/swiftguistd.h b/src/swiftguistandard/swiftguistd.h index c9a9a27aa..a805d45a9 100644 --- a/src/swiftguistandard/swiftguistd.h +++ b/src/swiftguistandard/swiftguistd.h @@ -44,6 +44,7 @@ class QTimer; class QWidget; namespace BlackMisc { namespace Aviation { class CAltitude; } } +namespace BlackGui { namespace Components { class CDbLoadDataDialog; }} namespace Ui { class SwiftGuiStd; } //! swift GUI @@ -101,7 +102,8 @@ protected: private: QScopedPointer ui; - QScopedPointer m_navigator{new BlackGui::Components::CNavigatorDialog()}; // if I pass the parent, the dialog is always centered over the parent + QScopedPointer m_navigator{ new BlackGui::Components::CNavigatorDialog() }; //!< navigator dialog bar, if I pass the parent, the dialog is always centered over the parent + QScopedPointer m_dbLoadDialog; //!< load DB data, lazy init UI component BlackCore::CActionBindings m_menuHotkeyHandlers; BlackGui::CManagedStatusBar m_statusBar; BlackMisc::CLogSubscriber m_logSubscriber { this, &SwiftGuiStd::displayStatusMessageInGui };