CatalogueSideBarWidget.cpp
461 lines
| 17.6 KiB
| text/x-c
|
CppLexer
r1095 | #include "Catalogue/CatalogueSideBarWidget.h" | |||
#include "ui_CatalogueSideBarWidget.h" | ||||
r1129 | #include <SqpApplication.h> | |||
#include <Catalogue/CatalogueController.h> | ||||
r1228 | #include <Catalogue/CatalogueExplorerHelper.h> | |||
r1229 | #include <Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.h> | |||
#include <Catalogue/CatalogueTreeItems/CatalogueTreeItem.h> | ||||
r1228 | #include <Catalogue/CatalogueTreeModel.h> | |||
r1129 | #include <CatalogueDao.h> | |||
r1308 | #include <Common/MimeTypesDef.h> | |||
r1129 | #include <ComparaisonPredicate.h> | |||
#include <DBCatalogue.h> | ||||
r1314 | #include <QKeyEvent> | |||
r1141 | #include <QMenu> | |||
r1302 | #include <QMessageBox> | |||
r1308 | #include <QMimeData> | |||
r1141 | ||||
r1147 | Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget") | |||
r1095 | ||||
r1229 | 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; | ||||
r1098 | ||||
r1327 | const auto DEFAULT_CATALOGUE_NAME = QObject::tr("Catalogue"); | |||
r1098 | ||||
struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate { | ||||
r1129 | ||||
r1228 | CatalogueTreeModel *m_TreeModel = nullptr; | |||
void configureTreeWidget(QTreeView *treeView); | ||||
QModelIndex addDatabaseItem(const QString &name); | ||||
r1229 | CatalogueAbstractTreeItem *getDatabaseItem(const QString &name); | |||
r1303 | CatalogueAbstractTreeItem *addCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue, | |||
const QModelIndex &databaseIndex); | ||||
r1147 | ||||
r1229 | CatalogueTreeItem *getCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue) const; | |||
r1302 | void setHasChanges(bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget); | |||
r1228 | bool hasChanges(const QModelIndex &index, QTreeView *treeView); | |||
r1230 | ||||
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<std::shared_ptr<DBCatalogue> > selectedCatalogues(QTreeView *treeView) const | ||||
{ | ||||
QVector<std::shared_ptr<DBCatalogue> > 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<CatalogueTreeItem *>(item)->catalogue()); | ||||
} | ||||
} | ||||
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; | ||||
} | ||||
r1098 | }; | |||
r1095 | CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent) | |||
r1098 | : QWidget(parent), | |||
ui(new Ui::CatalogueSideBarWidget), | ||||
impl{spimpl::make_unique_impl<CatalogueSideBarWidgetPrivate>()} | ||||
r1095 | { | |||
ui->setupUi(this); | ||||
r1103 | ||||
r1228 | impl->m_TreeModel = new CatalogueTreeModel(this); | |||
ui->treeView->setModel(impl->m_TreeModel); | ||||
impl->configureTreeWidget(ui->treeView); | ||||
r1327 | emit catalogueListChanged(); | |||
r1228 | ||||
ui->treeView->header()->setStretchLastSection(false); | ||||
ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); | ||||
r1318 | ui->treeView->header()->setSectionResizeMode((int)CatalogueTreeModel::Column::Name, | |||
QHeaderView::Stretch); | ||||
r1142 | ||||
r1302 | connect(ui->treeView, &QTreeView::clicked, this, &CatalogueSideBarWidget::emitSelection); | |||
connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this, | ||||
&CatalogueSideBarWidget::emitSelection); | ||||
r1303 | connect(ui->btnAdd, &QToolButton::clicked, [this]() { | |||
auto catalogue = std::make_shared<DBCatalogue>(); | ||||
r1327 | catalogue->setName(DEFAULT_CATALOGUE_NAME); | |||
r1303 | sqpApp->catalogueController().addCatalogue(catalogue); | |||
auto item = this->addCatalogue(catalogue, REPOSITORY_DEFAULT); | ||||
this->setCatalogueChanges(catalogue, true); | ||||
ui->treeView->edit(impl->m_TreeModel->indexOf(item)); | ||||
r1302 | ||||
r1303 | }); | |||
r1302 | ||||
r1308 | connect(impl->m_TreeModel, &CatalogueTreeModel::itemDropped, | |||
[this](auto index, auto mimeData, auto action) { | ||||
auto item = impl->m_TreeModel->item(index); | ||||
r1325 | ||||
r1308 | if (item && item->type() == CATALOGUE_ITEM_TYPE) { | |||
auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue(); | ||||
this->setCatalogueChanges(catalogue, true); | ||||
} | ||||
if (action == Qt::MoveAction) { | ||||
/// Display a save button on source catalogues | ||||
auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData( | ||||
mimeData->data(MIME_TYPE_SOURCE_CATALOGUE_LIST)); | ||||
for (auto catalogue : sourceCatalogues) { | ||||
if (auto catalogueItem = impl->getCatalogueItem(catalogue)) { | ||||
r1325 | catalogueItem->replaceCatalogue(catalogue); | |||
r1308 | this->setCatalogueChanges(catalogue, true); | |||
} | ||||
} | ||||
r1325 | ||||
this->emitSelection(); | ||||
r1308 | } | |||
}); | ||||
r1302 | connect(ui->btnRemove, &QToolButton::clicked, [this]() { | |||
QVector<QPair<std::shared_ptr<DBCatalogue>, CatalogueAbstractTreeItem *> > | ||||
cataloguesToItems; | ||||
auto selectedIndexes = ui->treeView->selectionModel()->selectedRows(); | ||||
for (auto index : selectedIndexes) { | ||||
auto item = impl->m_TreeModel->item(index); | ||||
if (item && item->type() == CATALOGUE_ITEM_TYPE) { | ||||
auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue(); | ||||
cataloguesToItems << qMakePair(catalogue, item); | ||||
} | ||||
} | ||||
if (!cataloguesToItems.isEmpty()) { | ||||
if (QMessageBox::warning(this, tr("Remove Catalogue(s)"), | ||||
tr("The selected catalogues(s) will be completly removed " | ||||
"from the repository!\nAre you sure you want to continue?"), | ||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) | ||||
== QMessageBox::Yes) { | ||||
for (auto catalogueToItem : cataloguesToItems) { | ||||
sqpApp->catalogueController().removeCatalogue(catalogueToItem.first); | ||||
impl->m_TreeModel->removeChildItem( | ||||
catalogueToItem.second, | ||||
impl->m_TreeModel->indexOf(catalogueToItem.second->parent())); | ||||
} | ||||
r1317 | emitSelection(); | |||
r1327 | emit catalogueListChanged(); | |||
r1302 | } | |||
} | ||||
}); | ||||
r1105 | ||||
r1302 | connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [this](auto index) { | |||
r1228 | auto selectedIndexes = ui->treeView->selectionModel()->selectedRows(); | |||
if (selectedIndexes.contains(index)) { | ||||
r1302 | this->emitSelection(); | |||
r1228 | } | |||
r1302 | impl->setHasChanges(true, index, this); | |||
r1327 | emit this->catalogueListChanged(); | |||
r1228 | }); | |||
r1141 | ||||
r1228 | ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu); | |||
r1229 | connect(ui->treeView, &QTreeView::customContextMenuRequested, this, | |||
r1141 | &CatalogueSideBarWidget::onContextMenuRequested); | |||
r1095 | } | |||
CatalogueSideBarWidget::~CatalogueSideBarWidget() | ||||
{ | ||||
delete ui; | ||||
} | ||||
r1098 | ||||
r1303 | CatalogueAbstractTreeItem * | |||
CatalogueSideBarWidget::addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue, | ||||
const QString &repository) | ||||
r1231 | { | |||
auto repositoryItem = impl->getDatabaseItem(repository); | ||||
r1327 | auto catalogueItem | |||
= impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem)); | ||||
emit catalogueListChanged(); | ||||
return catalogueItem; | ||||
r1231 | } | |||
r1147 | void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue, | |||
bool hasChanges) | ||||
{ | ||||
r1228 | if (auto catalogueItem = impl->getCatalogueItem(catalogue)) { | |||
auto index = impl->m_TreeModel->indexOf(catalogueItem); | ||||
r1302 | impl->setHasChanges(hasChanges, index, this); | |||
r1229 | // catalogueItem->refresh(); | |||
r1147 | } | |||
} | ||||
r1231 | QVector<std::shared_ptr<DBCatalogue> > | |||
CatalogueSideBarWidget::getCatalogues(const QString &repository) const | ||||
{ | ||||
QVector<std::shared_ptr<DBCatalogue> > result; | ||||
auto repositoryItem = impl->getDatabaseItem(repository); | ||||
for (auto child : repositoryItem->children()) { | ||||
if (child->type() == CATALOGUE_ITEM_TYPE) { | ||||
auto catalogueItem = static_cast<CatalogueTreeItem *>(child); | ||||
result << catalogueItem->catalogue(); | ||||
} | ||||
else { | ||||
qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogues: invalid structure"; | ||||
} | ||||
} | ||||
return result; | ||||
} | ||||
r1302 | void CatalogueSideBarWidget::emitSelection() | |||
{ | ||||
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; | ||||
} | ||||
} | ||||
r1141 | void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos) | |||
{ | ||||
QMenu menu{this}; | ||||
r1228 | auto currentIndex = ui->treeView->currentIndex(); | |||
auto currentItem = impl->m_TreeModel->item(currentIndex); | ||||
if (!currentItem) { | ||||
return; | ||||
} | ||||
r1141 | switch (currentItem->type()) { | |||
case CATALOGUE_ITEM_TYPE: | ||||
r1228 | menu.addAction("Rename", [this, currentIndex]() { ui->treeView->edit(currentIndex); }); | |||
r1141 | break; | |||
case DATABASE_ITEM_TYPE: | ||||
break; | ||||
case ALL_EVENT_ITEM_TYPE: | ||||
break; | ||||
case TRASH_ITEM_TYPE: | ||||
menu.addAction("Empty Trash", []() { | ||||
// TODO | ||||
}); | ||||
break; | ||||
default: | ||||
break; | ||||
} | ||||
if (!menu.isEmpty()) { | ||||
r1228 | menu.exec(ui->treeView->mapToGlobal(pos)); | |||
r1141 | } | |||
} | ||||
r1228 | void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget(QTreeView *treeView) | |||
r1098 | { | |||
r1229 | auto allEventsItem = new CatalogueTextTreeItem{QIcon{":/icones/allEvents.png"}, "All Events", | |||
ALL_EVENT_ITEM_TYPE}; | ||||
r1235 | auto allEventIndex = m_TreeModel->addTopLevelItem(allEventsItem); | |||
treeView->setCurrentIndex(allEventIndex); | ||||
r1098 | ||||
r1229 | auto trashItem | |||
= new CatalogueTextTreeItem{QIcon{":/icones/trash.png"}, "Trash", TRASH_ITEM_TYPE}; | ||||
r1228 | m_TreeModel->addTopLevelItem(trashItem); | |||
r1098 | ||||
r1228 | auto separator = new QFrame{treeView}; | |||
r1098 | separator->setFrameShape(QFrame::HLine); | |||
r1229 | auto separatorItem | |||
= new CatalogueTextTreeItem{QIcon{}, QString{}, CatalogueAbstractTreeItem::DEFAULT_TYPE}; | ||||
separatorItem->setEnabled(false); | ||||
r1228 | auto separatorIndex = m_TreeModel->addTopLevelItem(separatorItem); | |||
treeView->setIndexWidget(separatorIndex, separator); | ||||
r1102 | ||||
r1164 | auto repositories = sqpApp->catalogueController().getRepositories(); | |||
for (auto dbname : repositories) { | ||||
r1228 | auto dbIndex = addDatabaseItem(dbname); | |||
r1164 | auto catalogues = sqpApp->catalogueController().retrieveCatalogues(dbname); | |||
for (auto catalogue : catalogues) { | ||||
r1228 | addCatalogueItem(catalogue, dbIndex); | |||
r1164 | } | |||
r1129 | } | |||
r1102 | ||||
r1228 | treeView->expandAll(); | |||
r1102 | } | |||
r1228 | QModelIndex | |||
CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name) | ||||
r1102 | { | |||
r1229 | auto databaseItem | |||
= new CatalogueTextTreeItem{QIcon{":/icones/database.png"}, {name}, DATABASE_ITEM_TYPE}; | ||||
r1228 | auto databaseIndex = m_TreeModel->addTopLevelItem(databaseItem); | |||
r1102 | ||||
r1228 | return databaseIndex; | |||
r1102 | } | |||
r1229 | CatalogueAbstractTreeItem * | |||
r1228 | CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name) | |||
r1134 | { | |||
r1229 | for (auto item : m_TreeModel->topLevelItems()) { | |||
if (item->type() == DATABASE_ITEM_TYPE && item->text() == name) { | ||||
r1134 | return item; | |||
} | ||||
} | ||||
return nullptr; | ||||
} | ||||
r1303 | CatalogueAbstractTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem( | |||
r1228 | const std::shared_ptr<DBCatalogue> &catalogue, const QModelIndex &databaseIndex) | |||
r1102 | { | |||
r1229 | auto catalogueItem | |||
= new CatalogueTreeItem{catalogue, QIcon{":/icones/catalogue.png"}, CATALOGUE_ITEM_TYPE}; | ||||
r1228 | m_TreeModel->addChildItem(catalogueItem, databaseIndex); | |||
r1303 | ||||
return catalogueItem; | ||||
r1098 | } | |||
r1147 | ||||
r1229 | CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem( | |||
r1228 | const std::shared_ptr<DBCatalogue> &catalogue) const | |||
r1147 | { | |||
r1229 | for (auto item : m_TreeModel->topLevelItems()) { | |||
r1147 | if (item->type() == DATABASE_ITEM_TYPE) { | |||
r1229 | for (auto childItem : item->children()) { | |||
r1147 | if (childItem->type() == CATALOGUE_ITEM_TYPE) { | |||
r1229 | auto catalogueItem = static_cast<CatalogueTreeItem *>(childItem); | |||
r1308 | if (catalogueItem->catalogue()->getUniqId() == catalogue->getUniqId()) { | |||
r1147 | return catalogueItem; | |||
} | ||||
} | ||||
else { | ||||
qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogueItem: Invalid tree " | ||||
"structure. A database item should " | ||||
"only contain catalogues."; | ||||
Q_ASSERT(false); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
return nullptr; | ||||
} | ||||
r1228 | ||||
r1302 | void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges( | |||
bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget) | ||||
r1228 | { | |||
r1302 | std::shared_ptr<DBCatalogue> catalogue = nullptr; | |||
auto item = m_TreeModel->item(index); | ||||
if (item && item->type() == CATALOGUE_ITEM_TYPE) { | ||||
catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue(); | ||||
} | ||||
r1228 | auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation); | |||
if (value) { | ||||
r1302 | if (!hasChanges(validationIndex, sideBarWidget->ui->treeView)) { | |||
r1228 | auto widget = CatalogueExplorerHelper::buildValidationWidget( | |||
r1302 | sideBarWidget->ui->treeView, | |||
[this, validationIndex, sideBarWidget, catalogue]() { | ||||
if (catalogue) { | ||||
sqpApp->catalogueController().saveCatalogue(catalogue); | ||||
r1316 | emit sideBarWidget->catalogueSaved(catalogue); | |||
r1302 | } | |||
setHasChanges(false, validationIndex, sideBarWidget); | ||||
}, | ||||
[this, validationIndex, sideBarWidget, catalogue, item]() { | ||||
if (catalogue) { | ||||
bool removed; | ||||
sqpApp->catalogueController().discardCatalogue(catalogue, removed); | ||||
if (removed) { | ||||
m_TreeModel->removeChildItem(item, | ||||
m_TreeModel->indexOf(item->parent())); | ||||
} | ||||
else { | ||||
m_TreeModel->refresh(m_TreeModel->indexOf(item)); | ||||
setHasChanges(false, validationIndex, sideBarWidget); | ||||
} | ||||
sideBarWidget->emitSelection(); | ||||
} | ||||
r1228 | }); | |||
r1302 | sideBarWidget->ui->treeView->setIndexWidget(validationIndex, widget); | |||
r1318 | sideBarWidget->ui->treeView->header()->resizeSection( | |||
(int)CatalogueTreeModel::Column::Validation, QHeaderView::ResizeToContents); | ||||
r1228 | } | |||
} | ||||
else { | ||||
// Note: the widget is destroyed | ||||
r1302 | sideBarWidget->ui->treeView->setIndexWidget(validationIndex, nullptr); | |||
r1228 | } | |||
} | ||||
bool CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::hasChanges(const QModelIndex &index, | ||||
QTreeView *treeView) | ||||
{ | ||||
auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation); | ||||
return treeView->indexWidget(validationIndex) != nullptr; | ||||
} | ||||
r1314 | ||||
void CatalogueSideBarWidget::keyPressEvent(QKeyEvent *event) | ||||
{ | ||||
switch (event->key()) { | ||||
case Qt::Key_Delete: { | ||||
ui->btnRemove->click(); | ||||
} | ||||
default: | ||||
break; | ||||
} | ||||
} | ||||