From 73a314ea66d538075fa0b4a616dfb3704e654914 2017-12-20 13:28:49 From: mperrinel Date: 2017-12-20 13:28:49 Subject: [PATCH] Merge branch 'feature/CatalogueDevelop' into develop --- diff --git a/app/include/MainWindow.h b/app/include/MainWindow.h index 77c6b09..d16d4f5 100644 --- a/app/include/MainWindow.h +++ b/app/include/MainWindow.h @@ -52,6 +52,7 @@ public slots: protected: void changeEvent(QEvent *e); + void closeEvent(QCloseEvent *event); private: std::unique_ptr m_Ui; diff --git a/app/src/Main.cpp b/app/src/Main.cpp index bd42c5e..8cc71ae 100644 --- a/app/src/Main.cpp +++ b/app/src/Main.cpp @@ -49,10 +49,12 @@ int main(int argc, char *argv[]) #endif Q_INIT_RESOURCE(sqpguiresources); - SqpApplication a{argc, argv}; SqpApplication::setOrganizationName("LPP"); SqpApplication::setOrganizationDomain("lpp.fr"); SqpApplication::setApplicationName("SciQLop"); + + SqpApplication a{argc, argv}; + MainWindow w; w.show(); diff --git a/app/src/MainWindow.cpp b/app/src/MainWindow.cpp index eda17cd..02ff19c 100644 --- a/app/src/MainWindow.cpp +++ b/app/src/MainWindow.cpp @@ -22,6 +22,7 @@ #include "MainWindow.h" #include "ui_MainWindow.h" +#include #include #include #include @@ -36,9 +37,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -74,6 +77,8 @@ public: SqpSettingsDialog *m_SettingsDialog; /// Catalogue dialog. MainWindow has the ownership CatalogueExplorer *m_CatalogExplorer; + + bool checkDataToSave(QWidget *parentWidget); }; MainWindow::MainWindow(QWidget *parent) @@ -364,3 +369,37 @@ void MainWindow::changeEvent(QEvent *e) break; } } + +void MainWindow::closeEvent(QCloseEvent *event) +{ + if (!impl->checkDataToSave(this)) { + event->ignore(); + } + else { + event->accept(); + } +} + +bool MainWindow::MainWindowPrivate::checkDataToSave(QWidget *parentWidget) +{ + auto hasChanges = sqpApp->catalogueController().hasChanges(); + if (hasChanges) { + // There are some unsaved changes + switch (QMessageBox::question( + parentWidget, "Save changes", + tr("The catalogue controller unsaved changes.\nDo you want to save them ?"), + QMessageBox::SaveAll | QMessageBox::Discard | QMessageBox::Cancel, + QMessageBox::SaveAll)) { + case QMessageBox::SaveAll: + sqpApp->catalogueController().saveAll(); + break; + case QMessageBox::Discard: + break; + case QMessageBox::Cancel: + default: + return false; + } + } + + return true; +} diff --git a/core/include/Catalogue/CatalogueController.h b/core/include/Catalogue/CatalogueController.h index 717037d..cf089e7 100644 --- a/core/include/Catalogue/CatalogueController.h +++ b/core/include/Catalogue/CatalogueController.h @@ -15,6 +15,7 @@ class DBCatalogue; class DBEvent; +class DBEventProduct; Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueController) @@ -44,10 +45,13 @@ public: retrieveEventsFromCatalogue(std::shared_ptr catalogue) const; void addEvent(std::shared_ptr event); void updateEvent(std::shared_ptr event); + void updateEventProduct(std::shared_ptr eventProduct); void removeEvent(std::shared_ptr event); // void trashEvent(std::shared_ptr event); - // void restore(QUuid eventId); + // void restore(std::shared_ptr event); void saveEvent(std::shared_ptr event); + void discardEvent(std::shared_ptr event); + bool eventHasChanges(std::shared_ptr event) const; // Catalogue // bool createCatalogue(const QString &name, QVector eventList); @@ -59,15 +63,19 @@ public: void saveCatalogue(std::shared_ptr catalogue); void saveAll(); + bool hasChanges() const; + + /// Returns the MIME data associated to a list of variables + QByteArray mimeDataForEvents(const QVector > &events) const; + + /// Returns the list of variables contained in a MIME data + QVector > eventsForMimeData(const QByteArray &mimeData) const; public slots: /// Manage init/end of the controller void initialize(); - void finalize(); private: - void waitForFinish(); - class CatalogueControllerPrivate; spimpl::unique_impl_ptr impl; }; diff --git a/core/include/DataSource/DataSourceItem.h b/core/include/DataSource/DataSourceItem.h index 9b82471..b872ce6 100644 --- a/core/include/DataSource/DataSourceItem.h +++ b/core/include/DataSource/DataSourceItem.h @@ -27,6 +27,8 @@ public: static const QString NAME_DATA_KEY; /// Key associated with the plugin of the item static const QString PLUGIN_DATA_KEY; + /// Key associated with a unique id of the plugin + static const QString ID_DATA_KEY; explicit DataSourceItem(DataSourceItemType type, const QString &name); explicit DataSourceItem(DataSourceItemType type, QVariantHash data = {}); diff --git a/core/src/Catalogue/CatalogueController.cpp b/core/src/Catalogue/CatalogueController.cpp index be76fcf..6ae79e0 100644 --- a/core/src/Catalogue/CatalogueController.cpp +++ b/core/src/Catalogue/CatalogueController.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -22,8 +23,8 @@ Q_LOGGING_CATEGORY(LOG_CatalogueController, "CatalogueController") namespace { -static QString REPOSITORY_WORK_SUFFIX = QString{"work"}; -static QString REPOSITORY_TRASH_SUFFIX = QString{"trash"}; +static QString REPOSITORY_WORK_SUFFIX = QString{"_work"}; +static QString REPOSITORY_TRASH_SUFFIX = QString{"_trash"}; } class CatalogueController::CatalogueControllerPrivate { @@ -31,15 +32,22 @@ class CatalogueController::CatalogueControllerPrivate { public: explicit CatalogueControllerPrivate(CatalogueController *parent) : m_Q{parent} {} - QMutex m_WorkingMutex; CatalogueDao m_CatalogueDao; QStringList m_RepositoryList; CatalogueController *m_Q; + QSet m_EventKeysWithChanges; + + QString eventUniqueKey(const std::shared_ptr &event) const; + void copyDBtoDB(const QString &dbFrom, const QString &dbTo); QString toWorkRepository(QString repository); QString toSyncRepository(QString repository); + void savAllDB(); + + void saveEvent(std::shared_ptr event, bool persist = true); + void saveCatalogue(std::shared_ptr catalogue, bool persist = true); }; CatalogueController::CatalogueController(QObject *parent) @@ -53,7 +61,6 @@ CatalogueController::~CatalogueController() { qCDebug(LOG_CatalogueController()) << tr("CatalogueController destruction") << QThread::currentThread(); - this->waitForFinish(); } QStringList CatalogueController::getRepositories() const @@ -132,11 +139,19 @@ CatalogueController::retrieveEventsFromCatalogue(std::shared_ptr ca void CatalogueController::updateEvent(std::shared_ptr event) { - event->setRepository(impl->toSyncRepository(event->getRepository())); + event->setRepository(impl->toWorkRepository(event->getRepository())); + + auto uniqueId = impl->eventUniqueKey(event); + impl->m_EventKeysWithChanges.insert(uniqueId); impl->m_CatalogueDao.updateEvent(*event); } +void CatalogueController::updateEventProduct(std::shared_ptr eventProduct) +{ + impl->m_CatalogueDao.updateEventProduct(*eventProduct); +} + void CatalogueController::removeEvent(std::shared_ptr event) { // Remove it from both repository and repository_work @@ -144,24 +159,72 @@ void CatalogueController::removeEvent(std::shared_ptr event) impl->m_CatalogueDao.removeEvent(*event); event->setRepository(impl->toSyncRepository(event->getRepository())); impl->m_CatalogueDao.removeEvent(*event); + impl->savAllDB(); } void CatalogueController::addEvent(std::shared_ptr event) { event->setRepository(impl->toWorkRepository(event->getRepository())); - impl->m_CatalogueDao.addEvent(*event); + auto eventTemp = *event; + impl->m_CatalogueDao.addEvent(eventTemp); // Call update is necessary at the creation of add Event if it has some tags or some event // products if (!event->getEventProducts().empty() || !event->getTags().empty()) { - impl->m_CatalogueDao.updateEvent(*event); + + auto eventProductsTemp = eventTemp.getEventProducts(); + auto eventProductTempUpdated = std::list{}; + for (auto eventProductTemp : eventProductsTemp) { + eventProductTemp.setEvent(eventTemp); + eventProductTempUpdated.push_back(eventProductTemp); + } + eventTemp.setEventProducts(eventProductTempUpdated); + + impl->m_CatalogueDao.updateEvent(eventTemp); } } void CatalogueController::saveEvent(std::shared_ptr event) { - impl->m_CatalogueDao.moveEvent(*event, impl->toSyncRepository(event->getRepository()), true); + impl->saveEvent(event, true); + impl->m_EventKeysWithChanges.remove(impl->eventUniqueKey(event)); +} + +void CatalogueController::discardEvent(std::shared_ptr event) +{ + auto uniqIdPredicate = std::make_shared( + QString{"uniqId"}, event->getUniqId(), ComparaisonOperation::EQUALEQUAL); + + auto syncRepositoryPredicate = std::make_shared( + QString{"repository"}, impl->toSyncRepository(event->getRepository()), + ComparaisonOperation::EQUALEQUAL); + + auto syncPred = std::make_shared(CompoundOperation::AND); + syncPred->AddRequestPredicate(uniqIdPredicate); + syncPred->AddRequestPredicate(syncRepositoryPredicate); + + + auto workRepositoryPredicate = std::make_shared( + QString{"repository"}, impl->toWorkRepository(event->getRepository()), + ComparaisonOperation::EQUALEQUAL); + + auto workPred = std::make_shared(CompoundOperation::AND); + workPred->AddRequestPredicate(uniqIdPredicate); + workPred->AddRequestPredicate(workRepositoryPredicate); + + + auto syncEvent = impl->m_CatalogueDao.getEvent(syncPred); + impl->m_CatalogueDao.copyEvent(syncEvent, impl->toWorkRepository(event->getRepository()), true); + + auto workEvent = impl->m_CatalogueDao.getEvent(workPred); + *event = workEvent; + impl->m_EventKeysWithChanges.remove(impl->eventUniqueKey(event)); +} + +bool CatalogueController::eventHasChanges(std::shared_ptr event) const +{ + return impl->m_EventKeysWithChanges.contains(impl->eventUniqueKey(event)); } std::list > @@ -179,7 +242,7 @@ CatalogueController::retrieveCatalogues(const QString &repository) const void CatalogueController::updateCatalogue(std::shared_ptr catalogue) { - catalogue->setRepository(impl->toSyncRepository(catalogue->getRepository())); + catalogue->setRepository(impl->toWorkRepository(catalogue->getRepository())); impl->m_CatalogueDao.updateCatalogue(*catalogue); } @@ -195,8 +258,7 @@ void CatalogueController::removeCatalogue(std::shared_ptr catalogue void CatalogueController::saveCatalogue(std::shared_ptr catalogue) { - impl->m_CatalogueDao.moveCatalogue(*catalogue, - impl->toSyncRepository(catalogue->getRepository()), true); + impl->saveCatalogue(catalogue, true); } void CatalogueController::saveAll() @@ -205,22 +267,70 @@ void CatalogueController::saveAll() // Save Event auto events = this->retrieveEvents(repository); for (auto event : events) { - this->saveEvent(event); + impl->saveEvent(event, false); } // Save Catalogue auto catalogues = this->retrieveCatalogues(repository); for (auto catalogue : catalogues) { - this->saveCatalogue(catalogue); + impl->saveCatalogue(catalogue, false); + } + } + + impl->savAllDB(); + impl->m_EventKeysWithChanges.clear(); +} + +bool CatalogueController::hasChanges() const +{ + return !impl->m_EventKeysWithChanges.isEmpty(); // TODO: catalogues +} + +QByteArray +CatalogueController::mimeDataForEvents(const QVector > &events) const +{ + auto encodedData = QByteArray{}; + + QMap idsPerRepository; + for (auto event : events) { + idsPerRepository[event->getRepository()] << event->getUniqId(); + } + + QDataStream stream{&encodedData, QIODevice::WriteOnly}; + stream << idsPerRepository; + + return encodedData; +} + +QVector > +CatalogueController::eventsForMimeData(const QByteArray &mimeData) const +{ + auto events = QVector >{}; + QDataStream stream{mimeData}; + + QMap idsPerRepository; + stream >> idsPerRepository; + + for (auto it = idsPerRepository.cbegin(); it != idsPerRepository.cend(); ++it) { + auto repository = it.key(); + auto allRepositoryEvent = retrieveEvents(repository); + for (auto uuid : it.value()) { + for (auto repositoryEvent : allRepositoryEvent) { + if (uuid.toUuid() == repositoryEvent->getUniqId()) { + events << repositoryEvent; + } + } } } + + return events; } void CatalogueController::initialize() { qCDebug(LOG_CatalogueController()) << tr("CatalogueController init") << QThread::currentThread(); - impl->m_WorkingMutex.lock(); + impl->m_CatalogueDao.initialize(); auto defaultRepositoryLocation = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); @@ -242,37 +352,24 @@ void CatalogueController::initialize() qCDebug(LOG_CatalogueController()) << tr("CatalogueController init END"); } -void CatalogueController::finalize() -{ - impl->m_WorkingMutex.unlock(); -} - -void CatalogueController::waitForFinish() +QString CatalogueController::CatalogueControllerPrivate::eventUniqueKey( + const std::shared_ptr &event) const { - QMutexLocker locker{&impl->m_WorkingMutex}; + return event->getUniqId().toString().append(event->getRepository()); } void CatalogueController::CatalogueControllerPrivate::copyDBtoDB(const QString &dbFrom, const QString &dbTo) { - auto cataloguesShared = std::list >{}; + // auto cataloguesShared = std::list >{}; auto catalogues = m_CatalogueDao.getCatalogues(dbFrom); + auto events = m_CatalogueDao.getEvents(dbFrom); for (auto catalogue : catalogues) { - cataloguesShared.push_back(std::make_shared(catalogue)); + m_CatalogueDao.copyCatalogue(catalogue, dbTo, true); } - auto eventsShared = std::list >{}; - auto events = m_CatalogueDao.getEvents(dbFrom); for (auto event : events) { - eventsShared.push_back(std::make_shared(event)); - } - - for (auto catalogue : cataloguesShared) { - m_CatalogueDao.copyCatalogue(*catalogue, dbTo, true); - } - - for (auto event : eventsShared) { - m_CatalogueDao.copyEvent(*event, dbTo, true); + m_CatalogueDao.copyEvent(event, dbTo, true); } } @@ -280,7 +377,7 @@ QString CatalogueController::CatalogueControllerPrivate::toWorkRepository(QStrin { auto syncRepository = toSyncRepository(repository); - return QString("%1_%2").arg(syncRepository, REPOSITORY_WORK_SUFFIX); + return QString("%1%2").arg(syncRepository, REPOSITORY_WORK_SUFFIX); } QString CatalogueController::CatalogueControllerPrivate::toSyncRepository(QString repository) @@ -294,3 +391,30 @@ QString CatalogueController::CatalogueControllerPrivate::toSyncRepository(QStrin } return syncRepository; } + +void CatalogueController::CatalogueControllerPrivate::savAllDB() +{ + for (auto repository : m_RepositoryList) { + auto defaultRepositoryLocation + = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); + m_CatalogueDao.saveDB(defaultRepositoryLocation, repository); + } +} + +void CatalogueController::CatalogueControllerPrivate::saveEvent(std::shared_ptr event, + bool persist) +{ + m_CatalogueDao.copyEvent(*event, toSyncRepository(event->getRepository()), true); + if (persist) { + savAllDB(); + } +} + +void CatalogueController::CatalogueControllerPrivate::saveCatalogue( + std::shared_ptr catalogue, bool persist) +{ + m_CatalogueDao.copyCatalogue(*catalogue, toSyncRepository(catalogue->getRepository()), true); + if (persist) { + savAllDB(); + } +} diff --git a/core/src/DataSource/DataSourceItem.cpp b/core/src/DataSource/DataSourceItem.cpp index 39d6599..86730d3 100644 --- a/core/src/DataSource/DataSourceItem.cpp +++ b/core/src/DataSource/DataSourceItem.cpp @@ -6,6 +6,7 @@ const QString DataSourceItem::NAME_DATA_KEY = QStringLiteral("name"); const QString DataSourceItem::PLUGIN_DATA_KEY = QStringLiteral("plugin"); +const QString DataSourceItem::ID_DATA_KEY = QStringLiteral("uuid"); struct DataSourceItem::DataSourceItemPrivate { explicit DataSourceItemPrivate(DataSourceItemType type, QVariantHash data) diff --git a/gui/include/Catalogue/CatalogueActionManager.h b/gui/include/Catalogue/CatalogueActionManager.h index 5bd3347..07172db 100644 --- a/gui/include/Catalogue/CatalogueActionManager.h +++ b/gui/include/Catalogue/CatalogueActionManager.h @@ -3,9 +3,11 @@ #include +class CatalogueExplorer; + class CatalogueActionManager { public: - CatalogueActionManager(); + CatalogueActionManager(CatalogueExplorer *catalogueExplorer); void installSelectionZoneActions(); diff --git a/gui/include/Catalogue/CatalogueEventsModel.h b/gui/include/Catalogue/CatalogueEventsModel.h index e51ee47..6251ac4 100644 --- a/gui/include/Catalogue/CatalogueEventsModel.h +++ b/gui/include/Catalogue/CatalogueEventsModel.h @@ -34,7 +34,7 @@ public: std::shared_ptr getEventProduct(const QModelIndex &index) const; /// Refresh the data for the specified event - void refreshEvent(const std::shared_ptr &event); + void refreshEvent(const std::shared_ptr &event, bool refreshEventProducts = false); /// Returns a QModelIndex which represent the specified event QModelIndex indexOf(const std::shared_ptr &event) const; diff --git a/gui/include/Catalogue/CatalogueEventsWidget.h b/gui/include/Catalogue/CatalogueEventsWidget.h index 036b520..0dee481 100644 --- a/gui/include/Catalogue/CatalogueEventsWidget.h +++ b/gui/include/Catalogue/CatalogueEventsWidget.h @@ -32,17 +32,27 @@ public: void setVisualizationWidget(VisualizationWidget *visualization); + void addEvent(const std::shared_ptr &event); void setEventChanges(const std::shared_ptr &event, bool hasChanges); + QVector > displayedCatalogues() const; + bool isAllEventsDisplayed() const; + bool isEventDisplayed(const std::shared_ptr &event) const; + public slots: void populateWithCatalogues(const QVector > &catalogues); void populateWithAllEvents(); + void clear(); + void refresh(); private: Ui::CatalogueEventsWidget *ui; class CatalogueEventsWidgetPrivate; spimpl::unique_impl_ptr impl; + +private slots: + void emitSelection(); }; #endif // SCIQLOP_CATALOGUEEVENTSWIDGET_H diff --git a/gui/include/Catalogue/CatalogueExplorer.h b/gui/include/Catalogue/CatalogueExplorer.h index 9baa58f..37d3ad9 100644 --- a/gui/include/Catalogue/CatalogueExplorer.h +++ b/gui/include/Catalogue/CatalogueExplorer.h @@ -8,6 +8,9 @@ namespace Ui { class CatalogueExplorer; } +class CatalogueEventsWidget; +class CatalogueSideBarWidget; + class VisualizationWidget; class CatalogueExplorer : public QDialog { @@ -19,6 +22,9 @@ public: void setVisualizationWidget(VisualizationWidget *visualization); + CatalogueEventsWidget &eventsWidget() const; + CatalogueSideBarWidget &sideBarWidget() const; + private: Ui::CatalogueExplorer *ui; diff --git a/gui/include/Catalogue/CatalogueSideBarWidget.h b/gui/include/Catalogue/CatalogueSideBarWidget.h index 0af2b26..1d578a8 100644 --- a/gui/include/Catalogue/CatalogueSideBarWidget.h +++ b/gui/include/Catalogue/CatalogueSideBarWidget.h @@ -28,8 +28,11 @@ public: explicit CatalogueSideBarWidget(QWidget *parent = 0); virtual ~CatalogueSideBarWidget(); + void addCatalogue(const std::shared_ptr &catalogue, const QString &repository); void setCatalogueChanges(const std::shared_ptr &catalogue, bool hasChanges); + QVector > getCatalogues(const QString &repository) const; + private: Ui::CatalogueSideBarWidget *ui; diff --git a/gui/include/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h b/gui/include/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h new file mode 100644 index 0000000..7fc10fc --- /dev/null +++ b/gui/include/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h @@ -0,0 +1,35 @@ +#ifndef SCIQLOP_CATALOGUEABSTRACTTREEITEM_H +#define SCIQLOP_CATALOGUEABSTRACTTREEITEM_H + +#include +#include +#include + +class QMimeData; + +class CatalogueAbstractTreeItem { +public: + constexpr static const int DEFAULT_TYPE = -1; + + CatalogueAbstractTreeItem(int type = DEFAULT_TYPE); + virtual ~CatalogueAbstractTreeItem(); + + void addChild(CatalogueAbstractTreeItem *child); + QVector children() const; + CatalogueAbstractTreeItem *parent() const; + + int type() const; + QString text(int column = 0) const; + + virtual QVariant data(int column, int role) const; + virtual Qt::ItemFlags flags(int column) const; + virtual bool setData(int column, int role, const QVariant &value); + virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action); + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action); + +private: + class CatalogueAbstractTreeItemPrivate; + spimpl::unique_impl_ptr impl; +}; + +#endif // SCIQLOP_CATALOGUEABSTRACTTREEITEM_H diff --git a/gui/include/Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.h b/gui/include/Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.h new file mode 100644 index 0000000..fc345c8 --- /dev/null +++ b/gui/include/Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.h @@ -0,0 +1,23 @@ +#ifndef SCIQLOP_CATALOGUETEXTTREEITEM_H +#define SCIQLOP_CATALOGUETEXTTREEITEM_H + +#include +#include + +class CatalogueTextTreeItem : public CatalogueAbstractTreeItem { +public: + CatalogueTextTreeItem(const QIcon &icon, const QString &text, int type); + + QVariant data(int column, int role) const override; + Qt::ItemFlags flags(int column) const override; + + QString text() const; + + void setEnabled(bool value); + +private: + class CatalogueTextTreeItemPrivate; + spimpl::unique_impl_ptr impl; +}; + +#endif // SCIQLOP_CATALOGUETEXTTREEITEM_H diff --git a/gui/include/Catalogue/CatalogueTreeItems/CatalogueTreeItem.h b/gui/include/Catalogue/CatalogueTreeItems/CatalogueTreeItem.h new file mode 100644 index 0000000..4aa26d0 --- /dev/null +++ b/gui/include/Catalogue/CatalogueTreeItems/CatalogueTreeItem.h @@ -0,0 +1,28 @@ +#ifndef SCIQLOP_CATALOGUETREEITEM_H +#define SCIQLOP_CATALOGUETREEITEM_H + +#include +#include + +class DBCatalogue; + + +class CatalogueTreeItem : public CatalogueAbstractTreeItem { +public: + CatalogueTreeItem(std::shared_ptr catalogue, const QIcon &icon, int type); + + QVariant data(int column, int role) const override; + bool setData(int column, int role, const QVariant &value) override; + Qt::ItemFlags flags(int column) const override; + bool canDropMimeData(const QMimeData *data, Qt::DropAction action) override; + bool dropMimeData(const QMimeData *data, Qt::DropAction action) override; + + /// Returns the catalogue represented by the item + std::shared_ptr catalogue() const; + +private: + class CatalogueTreeItemPrivate; + spimpl::unique_impl_ptr impl; +}; + +#endif // SCIQLOP_CATALOGUETREEITEM_H diff --git a/gui/include/Catalogue/CatalogueTreeModel.h b/gui/include/Catalogue/CatalogueTreeModel.h new file mode 100644 index 0000000..ac14836 --- /dev/null +++ b/gui/include/Catalogue/CatalogueTreeModel.h @@ -0,0 +1,56 @@ +#ifndef SCIQLOP_CATALOGUETREEMODEL_H +#define SCIQLOP_CATALOGUETREEMODEL_H + +#include +#include + +class CatalogueAbstractTreeItem; + +/** + * @brief Model to display catalogue items based on QTreeWidgetItem + * @warning Do not use the method QTreeWidgetItem::treeWidget for an item added to this model or the + * application will crash + */ +class CatalogueTreeModel : public QAbstractItemModel { + Q_OBJECT + +signals: + void itemRenamed(const QModelIndex &index); + void itemDropped(const QModelIndex &parentIndex); + +public: + CatalogueTreeModel(QObject *parent = nullptr); + + enum class Column { Name, Validation, Count }; + + QModelIndex addTopLevelItem(CatalogueAbstractTreeItem *item); + QVector topLevelItems() const; + + void addChildItem(CatalogueAbstractTreeItem *child, const QModelIndex &parentIndex); + + CatalogueAbstractTreeItem *item(const QModelIndex &index) const; + QModelIndex indexOf(CatalogueAbstractTreeItem *item, int column = 0) const; + + // model + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + QVariant data(const QModelIndex &index, int role) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + + bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, + const QModelIndex &parent) const override; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, + const QModelIndex &parent) override; + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; + +private: + class CatalogueTreeModelPrivate; + spimpl::unique_impl_ptr impl; +}; + +#endif // CATALOGUETREEMODEL_H diff --git a/gui/include/Catalogue/CatalogueTreeWidgetItem.h b/gui/include/Catalogue/CatalogueTreeWidgetItem.h deleted file mode 100644 index f9b0d65..0000000 --- a/gui/include/Catalogue/CatalogueTreeWidgetItem.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef SCIQLOP_CATALOGUETREEWIDGETITEM_H -#define SCIQLOP_CATALOGUETREEWIDGETITEM_H - -#include -#include - -class DBCatalogue; - - -class CatalogueTreeWidgetItem : public QTreeWidgetItem { -public: - CatalogueTreeWidgetItem(std::shared_ptr catalogue, - int type = QTreeWidgetItem::Type); - - QVariant data(int column, int role) const override; - void setData(int column, int role, const QVariant &value) override; - - /// Returns the catalogue represented by the item - std::shared_ptr catalogue() const; - - /// Displays or hides the save and cancel buttons indicating that the catalogue has unsaved - /// changes - void setHasChanges(bool value); - - /// Returns true if the widget indicating the event has unsaved changes is displayed - bool hasChanges(); - - /// Refreshes the data displayed by the item from the catalogue - void refresh(); - -private: - class CatalogueTreeWidgetItemPrivate; - spimpl::unique_impl_ptr impl; -}; - -#endif // SCIQLOP_CATALOGUETREEWIDGETITEM_H diff --git a/gui/include/Catalogue/CreateEventDialog.h b/gui/include/Catalogue/CreateEventDialog.h index 4cb4baa..9e85fa4 100644 --- a/gui/include/Catalogue/CreateEventDialog.h +++ b/gui/include/Catalogue/CreateEventDialog.h @@ -15,7 +15,8 @@ class CreateEventDialog : public QDialog { Q_OBJECT public: - explicit CreateEventDialog(QWidget *parent = 0); + explicit CreateEventDialog(const QVector > &catalogues, + QWidget *parent = 0); virtual ~CreateEventDialog(); void hideCatalogueChoice(); diff --git a/gui/meson.build b/gui/meson.build index e37bc1c..add3dca 100644 --- a/gui/meson.build +++ b/gui/meson.build @@ -27,7 +27,8 @@ gui_moc_headers = [ 'include/Catalogue/CatalogueSideBarWidget.h', 'include/Catalogue/CatalogueInspectorWidget.h', 'include/Catalogue/CatalogueEventsModel.h', - 'include/Catalogue/CreateEventDialog.h' + 'include/Catalogue/CreateEventDialog.h', + 'include/Catalogue/CatalogueTreeModel.h' ] gui_ui_files = [ @@ -118,11 +119,14 @@ gui_sources = [ 'src/Catalogue/CatalogueEventsWidget.cpp', 'src/Catalogue/CatalogueSideBarWidget.cpp', 'src/Catalogue/CatalogueInspectorWidget.cpp', - 'src/Catalogue/CatalogueTreeWidgetItem.cpp', + 'src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp', + 'src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp', + 'src/Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.cpp', 'src/Catalogue/CatalogueEventsModel.cpp', 'src/Catalogue/CatalogueExplorerHelper.cpp', 'src/Catalogue/CatalogueActionManager.cpp', - 'src/Catalogue/CreateEventDialog.cpp' + 'src/Catalogue/CreateEventDialog.cpp', + 'src/Catalogue/CatalogueTreeModel.cpp' ] gui_inc = include_directories(['include']) diff --git a/gui/src/Catalogue/CatalogueActionManager.cpp b/gui/src/Catalogue/CatalogueActionManager.cpp index 258ac5d..685b679 100644 --- a/gui/src/Catalogue/CatalogueActionManager.cpp +++ b/gui/src/Catalogue/CatalogueActionManager.cpp @@ -2,13 +2,18 @@ #include #include +#include #include #include #include #include +#include +#include +#include #include +#include #include #include #include @@ -21,6 +26,14 @@ #include struct CatalogueActionManager::CatalogueActionManagerPrivate { + + CatalogueExplorer *m_CatalogueExplorer = nullptr; + + CatalogueActionManagerPrivate(CatalogueExplorer *catalogueExplorer) + : m_CatalogueExplorer(catalogueExplorer) + { + } + void createEventFromZones(const QString &eventName, const QVector &zones, const std::shared_ptr &catalogue = nullptr) @@ -39,7 +52,8 @@ struct CatalogueActionManager::CatalogueActionManagerPrivate { eventProduct->setTStart(zoneRange.m_TStart); eventProduct->setTEnd(zoneRange.m_TEnd); - eventProduct->setProductId(var->metadata().value("id", "TODO").toString()); // todo + eventProduct->setProductId( + var->metadata().value(DataSourceItem::ID_DATA_KEY, "UnknownID").toString()); productList.push_back(*eventProduct); } @@ -49,15 +63,25 @@ struct CatalogueActionManager::CatalogueActionManagerPrivate { sqpApp->catalogueController().addEvent(event); + if (catalogue) { // TODO // catalogue->addEvent(event); + m_CatalogueExplorer->sideBarWidget().setCatalogueChanges(catalogue, true); + if (m_CatalogueExplorer->eventsWidget().displayedCatalogues().contains(catalogue)) { + m_CatalogueExplorer->eventsWidget().addEvent(event); + m_CatalogueExplorer->eventsWidget().setEventChanges(event, true); + } + } + else if (m_CatalogueExplorer->eventsWidget().isAllEventsDisplayed()) { + m_CatalogueExplorer->eventsWidget().addEvent(event); + m_CatalogueExplorer->eventsWidget().setEventChanges(event, true); } } }; -CatalogueActionManager::CatalogueActionManager() - : impl{spimpl::make_unique_impl()} +CatalogueActionManager::CatalogueActionManager(CatalogueExplorer *catalogueExplorer) + : impl{spimpl::make_unique_impl(catalogueExplorer)} { } @@ -82,7 +106,8 @@ void CatalogueActionManager::installSelectionZoneActions() auto createEventAction = actionController.addSectionZoneAction( {QObject::tr("Catalogues")}, QObject::tr("New Event..."), [this](auto zones) { - CreateEventDialog dialog; + CreateEventDialog dialog( + impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT)); dialog.hideCatalogueChoice(); if (dialog.exec() == QDialog::Accepted) { impl->createEventFromZones(dialog.eventName(), zones); @@ -92,12 +117,16 @@ void CatalogueActionManager::installSelectionZoneActions() auto createEventInCatalogueAction = actionController.addSectionZoneAction( {QObject::tr("Catalogues")}, QObject::tr("New Event in Catalogue..."), [this](auto zones) { - CreateEventDialog dialog; + CreateEventDialog dialog( + impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT)); if (dialog.exec() == QDialog::Accepted) { auto selectedCatalogue = dialog.selectedCatalogue(); if (!selectedCatalogue) { selectedCatalogue = std::make_shared(); selectedCatalogue->setName(dialog.catalogueName()); + // sqpApp->catalogueController().addCatalogue(selectedCatalogue); TODO + impl->m_CatalogueExplorer->sideBarWidget().addCatalogue(selectedCatalogue, + REPOSITORY_DEFAULT); } impl->createEventFromZones(dialog.eventName(), zones, selectedCatalogue); diff --git a/gui/src/Catalogue/CatalogueEventsModel.cpp b/gui/src/Catalogue/CatalogueEventsModel.cpp index de66396..9a79328 100644 --- a/gui/src/Catalogue/CatalogueEventsModel.cpp +++ b/gui/src/Catalogue/CatalogueEventsModel.cpp @@ -1,5 +1,6 @@ #include "Catalogue/CatalogueEventsModel.h" +#include #include #include #include @@ -23,7 +24,6 @@ const auto EVENT_PRODUCT_ITEM_TYPE = 2; struct CatalogueEventsModel::CatalogueEventsModelPrivate { QVector > m_Events; std::unordered_map > > m_EventProducts; - std::unordered_set > m_EventsWithChanges; QStringList columnNames() { @@ -34,8 +34,8 @@ struct CatalogueEventsModel::CatalogueEventsModelPrivate { QVariant sortData(int col, const std::shared_ptr &event) const { if (col == (int)CatalogueEventsModel::Column::Validation) { - return m_EventsWithChanges.find(event) != m_EventsWithChanges.cend() ? true - : QVariant(); + auto hasChanges = sqpApp->catalogueController().eventHasChanges(event); + return hasChanges ? true : QVariant(); } return eventData(col, event); @@ -114,6 +114,14 @@ struct CatalogueEventsModel::CatalogueEventsModelPrivate { Q_ASSERT(false); return QStringLiteral("Unknown Data"); } + + void refreshChildrenOfIndex(CatalogueEventsModel *model, const QModelIndex &index) const + { + auto childCount = model->rowCount(index); + auto colCount = model->columnCount(); + emit model->dataChanged(model->index(0, 0, index), + model->index(childCount, colCount, index)); + } }; CatalogueEventsModel::CatalogueEventsModel(QObject *parent) @@ -127,7 +135,6 @@ void CatalogueEventsModel::setEvents(const QVector > &e impl->m_Events = events; impl->m_EventProducts.clear(); - impl->m_EventsWithChanges.clear(); for (auto event : events) { impl->parseEventProduct(event); } @@ -169,10 +176,14 @@ CatalogueEventsModel::getEventProduct(const QModelIndex &index) const void CatalogueEventsModel::addEvent(const std::shared_ptr &event) { - beginInsertRows(QModelIndex(), impl->m_Events.count() - 1, impl->m_Events.count() - 1); + beginInsertRows(QModelIndex(), impl->m_Events.count(), impl->m_Events.count()); impl->m_Events.append(event); impl->parseEventProduct(event); endInsertRows(); + + // Also refreshes its children event products + auto eventIndex = index(impl->m_Events.count(), 0); + impl->refreshChildrenOfIndex(this, eventIndex); } void CatalogueEventsModel::removeEvent(const std::shared_ptr &event) @@ -182,7 +193,6 @@ void CatalogueEventsModel::removeEvent(const std::shared_ptr &event) beginRemoveRows(QModelIndex(), index, index); impl->m_Events.removeAt(index); impl->m_EventProducts.erase(event.get()); - impl->m_EventsWithChanges.erase(event); endRemoveRows(); } } @@ -192,18 +202,41 @@ QVector > CatalogueEventsModel::events() const return impl->m_Events; } -void CatalogueEventsModel::refreshEvent(const std::shared_ptr &event) +void CatalogueEventsModel::refreshEvent(const std::shared_ptr &event, + bool refreshEventProducts) { auto eventIndex = indexOf(event); if (eventIndex.isValid()) { + if (refreshEventProducts) { + // Reparse the associated event products + + auto nbEventProducts = impl->nbEventProducts(event); + auto newNbOfEventProducts = event->getEventProducts().size(); + if (newNbOfEventProducts < nbEventProducts) { + beginRemoveRows(eventIndex, newNbOfEventProducts, nbEventProducts - 1); + impl->m_EventProducts.erase(event.get()); + impl->parseEventProduct(event); + endRemoveRows(); + } + else if (newNbOfEventProducts > nbEventProducts) { + beginInsertRows(eventIndex, nbEventProducts, newNbOfEventProducts - 1); + impl->m_EventProducts.erase(event.get()); + impl->parseEventProduct(event); + endInsertRows(); + } + else { // newNbOfEventProducts == nbEventProducts + impl->m_EventProducts.erase(event.get()); + impl->parseEventProduct(event); + } + } + // Refreshes the event line auto colCount = columnCount(); emit dataChanged(eventIndex, index(eventIndex.row(), colCount)); // Also refreshes its children event products - auto childCount = rowCount(eventIndex); - emit dataChanged(index(0, 0, eventIndex), index(childCount, colCount, eventIndex)); + impl->refreshChildrenOfIndex(this, eventIndex); } else { qCWarning(LOG_CatalogueEventsModel()) << "refreshEvent: event not found."; @@ -220,22 +253,6 @@ QModelIndex CatalogueEventsModel::indexOf(const std::shared_ptr &event) return QModelIndex(); } -void CatalogueEventsModel::setEventHasChanges(const std::shared_ptr &event, - bool hasChanges) -{ - if (hasChanges) { - impl->m_EventsWithChanges.insert(event); - } - else { - impl->m_EventsWithChanges.erase(event); - } -} - -bool CatalogueEventsModel::eventsHasChanges(const std::shared_ptr &event) const -{ - return impl->m_EventsWithChanges.find(event) != impl->m_EventsWithChanges.cend(); -} - QModelIndex CatalogueEventsModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { @@ -354,6 +371,7 @@ QVariant CatalogueEventsModel::headerData(int section, Qt::Orientation orientati void CatalogueEventsModel::sort(int column, Qt::SortOrder order) { + beginResetModel(); std::sort(impl->m_Events.begin(), impl->m_Events.end(), [this, column, order](auto e1, auto e2) { auto data1 = impl->sortData(column, e1); @@ -364,13 +382,13 @@ void CatalogueEventsModel::sort(int column, Qt::SortOrder order) return order == Qt::AscendingOrder ? result : !result; }); - emit dataChanged(QModelIndex(), QModelIndex()); + endResetModel(); emit modelSorted(); } Qt::DropActions CatalogueEventsModel::supportedDragActions() const { - return Qt::CopyAction | Qt::MoveAction; + return Qt::CopyAction; } QStringList CatalogueEventsModel::mimeTypes() const @@ -415,9 +433,10 @@ QMimeData *CatalogueEventsModel::mimeData(const QModelIndexList &indexes) const } } - auto eventsEncodedData - = QByteArray{}; // sqpApp->catalogueController().->mimeDataForEvents(eventList); //TODO - mimeData->setData(MIME_TYPE_EVENT_LIST, eventsEncodedData); + if (!eventList.isEmpty() && eventProductList.isEmpty()) { + auto eventsEncodedData = sqpApp->catalogueController().mimeDataForEvents(eventList); + mimeData->setData(MIME_TYPE_EVENT_LIST, eventsEncodedData); + } if (eventList.count() + eventProductList.count() == 1) { // No time range MIME data if multiple events are dragged diff --git a/gui/src/Catalogue/CatalogueEventsWidget.cpp b/gui/src/Catalogue/CatalogueEventsWidget.cpp index de503c2..0493aa5 100644 --- a/gui/src/Catalogue/CatalogueEventsWidget.cpp +++ b/gui/src/Catalogue/CatalogueEventsWidget.cpp @@ -14,6 +14,7 @@ #include #include #include +#include Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget") @@ -25,14 +26,22 @@ struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate { CatalogueEventsModel *m_Model = nullptr; QStringList m_ZonesForTimeMode; QString m_ZoneForGraphMode; + QVector > m_DisplayedCatalogues; VisualizationWidget *m_VisualizationWidget = nullptr; - void setEvents(const QVector > &events, QTreeView *treeView) + void setEvents(const QVector > &events, CatalogueEventsWidget *widget) { - treeView->setSortingEnabled(false); + widget->ui->treeView->setSortingEnabled(false); m_Model->setEvents(events); - treeView->setSortingEnabled(true); + widget->ui->treeView->setSortingEnabled(true); + + for (auto event : events) { + if (sqpApp->catalogueController().eventHasChanges(event)) { + auto index = m_Model->indexOf(event); + widget->setEventChanges(event, true); + } + } } void addEvent(const std::shared_ptr &event, QTreeView *treeView) @@ -195,6 +204,22 @@ struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate { << "updateGraphMode: not compatible with multiple events selected"; } } + + void getSelectedItems( + QTreeView *treeView, QVector > &events, + QVector, std::shared_ptr > > &eventProducts) + { + for (auto rowIndex : treeView->selectionModel()->selectedRows()) { + auto itemType = m_Model->itemTypeOf(rowIndex); + if (itemType == CatalogueEventsModel::ItemType::Event) { + events << m_Model->getEvent(rowIndex); + } + else if (itemType == CatalogueEventsModel::ItemType::EventProduct) { + eventProducts << qMakePair(m_Model->getParentEvent(rowIndex), + m_Model->getEventProduct(rowIndex)); + } + } + } }; CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) @@ -234,36 +259,32 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) } }); - auto emitSelection = [this]() { + connect(ui->btnRemove, &QToolButton::clicked, [this]() { QVector > events; QVector, std::shared_ptr > > eventProducts; + impl->getSelectedItems(ui->treeView, events, eventProducts); - for (auto rowIndex : ui->treeView->selectionModel()->selectedRows()) { + if (!events.isEmpty() && eventProducts.isEmpty()) { - auto itemType = impl->m_Model->itemTypeOf(rowIndex); - if (itemType == CatalogueEventsModel::ItemType::Event) { - events << impl->m_Model->getEvent(rowIndex); - } - else if (itemType == CatalogueEventsModel::ItemType::EventProduct) { - eventProducts << qMakePair(impl->m_Model->getParentEvent(rowIndex), - impl->m_Model->getEventProduct(rowIndex)); - } - } + if (QMessageBox::warning(this, tr("Remove Event(s)"), + tr("The selected event(s) will be completly removed " + "from the repository!\nAre you sure you want to continue?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + == QMessageBox::Yes) { - if (!events.isEmpty() && eventProducts.isEmpty()) { - emit this->eventsSelected(events); - } - else if (events.isEmpty() && !eventProducts.isEmpty()) { - emit this->eventProductsSelected(eventProducts); - } - else { - emit this->selectionCleared(); + for (auto event : events) { + sqpApp->catalogueController().removeEvent(event); + impl->removeEvent(event, ui->treeView); + } + } } - }; + }); - connect(ui->treeView, &QTreeView::clicked, emitSelection); - connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, emitSelection); + connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection); + connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, + &CatalogueEventsWidget::emitSelection); + ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() { auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1; ui->btnChart->setEnabled(isNotMultiSelection); @@ -275,13 +296,20 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) else if (isNotMultiSelection && ui->btnChart->isChecked()) { impl->updateForGraphMode(ui->treeView); } + + QVector > events; + QVector, std::shared_ptr > > eventProducts; + impl->getSelectedItems(ui->treeView, events, eventProducts); + ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty()); }); ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name, + ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags, QHeaderView::Stretch); ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation, QHeaderView::Fixed); + ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name, + QHeaderView::Interactive); ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation, VALIDATION_COLUMN_SIZE); ui->treeView->header()->setSortIndicatorShown(true); @@ -289,9 +317,11 @@ CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent) connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() { auto allEvents = impl->m_Model->events(); for (auto event : allEvents) { - setEventChanges(event, impl->m_Model->eventsHasChanges(event)); + setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event)); } }); + + populateWithAllEvents(); } CatalogueEventsWidget::~CatalogueEventsWidget() @@ -304,6 +334,11 @@ void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualiz impl->m_VisualizationWidget = visualization; } +void CatalogueEventsWidget::addEvent(const std::shared_ptr &event) +{ + impl->addEvent(event, ui->treeView); +} + void CatalogueEventsWidget::setEventChanges(const std::shared_ptr &event, bool hasChanges) { impl->m_Model->refreshEvent(event); @@ -312,25 +347,55 @@ void CatalogueEventsWidget::setEventChanges(const std::shared_ptr &even auto validationIndex = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation); - if (hasChanges) { - if (ui->treeView->indexWidget(validationIndex) == nullptr) { - auto widget = CatalogueExplorerHelper::buildValidationWidget( - ui->treeView, [this, event]() { setEventChanges(event, false); }, - [this, event]() { setEventChanges(event, false); }); - ui->treeView->setIndexWidget(validationIndex, widget); + if (validationIndex.isValid()) { + if (hasChanges) { + if (ui->treeView->indexWidget(validationIndex) == nullptr) { + auto widget = CatalogueExplorerHelper::buildValidationWidget( + ui->treeView, + [this, event]() { + sqpApp->catalogueController().saveEvent(event); + setEventChanges(event, false); + }, + [this, event]() { + sqpApp->catalogueController().discardEvent(event); + setEventChanges(event, false); + impl->m_Model->refreshEvent(event, true); + emitSelection(); + }); + ui->treeView->setIndexWidget(validationIndex, widget); + } + } + else { + // Note: the widget is destroyed + ui->treeView->setIndexWidget(validationIndex, nullptr); } } else { - // Note: the widget is destroyed - ui->treeView->setIndexWidget(validationIndex, nullptr); + qCWarning(LOG_CatalogueEventsWidget()) + << "setEventChanges: the event is not displayed in the model."; } +} - impl->m_Model->setEventHasChanges(event, hasChanges); +QVector > CatalogueEventsWidget::displayedCatalogues() const +{ + return impl->m_DisplayedCatalogues; +} + +bool CatalogueEventsWidget::isAllEventsDisplayed() const +{ + return impl->m_DisplayedCatalogues.isEmpty() && !impl->m_Model->events().isEmpty(); +} + +bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr &event) const +{ + return impl->m_Model->indexOf(event).isValid(); } void CatalogueEventsWidget::populateWithCatalogues( const QVector > &catalogues) { + impl->m_DisplayedCatalogues = catalogues; + QSet eventIds; QVector > events; @@ -344,11 +409,13 @@ void CatalogueEventsWidget::populateWithCatalogues( } } - impl->setEvents(events, ui->treeView); + impl->setEvents(events, this); } void CatalogueEventsWidget::populateWithAllEvents() { + impl->m_DisplayedCatalogues.clear(); + auto allEvents = sqpApp->catalogueController().retrieveAllEvents(); QVector > events; @@ -356,5 +423,38 @@ void CatalogueEventsWidget::populateWithAllEvents() events << event; } - impl->setEvents(events, ui->treeView); + impl->setEvents(events, this); +} + +void CatalogueEventsWidget::clear() +{ + impl->m_DisplayedCatalogues.clear(); + impl->setEvents({}, this); +} + +void CatalogueEventsWidget::refresh() +{ + if (impl->m_DisplayedCatalogues.isEmpty()) { + populateWithAllEvents(); + } + else { + populateWithCatalogues(impl->m_DisplayedCatalogues); + } +} + +void CatalogueEventsWidget::emitSelection() +{ + QVector > events; + QVector, std::shared_ptr > > eventProducts; + impl->getSelectedItems(ui->treeView, events, eventProducts); + + if (!events.isEmpty() && eventProducts.isEmpty()) { + emit eventsSelected(events); + } + else if (events.isEmpty() && !eventProducts.isEmpty()) { + emit eventProductsSelected(eventProducts); + } + else { + emit selectionCleared(); + } } diff --git a/gui/src/Catalogue/CatalogueExplorer.cpp b/gui/src/Catalogue/CatalogueExplorer.cpp index e409089..e730041 100644 --- a/gui/src/Catalogue/CatalogueExplorer.cpp +++ b/gui/src/Catalogue/CatalogueExplorer.cpp @@ -11,12 +11,17 @@ struct CatalogueExplorer::CatalogueExplorerPrivate { CatalogueActionManager m_ActionManager; + + CatalogueExplorerPrivate(CatalogueExplorer *catalogueExplorer) + : m_ActionManager(catalogueExplorer) + { + } }; CatalogueExplorer::CatalogueExplorer(QWidget *parent) : QDialog(parent, Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint), ui(new Ui::CatalogueExplorer), - impl{spimpl::make_unique_impl()} + impl{spimpl::make_unique_impl(this)} { ui->setupUi(this); @@ -37,16 +42,29 @@ CatalogueExplorer::CatalogueExplorer(QWidget *parent) ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); }); - connect(ui->catalogues, &CatalogueSideBarWidget::trashSelected, - [this]() { ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); }); + connect(ui->catalogues, &CatalogueSideBarWidget::trashSelected, [this]() { + ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); + ui->events->clear(); + }); connect(ui->catalogues, &CatalogueSideBarWidget::allEventsSelected, [this]() { ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); ui->events->populateWithAllEvents(); }); - connect(ui->catalogues, &CatalogueSideBarWidget::selectionCleared, - [this]() { ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); }); + connect(ui->catalogues, &CatalogueSideBarWidget::databaseSelected, [this](auto databaseList) { + QVector > catalogueList; + for (auto database : databaseList) { + catalogueList.append(ui->catalogues->getCatalogues(database)); + } + ui->events->populateWithCatalogues(catalogueList); + ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); + }); + + connect(ui->catalogues, &CatalogueSideBarWidget::selectionCleared, [this]() { + ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); + ui->events->clear(); + }); connect(ui->events, &CatalogueEventsWidget::eventsSelected, [this](auto events) { if (events.count() == 1) { @@ -81,7 +99,10 @@ CatalogueExplorer::CatalogueExplorer(QWidget *parent) }); connect(ui->inspector, &CatalogueInspectorWidget::eventProductUpdated, - [this](auto event, auto eventProduct) { ui->events->setEventChanges(event, true); }); + [this](auto event, auto eventProduct) { + sqpApp->catalogueController().updateEventProduct(eventProduct); + ui->events->setEventChanges(event, true); + }); } CatalogueExplorer::~CatalogueExplorer() @@ -93,3 +114,13 @@ void CatalogueExplorer::setVisualizationWidget(VisualizationWidget *visualizatio { ui->events->setVisualizationWidget(visualization); } + +CatalogueEventsWidget &CatalogueExplorer::eventsWidget() const +{ + return *ui->events; +} + +CatalogueSideBarWidget &CatalogueExplorer::sideBarWidget() const +{ + return *ui->catalogues; +} diff --git a/gui/src/Catalogue/CatalogueInspectorWidget.cpp b/gui/src/Catalogue/CatalogueInspectorWidget.cpp index a51f4a7..57fb08f 100644 --- a/gui/src/Catalogue/CatalogueInspectorWidget.cpp +++ b/gui/src/Catalogue/CatalogueInspectorWidget.cpp @@ -78,8 +78,18 @@ void CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate::connectEventUpda connect(ui->leEventProduct, &QLineEdit::editingFinished, [ui, inspector, this]() { if (ui->leEventProduct->text() != m_DisplayedEventProduct->getProductId()) { + auto oldProductId = m_DisplayedEventProduct->getProductId(); m_DisplayedEventProduct->setProductId(ui->leEventProduct->text()); - emit inspector->eventProductUpdated(m_DisplayedEvent, m_DisplayedEventProduct); + + auto eventProducts = m_DisplayedEvent->getEventProducts(); + for (auto &eventProduct : eventProducts) { + if (eventProduct.getProductId() == oldProductId) { + eventProduct.setProductId(m_DisplayedEventProduct->getProductId()); + } + } + m_DisplayedEvent->setEventProducts(eventProducts); + + emit inspector->eventUpdated(m_DisplayedEvent); } }); @@ -87,7 +97,16 @@ void CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate::connectEventUpda auto time = DateUtils::secondsSinceEpoch(ui->dateTimeEventTStart->dateTime()); if (time != m_DisplayedEventProduct->getTStart()) { m_DisplayedEventProduct->setTStart(time); - emit inspector->eventProductUpdated(m_DisplayedEvent, m_DisplayedEventProduct); + + auto eventProducts = m_DisplayedEvent->getEventProducts(); + for (auto &eventProduct : eventProducts) { + if (eventProduct.getProductId() == m_DisplayedEventProduct->getProductId()) { + eventProduct.setTStart(m_DisplayedEventProduct->getTStart()); + } + } + m_DisplayedEvent->setEventProducts(eventProducts); + + emit inspector->eventUpdated(m_DisplayedEvent); } }); @@ -95,7 +114,16 @@ void CatalogueInspectorWidget::CatalogueInspectorWidgetPrivate::connectEventUpda auto time = DateUtils::secondsSinceEpoch(ui->dateTimeEventTEnd->dateTime()); if (time != m_DisplayedEventProduct->getTEnd()) { m_DisplayedEventProduct->setTEnd(time); - emit inspector->eventProductUpdated(m_DisplayedEvent, m_DisplayedEventProduct); + + auto eventProducts = m_DisplayedEvent->getEventProducts(); + for (auto &eventProduct : eventProducts) { + if (eventProduct.getProductId() == m_DisplayedEventProduct->getProductId()) { + eventProduct.setTEnd(m_DisplayedEventProduct->getTEnd()); + } + } + m_DisplayedEvent->setEventProducts(eventProducts); + + emit inspector->eventUpdated(m_DisplayedEvent); } }); } @@ -145,6 +173,8 @@ void CatalogueInspectorWidget::setEvent(const std::shared_ptr &event) void CatalogueInspectorWidget::setEventProduct(const std::shared_ptr &event, const std::shared_ptr &eventProduct) { + + impl->m_DisplayedEvent = event; impl->m_DisplayedEventProduct = eventProduct; blockSignals(true); @@ -152,7 +182,7 @@ void CatalogueInspectorWidget::setEventProduct(const std::shared_ptr &e showPage(Page::EventProperties); ui->leEventName->setEnabled(false); ui->leEventName->setText(event->getName()); - ui->leEventProduct->setEnabled(true); + ui->leEventProduct->setEnabled(false); ui->leEventProduct->setText(eventProduct->getProductId()); ui->leEventTags->setEnabled(false); diff --git a/gui/src/Catalogue/CatalogueSideBarWidget.cpp b/gui/src/Catalogue/CatalogueSideBarWidget.cpp index e9f5d4e..06db478 100644 --- a/gui/src/Catalogue/CatalogueSideBarWidget.cpp +++ b/gui/src/Catalogue/CatalogueSideBarWidget.cpp @@ -3,7 +3,10 @@ #include #include -#include +#include +#include +#include +#include #include #include #include @@ -13,22 +16,81 @@ Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget") -constexpr auto ALL_EVENT_ITEM_TYPE = QTreeWidgetItem::UserType; -constexpr auto TRASH_ITEM_TYPE = QTreeWidgetItem::UserType + 1; -constexpr auto CATALOGUE_ITEM_TYPE = QTreeWidgetItem::UserType + 2; -constexpr auto DATABASE_ITEM_TYPE = QTreeWidgetItem::UserType + 3; +constexpr auto ALL_EVENT_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 1; +constexpr auto TRASH_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 2; +constexpr auto CATALOGUE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 3; +constexpr auto DATABASE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 4; struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate { - void configureTreeWidget(QTreeWidget *treeWidget); - QTreeWidgetItem *addDatabaseItem(const QString &name, QTreeWidget *treeWidget); - QTreeWidgetItem *getDatabaseItem(const QString &name, QTreeWidget *treeWidget); + CatalogueTreeModel *m_TreeModel = nullptr; + + void configureTreeWidget(QTreeView *treeView); + QModelIndex addDatabaseItem(const QString &name); + CatalogueAbstractTreeItem *getDatabaseItem(const QString &name); void addCatalogueItem(const std::shared_ptr &catalogue, - QTreeWidgetItem *parentDatabaseItem); + const QModelIndex &databaseIndex); + + CatalogueTreeItem *getCatalogueItem(const std::shared_ptr &catalogue) const; + void setHasChanges(bool value, const QModelIndex &index, QTreeView *treeView); + bool hasChanges(const QModelIndex &index, QTreeView *treeView); + + int selectionType(QTreeView *treeView) const + { + auto selectedItems = treeView->selectionModel()->selectedRows(); + if (selectedItems.isEmpty()) { + return CatalogueAbstractTreeItem::DEFAULT_TYPE; + } + else { + auto firstIndex = selectedItems.first(); + auto firstItem = m_TreeModel->item(firstIndex); + if (!firstItem) { + Q_ASSERT(false); + return CatalogueAbstractTreeItem::DEFAULT_TYPE; + } + auto selectionType = firstItem->type(); + + for (auto itemIndex : selectedItems) { + auto item = m_TreeModel->item(itemIndex); + if (!item || item->type() != selectionType) { + // Incoherent multi selection + selectionType = CatalogueAbstractTreeItem::DEFAULT_TYPE; + break; + } + } + + return selectionType; + } + } + + QVector > selectedCatalogues(QTreeView *treeView) const + { + QVector > catalogues; + auto selectedItems = treeView->selectionModel()->selectedRows(); + for (auto itemIndex : selectedItems) { + auto item = m_TreeModel->item(itemIndex); + if (item && item->type() == CATALOGUE_ITEM_TYPE) { + catalogues.append(static_cast(item)->catalogue()); + } + } - CatalogueTreeWidgetItem *getCatalogueItem(const std::shared_ptr &catalogue, - QTreeWidget *treeWidget) const; + return catalogues; + } + + QStringList selectedRepositories(QTreeView *treeView) const + { + QStringList repositories; + auto selectedItems = treeView->selectionModel()->selectedRows(); + for (auto itemIndex : selectedItems) { + auto item = m_TreeModel->item(itemIndex); + if (item && item->type() == DATABASE_ITEM_TYPE) { + repositories.append(item->text()); + } + } + + return repositories; + } }; CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent) @@ -37,82 +99,53 @@ CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent) impl{spimpl::make_unique_impl()} { ui->setupUi(this); - impl->configureTreeWidget(ui->treeWidget); - ui->treeWidget->setColumnCount(2); - ui->treeWidget->header()->setStretchLastSection(false); - ui->treeWidget->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui->treeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch); + impl->m_TreeModel = new CatalogueTreeModel(this); + ui->treeView->setModel(impl->m_TreeModel); + + impl->configureTreeWidget(ui->treeView); + + ui->treeView->header()->setStretchLastSection(false); + ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui->treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch); auto emitSelection = [this]() { - auto selectedItems = ui->treeWidget->selectedItems(); - if (selectedItems.isEmpty()) { - emit this->selectionCleared(); + auto selectionType = impl->selectionType(ui->treeView); + + switch (selectionType) { + case CATALOGUE_ITEM_TYPE: + emit this->catalogueSelected(impl->selectedCatalogues(ui->treeView)); + break; + case DATABASE_ITEM_TYPE: + emit this->databaseSelected(impl->selectedRepositories(ui->treeView)); + break; + case ALL_EVENT_ITEM_TYPE: + emit this->allEventsSelected(); + break; + case TRASH_ITEM_TYPE: + emit this->trashSelected(); + break; + default: + emit this->selectionCleared(); + break; } - else { - QVector > catalogues; - QStringList databases; - int selectionType = selectedItems.first()->type(); - - for (auto item : ui->treeWidget->selectedItems()) { - if (item->type() == selectionType) { - switch (selectionType) { - case CATALOGUE_ITEM_TYPE: - catalogues.append( - static_cast(item)->catalogue()); - break; - case DATABASE_ITEM_TYPE: - selectionType = DATABASE_ITEM_TYPE; - databases.append(item->text(0)); - case ALL_EVENT_ITEM_TYPE: // fallthrough - case TRASH_ITEM_TYPE: // fallthrough - default: - break; - } - } - else { - // Incoherent multi selection - selectionType = -1; - break; - } - } + }; - switch (selectionType) { - case CATALOGUE_ITEM_TYPE: - emit this->catalogueSelected(catalogues); - break; - case DATABASE_ITEM_TYPE: - emit this->databaseSelected(databases); - break; - case ALL_EVENT_ITEM_TYPE: - emit this->allEventsSelected(); - break; - case TRASH_ITEM_TYPE: - emit this->trashSelected(); - break; - default: - emit this->selectionCleared(); - break; - } + connect(ui->treeView, &QTreeView::clicked, emitSelection); + connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, emitSelection); + connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [emitSelection, this](auto index) { + auto selectedIndexes = ui->treeView->selectionModel()->selectedRows(); + if (selectedIndexes.contains(index)) { + emitSelection(); } + auto item = impl->m_TreeModel->item(index); + impl->setHasChanges(true, index, ui->treeView); + }); - }; - - connect(ui->treeWidget, &QTreeWidget::itemClicked, emitSelection); - connect(ui->treeWidget, &QTreeWidget::currentItemChanged, emitSelection); - connect(ui->treeWidget, &QTreeWidget::itemChanged, - [emitSelection, this](auto item, auto column) { - auto selectedItems = ui->treeWidget->selectedItems(); - qDebug() << "ITEM CHANGED" << column; - if (selectedItems.contains(item) && column == 0) { - emitSelection(); - } - }); - - ui->treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); - connect(ui->treeWidget, &QTreeWidget::customContextMenuRequested, this, + ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->treeView, &QTreeView::customContextMenuRequested, this, &CatalogueSideBarWidget::onContextMenuRequested); } @@ -121,24 +154,54 @@ CatalogueSideBarWidget::~CatalogueSideBarWidget() delete ui; } +void CatalogueSideBarWidget::addCatalogue(const std::shared_ptr &catalogue, + const QString &repository) +{ + auto repositoryItem = impl->getDatabaseItem(repository); + impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem)); +} + void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr &catalogue, bool hasChanges) { - if (auto catalogueItem = impl->getCatalogueItem(catalogue, ui->treeWidget)) { - catalogueItem->setHasChanges(hasChanges); - catalogueItem->refresh(); + if (auto catalogueItem = impl->getCatalogueItem(catalogue)) { + auto index = impl->m_TreeModel->indexOf(catalogueItem); + impl->setHasChanges(hasChanges, index, ui->treeView); + // catalogueItem->refresh(); } } +QVector > +CatalogueSideBarWidget::getCatalogues(const QString &repository) const +{ + QVector > result; + auto repositoryItem = impl->getDatabaseItem(repository); + for (auto child : repositoryItem->children()) { + if (child->type() == CATALOGUE_ITEM_TYPE) { + auto catalogueItem = static_cast(child); + result << catalogueItem->catalogue(); + } + else { + qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogues: invalid structure"; + } + } + + return result; +} + void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos) { QMenu menu{this}; - auto currentItem = ui->treeWidget->currentItem(); + auto currentIndex = ui->treeView->currentIndex(); + auto currentItem = impl->m_TreeModel->item(currentIndex); + if (!currentItem) { + return; + } + switch (currentItem->type()) { case CATALOGUE_ITEM_TYPE: - menu.addAction("Rename", - [this, currentItem]() { ui->treeWidget->editItem(currentItem); }); + menu.addAction("Rename", [this, currentIndex]() { ui->treeView->edit(currentIndex); }); break; case DATABASE_ITEM_TYPE: break; @@ -154,59 +217,56 @@ void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos) } if (!menu.isEmpty()) { - menu.exec(ui->treeWidget->mapToGlobal(pos)); + menu.exec(ui->treeView->mapToGlobal(pos)); } } -void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget( - QTreeWidget *treeWidget) +void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget(QTreeView *treeView) { - auto allEventsItem = new QTreeWidgetItem{{"All Events"}, ALL_EVENT_ITEM_TYPE}; - allEventsItem->setIcon(0, QIcon(":/icones/allEvents.png")); - treeWidget->addTopLevelItem(allEventsItem); + auto allEventsItem = new CatalogueTextTreeItem{QIcon{":/icones/allEvents.png"}, "All Events", + ALL_EVENT_ITEM_TYPE}; + auto allEventIndex = m_TreeModel->addTopLevelItem(allEventsItem); + treeView->setCurrentIndex(allEventIndex); - auto trashItem = new QTreeWidgetItem{{"Trash"}, TRASH_ITEM_TYPE}; - trashItem->setIcon(0, QIcon(":/icones/trash.png")); - treeWidget->addTopLevelItem(trashItem); + auto trashItem + = new CatalogueTextTreeItem{QIcon{":/icones/trash.png"}, "Trash", TRASH_ITEM_TYPE}; + m_TreeModel->addTopLevelItem(trashItem); - auto separator = new QFrame{treeWidget}; + auto separator = new QFrame{treeView}; separator->setFrameShape(QFrame::HLine); - auto separatorItem = new QTreeWidgetItem{}; - separatorItem->setFlags(Qt::NoItemFlags); - treeWidget->addTopLevelItem(separatorItem); - treeWidget->setItemWidget(separatorItem, 0, separator); + auto separatorItem + = new CatalogueTextTreeItem{QIcon{}, QString{}, CatalogueAbstractTreeItem::DEFAULT_TYPE}; + separatorItem->setEnabled(false); + auto separatorIndex = m_TreeModel->addTopLevelItem(separatorItem); + treeView->setIndexWidget(separatorIndex, separator); auto repositories = sqpApp->catalogueController().getRepositories(); for (auto dbname : repositories) { - auto db = addDatabaseItem(dbname, treeWidget); - + auto dbIndex = addDatabaseItem(dbname); auto catalogues = sqpApp->catalogueController().retrieveCatalogues(dbname); for (auto catalogue : catalogues) { - addCatalogueItem(catalogue, db); + addCatalogueItem(catalogue, dbIndex); } } - treeWidget->expandAll(); + treeView->expandAll(); } -QTreeWidgetItem * -CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name, - QTreeWidget *treeWidget) +QModelIndex +CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name) { - auto databaseItem = new QTreeWidgetItem{{name}, DATABASE_ITEM_TYPE}; - databaseItem->setIcon(0, QIcon{":/icones/database.png"}); - treeWidget->addTopLevelItem(databaseItem); + auto databaseItem + = new CatalogueTextTreeItem{QIcon{":/icones/database.png"}, {name}, DATABASE_ITEM_TYPE}; + auto databaseIndex = m_TreeModel->addTopLevelItem(databaseItem); - return databaseItem; + return databaseIndex; } -QTreeWidgetItem * -CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name, - QTreeWidget *treeWidget) +CatalogueAbstractTreeItem * +CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name) { - for (auto i = 0; i < treeWidget->topLevelItemCount(); ++i) { - auto item = treeWidget->topLevelItem(i); - if (item->type() == DATABASE_ITEM_TYPE && item->text(0) == name) { + for (auto item : m_TreeModel->topLevelItems()) { + if (item->type() == DATABASE_ITEM_TYPE && item->text() == name) { return item; } } @@ -215,23 +275,21 @@ CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QSt } void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem( - const std::shared_ptr &catalogue, QTreeWidgetItem *parentDatabaseItem) + const std::shared_ptr &catalogue, const QModelIndex &databaseIndex) { - auto catalogueItem = new CatalogueTreeWidgetItem{catalogue, CATALOGUE_ITEM_TYPE}; - catalogueItem->setIcon(0, QIcon{":/icones/catalogue.png"}); - parentDatabaseItem->addChild(catalogueItem); + auto catalogueItem + = new CatalogueTreeItem{catalogue, QIcon{":/icones/catalogue.png"}, CATALOGUE_ITEM_TYPE}; + m_TreeModel->addChildItem(catalogueItem, databaseIndex); } -CatalogueTreeWidgetItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem( - const std::shared_ptr &catalogue, QTreeWidget *treeWidget) const +CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem( + const std::shared_ptr &catalogue) const { - for (auto i = 0; i < treeWidget->topLevelItemCount(); ++i) { - auto item = treeWidget->topLevelItem(i); + for (auto item : m_TreeModel->topLevelItems()) { if (item->type() == DATABASE_ITEM_TYPE) { - for (auto j = 0; j < item->childCount(); ++j) { - auto childItem = item->child(j); + for (auto childItem : item->children()) { if (childItem->type() == CATALOGUE_ITEM_TYPE) { - auto catalogueItem = static_cast(childItem); + auto catalogueItem = static_cast(childItem); if (catalogueItem->catalogue() == catalogue) { return catalogueItem; } @@ -248,3 +306,32 @@ CatalogueTreeWidgetItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate:: return nullptr; } + +void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges(bool value, + const QModelIndex &index, + QTreeView *treeView) +{ + auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation); + if (value) { + if (!hasChanges(validationIndex, treeView)) { + auto widget = CatalogueExplorerHelper::buildValidationWidget( + treeView, [this, validationIndex, + treeView]() { setHasChanges(false, validationIndex, treeView); }, + [this, validationIndex, treeView]() { + setHasChanges(false, validationIndex, treeView); + }); + treeView->setIndexWidget(validationIndex, widget); + } + } + else { + // Note: the widget is destroyed + treeView->setIndexWidget(validationIndex, nullptr); + } +} + +bool CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::hasChanges(const QModelIndex &index, + QTreeView *treeView) +{ + auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation); + return treeView->indexWidget(validationIndex) != nullptr; +} diff --git a/gui/src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp b/gui/src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp new file mode 100644 index 0000000..3fb084c --- /dev/null +++ b/gui/src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp @@ -0,0 +1,81 @@ +#include "Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h" + +struct CatalogueAbstractTreeItem::CatalogueAbstractTreeItemPrivate { + int m_Type; + QVector m_Children; + CatalogueAbstractTreeItem *m_Parent = nullptr; + + CatalogueAbstractTreeItemPrivate(int type) : m_Type(type) {} +}; + +CatalogueAbstractTreeItem::CatalogueAbstractTreeItem(int type) + : impl{spimpl::make_unique_impl(type)} +{ +} + +CatalogueAbstractTreeItem::~CatalogueAbstractTreeItem() +{ + qDeleteAll(impl->m_Children); +} + +void CatalogueAbstractTreeItem::addChild(CatalogueAbstractTreeItem *child) +{ + impl->m_Children << child; + child->impl->m_Parent = this; +} + +QVector CatalogueAbstractTreeItem::children() const +{ + return impl->m_Children; +} + +CatalogueAbstractTreeItem *CatalogueAbstractTreeItem::parent() const +{ + return impl->m_Parent; +} + +int CatalogueAbstractTreeItem::type() const +{ + return impl->m_Type; +} + +QString CatalogueAbstractTreeItem::text(int column) const +{ + return data(0, Qt::DisplayRole).toString(); +} + +QVariant CatalogueAbstractTreeItem::data(int column, int role) const +{ + Q_UNUSED(column); + Q_UNUSED(role); + return QVariant(); +} + +Qt::ItemFlags CatalogueAbstractTreeItem::flags(int column) const +{ + Q_UNUSED(column); + return Qt::NoItemFlags; +} + +bool CatalogueAbstractTreeItem::setData(int column, int role, const QVariant &value) +{ + Q_UNUSED(column); + Q_UNUSED(role); + Q_UNUSED(value); + + return false; +} + +bool CatalogueAbstractTreeItem::canDropMimeData(const QMimeData *data, Qt::DropAction action) +{ + Q_UNUSED(data); + Q_UNUSED(action); + return false; +} + +bool CatalogueAbstractTreeItem::dropMimeData(const QMimeData *data, Qt::DropAction action) +{ + Q_UNUSED(data); + Q_UNUSED(action); + return false; +} diff --git a/gui/src/Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.cpp b/gui/src/Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.cpp new file mode 100644 index 0000000..9360c8c --- /dev/null +++ b/gui/src/Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.cpp @@ -0,0 +1,59 @@ +#include "Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.h" + +#include + +struct CatalogueTextTreeItem::CatalogueTextTreeItemPrivate { + + QString m_Text; + QIcon m_Icon; + bool m_IsEnabled = true; + + CatalogueTextTreeItemPrivate(const QIcon &icon, const QString &text) + : m_Text(text), m_Icon(icon) + { + } +}; + + +CatalogueTextTreeItem::CatalogueTextTreeItem(const QIcon &icon, const QString &text, int type) + : CatalogueAbstractTreeItem(type), + impl{spimpl::make_unique_impl(icon, text)} +{ +} + +QVariant CatalogueTextTreeItem::data(int column, int role) const +{ + if (column > 0) { + return QVariant(); + } + + switch (role) { + case Qt::DisplayRole: + return impl->m_Text; + case Qt::DecorationRole: + return impl->m_Icon; + } + + return QVariant(); +} + +Qt::ItemFlags CatalogueTextTreeItem::flags(int column) const +{ + Q_UNUSED(column); + + if (!impl->m_IsEnabled) { + return Qt::NoItemFlags; + } + + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +QString CatalogueTextTreeItem::text() const +{ + return impl->m_Text; +} + +void CatalogueTextTreeItem::setEnabled(bool value) +{ + impl->m_IsEnabled = value; +} diff --git a/gui/src/Catalogue/CatalogueTreeWidgetItem.cpp b/gui/src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp similarity index 53% rename from gui/src/Catalogue/CatalogueTreeWidgetItem.cpp rename to gui/src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp index 92de0ff..78bcd4b 100644 --- a/gui/src/Catalogue/CatalogueTreeWidgetItem.cpp +++ b/gui/src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp @@ -1,91 +1,95 @@ -#include "Catalogue/CatalogueTreeWidgetItem.h" +#include "Catalogue/CatalogueTreeItems/CatalogueTreeItem.h" #include #include +#include +#include +#include #include #include #include -/// Column in the tree widget where the apply and cancel buttons must appear -const auto APPLY_CANCEL_BUTTONS_COLUMN = 1; - -struct CatalogueTreeWidgetItem::CatalogueTreeWidgetItemPrivate { +struct CatalogueTreeItem::CatalogueTreeItemPrivate { std::shared_ptr m_Catalogue; + QIcon m_Icon; - CatalogueTreeWidgetItemPrivate(std::shared_ptr catalogue) : m_Catalogue(catalogue) + CatalogueTreeItemPrivate(std::shared_ptr catalogue, const QIcon &icon) + : m_Catalogue(catalogue), m_Icon(icon) { } }; -CatalogueTreeWidgetItem::CatalogueTreeWidgetItem(std::shared_ptr catalogue, int type) - : QTreeWidgetItem(type), - impl{spimpl::make_unique_impl(catalogue)} +CatalogueTreeItem::CatalogueTreeItem(std::shared_ptr catalogue, const QIcon &icon, + int type) + : CatalogueAbstractTreeItem(type), + impl{spimpl::make_unique_impl(catalogue, icon)} { - setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable); } -QVariant CatalogueTreeWidgetItem::data(int column, int role) const +QVariant CatalogueTreeItem::data(int column, int role) const { if (column == 0) { switch (role) { case Qt::EditRole: // fallthrough case Qt::DisplayRole: return impl->m_Catalogue->getName(); + case Qt::DecorationRole: + return impl->m_Icon; default: break; } } - return QTreeWidgetItem::data(column, role); + return QVariant(); } -void CatalogueTreeWidgetItem::setData(int column, int role, const QVariant &value) +bool CatalogueTreeItem::setData(int column, int role, const QVariant &value) { + bool result = false; + if (role == Qt::EditRole && column == 0) { auto newName = value.toString(); if (newName != impl->m_Catalogue->getName()) { - setText(0, newName); impl->m_Catalogue->setName(newName); sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue); - setHasChanges(true); + result = true; } } - else { - QTreeWidgetItem::setData(column, role, value); - } -} -std::shared_ptr CatalogueTreeWidgetItem::catalogue() const -{ - return impl->m_Catalogue; + return result; } -void CatalogueTreeWidgetItem::setHasChanges(bool value) +Qt::ItemFlags CatalogueTreeItem::flags(int column) const { - if (value) { - if (!hasChanges()) { - auto widget = CatalogueExplorerHelper::buildValidationWidget( - treeWidget(), [this]() { setHasChanges(false); }, - [this]() { setHasChanges(false); }); - treeWidget()->setItemWidget(this, APPLY_CANCEL_BUTTONS_COLUMN, widget); - } + if (column == 0) { + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable + | Qt::ItemIsDropEnabled; } else { - // Note: the widget is destroyed - treeWidget()->setItemWidget(this, APPLY_CANCEL_BUTTONS_COLUMN, nullptr); + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } } -bool CatalogueTreeWidgetItem::hasChanges() +bool CatalogueTreeItem::canDropMimeData(const QMimeData *data, Qt::DropAction action) { - return treeWidget()->itemWidget(this, APPLY_CANCEL_BUTTONS_COLUMN) != nullptr; + return data->hasFormat(MIME_TYPE_EVENT_LIST); } -void CatalogueTreeWidgetItem::refresh() +bool CatalogueTreeItem::dropMimeData(const QMimeData *data, Qt::DropAction action) { - emitDataChanged(); + Q_ASSERT(canDropMimeData(data, action)); + + auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST)); + // impl->m_Catalogue->addEvents(events); TODO: move events in the new catalogue + // Warning: Check that the events aren't already in the catalogue + // Also check for the repository !!! +} + +std::shared_ptr CatalogueTreeItem::catalogue() const +{ + return impl->m_Catalogue; } diff --git a/gui/src/Catalogue/CatalogueTreeModel.cpp b/gui/src/Catalogue/CatalogueTreeModel.cpp new file mode 100644 index 0000000..86ac456 --- /dev/null +++ b/gui/src/Catalogue/CatalogueTreeModel.cpp @@ -0,0 +1,201 @@ +#include "Catalogue/CatalogueTreeModel.h" +#include + +#include +#include + +#include + +struct CatalogueTreeModel::CatalogueTreeModelPrivate { + std::unique_ptr m_RootItem = nullptr; + + CatalogueTreeModelPrivate() : m_RootItem{std::make_unique()} {} +}; + +CatalogueTreeModel::CatalogueTreeModel(QObject *parent) + : QAbstractItemModel(parent), impl{spimpl::make_unique_impl()} +{ +} + +QModelIndex CatalogueTreeModel::addTopLevelItem(CatalogueAbstractTreeItem *item) +{ + auto nbTopLevelItems = impl->m_RootItem->children().count(); + beginInsertRows(QModelIndex(), nbTopLevelItems, nbTopLevelItems); + impl->m_RootItem->addChild(item); + endInsertRows(); + + emit dataChanged(QModelIndex(), QModelIndex()); + + return index(nbTopLevelItems, 0); +} + +QVector CatalogueTreeModel::topLevelItems() const +{ + return impl->m_RootItem->children(); +} + +void CatalogueTreeModel::addChildItem(CatalogueAbstractTreeItem *child, + const QModelIndex &parentIndex) +{ + auto parentItem = item(parentIndex); + int c = parentItem->children().count(); + beginInsertRows(parentIndex, c, c); + parentItem->addChild(child); + endInsertRows(); + + emit dataChanged(parentIndex, parentIndex); +} + +CatalogueAbstractTreeItem *CatalogueTreeModel::item(const QModelIndex &index) const +{ + return static_cast(index.internalPointer()); +} + +QModelIndex CatalogueTreeModel::indexOf(CatalogueAbstractTreeItem *item, int column) const +{ + auto parentItem = item->parent(); + if (!parentItem) { + return QModelIndex(); + } + + auto row = parentItem->children().indexOf(item); + return createIndex(row, column, item); +} + +QModelIndex CatalogueTreeModel::index(int row, int column, const QModelIndex &parent) const +{ + if (column > 0) { + int a = 0; + } + + if (!hasIndex(row, column, parent)) { + return QModelIndex(); + } + + CatalogueAbstractTreeItem *parentItem = nullptr; + + if (!parent.isValid()) { + parentItem = impl->m_RootItem.get(); + } + else { + parentItem = item(parent); + } + + auto childItem = parentItem->children().value(row); + if (childItem) { + return createIndex(row, column, childItem); + } + + return QModelIndex(); +} + + +QModelIndex CatalogueTreeModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) { + return QModelIndex(); + } + + auto childItem = item(index); + auto parentItem = childItem->parent(); + + if (parentItem == nullptr || parentItem->parent() == nullptr) { + return QModelIndex(); + } + + auto row = parentItem->parent()->children().indexOf(parentItem); + return createIndex(row, 0, parentItem); +} + +int CatalogueTreeModel::rowCount(const QModelIndex &parent) const +{ + CatalogueAbstractTreeItem *parentItem = nullptr; + + if (!parent.isValid()) { + parentItem = impl->m_RootItem.get(); + } + else { + parentItem = item(parent); + } + + return parentItem->children().count(); +} + +int CatalogueTreeModel::columnCount(const QModelIndex &parent) const +{ + return (int)Column::Count; +} + +Qt::ItemFlags CatalogueTreeModel::flags(const QModelIndex &index) const +{ + auto treeItem = item(index); + if (treeItem) { + return treeItem->flags(index.column()); + } + + return Qt::NoItemFlags; +} + +QVariant CatalogueTreeModel::data(const QModelIndex &index, int role) const +{ + auto treeItem = item(index); + if (treeItem) { + return treeItem->data(index.column(), role); + } + + return QModelIndex(); +} + +bool CatalogueTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + auto treeItem = item(index); + if (treeItem) { + auto result = treeItem->setData(index.column(), role, value); + + if (result && index.column() == (int)Column::Name) { + emit itemRenamed(index); + } + + return result; + } + + return false; +} +bool CatalogueTreeModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, + int column, const QModelIndex &parent) const +{ + auto draggedIndex = parent; + auto draggedItem = item(draggedIndex); + if (draggedItem) { + return draggedItem->canDropMimeData(data, action); + } + + return false; +} + +bool CatalogueTreeModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, + int column, const QModelIndex &parent) +{ + bool result = false; + + auto draggedIndex = parent; + auto draggedItem = item(draggedIndex); + if (draggedItem) { + result = draggedItem->dropMimeData(data, action); + if (result) { + emit itemDropped(draggedIndex); + } + } + + return result; +} + +Qt::DropActions CatalogueTreeModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +QStringList CatalogueTreeModel::mimeTypes() const +{ + return {MIME_TYPE_EVENT_LIST}; +} diff --git a/gui/src/Catalogue/CreateEventDialog.cpp b/gui/src/Catalogue/CreateEventDialog.cpp index c3e90c9..2dd6249 100644 --- a/gui/src/Catalogue/CreateEventDialog.cpp +++ b/gui/src/Catalogue/CreateEventDialog.cpp @@ -10,7 +10,8 @@ struct CreateEventDialog::CreateEventDialogPrivate { QVector > m_DisplayedCatalogues; }; -CreateEventDialog::CreateEventDialog(QWidget *parent) +CreateEventDialog::CreateEventDialog(const QVector > &catalogues, + QWidget *parent) : QDialog(parent), ui(new Ui::CreateEventDialog), impl{spimpl::make_unique_impl()} @@ -20,10 +21,9 @@ CreateEventDialog::CreateEventDialog(QWidget *parent) connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - auto catalogues = sqpApp->catalogueController().retrieveCatalogues(); - for (auto cat : catalogues) { + impl->m_DisplayedCatalogues = catalogues; + for (auto cat : impl->m_DisplayedCatalogues) { ui->cbCatalogue->addItem(cat->getName()); - impl->m_DisplayedCatalogues << cat; } } diff --git a/gui/src/SqpApplication.cpp b/gui/src/SqpApplication.cpp index d5dc264..b69af14 100644 --- a/gui/src/SqpApplication.cpp +++ b/gui/src/SqpApplication.cpp @@ -64,9 +64,6 @@ public: m_VariableControllerThread.setObjectName("VariableControllerThread"); m_VisualizationController->moveToThread(&m_VisualizationControllerThread); m_VisualizationControllerThread.setObjectName("VsualizationControllerThread"); - m_CatalogueController->moveToThread(&m_CatalogueControllerThread); - m_CatalogueControllerThread.setObjectName("CatalogueControllerThread"); - // Additionnal init m_VariableController->setTimeController(m_TimeController.get()); @@ -85,9 +82,6 @@ public: m_VisualizationControllerThread.quit(); m_VisualizationControllerThread.wait(); - - m_CatalogueControllerThread.quit(); - m_CatalogueControllerThread.wait(); } std::unique_ptr m_DataSourceController; @@ -101,7 +95,6 @@ public: QThread m_NetworkControllerThread; QThread m_VariableControllerThread; QThread m_VisualizationControllerThread; - QThread m_CatalogueControllerThread; std::unique_ptr m_DragDropGuiController; std::unique_ptr m_ActionsGuiController; @@ -136,16 +129,11 @@ SqpApplication::SqpApplication(int &argc, char **argv) connect(&impl->m_VisualizationControllerThread, &QThread::finished, impl->m_VisualizationController.get(), &VisualizationController::finalize); - connect(&impl->m_CatalogueControllerThread, &QThread::started, - impl->m_CatalogueController.get(), &CatalogueController::initialize); - connect(&impl->m_CatalogueControllerThread, &QThread::finished, - impl->m_CatalogueController.get(), &CatalogueController::finalize); - impl->m_DataSourceControllerThread.start(); impl->m_NetworkControllerThread.start(); impl->m_VariableControllerThread.start(); impl->m_VisualizationControllerThread.start(); - impl->m_CatalogueControllerThread.start(); + impl->m_CatalogueController->initialize(); } SqpApplication::~SqpApplication() diff --git a/gui/ui/Catalogue/CatalogueEventsWidget.ui b/gui/ui/Catalogue/CatalogueEventsWidget.ui index af26468..3ebde47 100644 --- a/gui/ui/Catalogue/CatalogueEventsWidget.ui +++ b/gui/ui/Catalogue/CatalogueEventsWidget.ui @@ -30,6 +30,9 @@ + + false + + @@ -82,6 +85,9 @@ + + false + G diff --git a/gui/ui/Catalogue/CatalogueSideBarWidget.ui b/gui/ui/Catalogue/CatalogueSideBarWidget.ui index 34e03a1..7c99893 100644 --- a/gui/ui/Catalogue/CatalogueSideBarWidget.ui +++ b/gui/ui/Catalogue/CatalogueSideBarWidget.ui @@ -30,6 +30,9 @@ + + false + + @@ -44,6 +47,9 @@ + + false + - @@ -72,18 +78,22 @@ - + + + true + + + QAbstractItemView::DragDrop + QAbstractItemView::ExtendedSelection false - - - 1 - - + + false + diff --git a/plugins/amda/src/AmdaPlugin.cpp b/plugins/amda/src/AmdaPlugin.cpp index 85c51a6..9e32132 100644 --- a/plugins/amda/src/AmdaPlugin.cpp +++ b/plugins/amda/src/AmdaPlugin.cpp @@ -37,6 +37,7 @@ void associateActions(DataSourceItem &item, const QUuid &dataSourceUid) auto actionLabel = QObject::tr( itemType == DataSourceItemType::PRODUCT ? "Load %1 product" : "Load %1 component"); addLoadAction(actionLabel.arg(item.name())); + item.setData(DataSourceItem::ID_DATA_KEY, item.data(AMDA_XML_ID_KEY)); } auto count = item.childCount(); diff --git a/plugins/mockplugin/src/MockPlugin.cpp b/plugins/mockplugin/src/MockPlugin.cpp index 88e7e0a..a3813c8 100644 --- a/plugins/mockplugin/src/MockPlugin.cpp +++ b/plugins/mockplugin/src/MockPlugin.cpp @@ -28,6 +28,7 @@ std::unique_ptr createProductItem(const QVariantHash &data, // Adds plugin name to product metadata result->setData(DataSourceItem::PLUGIN_DATA_KEY, DATA_SOURCE_NAME); + result->setData(DataSourceItem::ID_DATA_KEY, data.value(DataSourceItem::NAME_DATA_KEY)); auto productName = data.value(DataSourceItem::NAME_DATA_KEY).toString();