##// END OF EJS Templates
Merge pull request 397 from SciQLop-fork develop...
Merge pull request 397 from SciQLop-fork develop Develop

File last commit:

r1245:f0b824bb0975
r1248:63746919da62 merge
Show More
CatalogueEventsModel.cpp
461 lines | 14.7 KiB | text/x-c | CppLexer
/ gui / src / Catalogue / CatalogueEventsModel.cpp
#include "Catalogue/CatalogueEventsModel.h"
#include <Catalogue/CatalogueController.h>
#include <Common/DateUtils.h>
#include <Common/MimeTypesDef.h>
#include <DBEvent.h>
#include <DBEventProduct.h>
#include <DBTag.h>
#include <Data/SqpRange.h>
#include <SqpApplication.h>
#include <Time/TimeController.h>
#include <list>
#include <unordered_map>
#include <QHash>
#include <QMimeData>
Q_LOGGING_CATEGORY(LOG_CatalogueEventsModel, "CatalogueEventsModel")
const auto EVENT_ITEM_TYPE = 1;
const auto EVENT_PRODUCT_ITEM_TYPE = 2;
struct CatalogueEventsModel::CatalogueEventsModelPrivate {
QVector<std::shared_ptr<DBEvent> > m_Events;
std::unordered_map<DBEvent *, QVector<std::shared_ptr<DBEventProduct> > > m_EventProducts;
QStringList columnNames()
{
return QStringList{tr("Event"), tr("TStart"), tr("TEnd"),
tr("Tags"), tr("Product"), tr("")};
}
QVariant sortData(int col, const std::shared_ptr<DBEvent> &event) const
{
if (col == (int)CatalogueEventsModel::Column::Validation) {
auto hasChanges = sqpApp->catalogueController().eventHasChanges(event);
return hasChanges ? true : QVariant();
}
return eventData(col, event);
}
QVariant eventData(int col, const std::shared_ptr<DBEvent> &event) const
{
switch (static_cast<Column>(col)) {
case CatalogueEventsModel::Column::Name:
return event->getName();
case CatalogueEventsModel::Column::TStart:
return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTStart())
: QVariant{};
case CatalogueEventsModel::Column::TEnd:
return nbEventProducts(event) > 0 ? DateUtils::dateTime(event->getTEnd())
: QVariant{};
case CatalogueEventsModel::Column::Product:
return QString::number(nbEventProducts(event)) + " product(s)";
case CatalogueEventsModel::Column::Tags: {
QString tagList;
auto tags = event->getTags();
for (auto tag : tags) {
tagList += tag.getName();
tagList += ' ';
}
return tagList;
}
case CatalogueEventsModel::Column::Validation:
return QVariant();
default:
break;
}
Q_ASSERT(false);
return QStringLiteral("Unknown Data");
}
void parseEventProduct(const std::shared_ptr<DBEvent> &event)
{
for (auto product : event->getEventProducts()) {
m_EventProducts[event.get()].append(std::make_shared<DBEventProduct>(product));
}
}
int nbEventProducts(const std::shared_ptr<DBEvent> &event) const
{
auto eventProductsIt = m_EventProducts.find(event.get());
if (eventProductsIt != m_EventProducts.cend()) {
return m_EventProducts.at(event.get()).count();
}
else {
return 0;
}
}
QVariant eventProductData(int col, const std::shared_ptr<DBEventProduct> &eventProduct) const
{
switch (static_cast<Column>(col)) {
case CatalogueEventsModel::Column::Name:
return eventProduct->getProductId();
case CatalogueEventsModel::Column::TStart:
return DateUtils::dateTime(eventProduct->getTStart());
case CatalogueEventsModel::Column::TEnd:
return DateUtils::dateTime(eventProduct->getTEnd());
case CatalogueEventsModel::Column::Product:
return eventProduct->getProductId();
case CatalogueEventsModel::Column::Tags:
return QString();
case CatalogueEventsModel::Column::Validation:
return QVariant();
default:
break;
}
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)
: QAbstractItemModel(parent), impl{spimpl::make_unique_impl<CatalogueEventsModelPrivate>()}
{
}
void CatalogueEventsModel::setEvents(const QVector<std::shared_ptr<DBEvent> > &events)
{
beginResetModel();
impl->m_Events = events;
impl->m_EventProducts.clear();
for (auto event : events) {
impl->parseEventProduct(event);
}
endResetModel();
}
std::shared_ptr<DBEvent> CatalogueEventsModel::getEvent(const QModelIndex &index) const
{
if (itemTypeOf(index) == CatalogueEventsModel::ItemType::Event) {
return impl->m_Events.value(index.row());
}
else {
return nullptr;
}
}
std::shared_ptr<DBEvent> CatalogueEventsModel::getParentEvent(const QModelIndex &index) const
{
if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
return getEvent(index.parent());
}
else {
return nullptr;
}
}
std::shared_ptr<DBEventProduct>
CatalogueEventsModel::getEventProduct(const QModelIndex &index) const
{
if (itemTypeOf(index) == CatalogueEventsModel::ItemType::EventProduct) {
auto event = static_cast<DBEvent *>(index.internalPointer());
return impl->m_EventProducts.at(event).value(index.row());
}
else {
return nullptr;
}
}
void CatalogueEventsModel::addEvent(const std::shared_ptr<DBEvent> &event)
{
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<DBEvent> &event)
{
auto index = impl->m_Events.indexOf(event);
if (index >= 0) {
beginRemoveRows(QModelIndex(), index, index);
impl->m_Events.removeAt(index);
impl->m_EventProducts.erase(event.get());
endRemoveRows();
}
}
QVector<std::shared_ptr<DBEvent> > CatalogueEventsModel::events() const
{
return impl->m_Events;
}
void CatalogueEventsModel::refreshEvent(const std::shared_ptr<DBEvent> &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
impl->refreshChildrenOfIndex(this, eventIndex);
}
else {
qCWarning(LOG_CatalogueEventsModel()) << "refreshEvent: event not found.";
}
}
QModelIndex CatalogueEventsModel::indexOf(const std::shared_ptr<DBEvent> &event) const
{
auto row = impl->m_Events.indexOf(event);
if (row >= 0) {
return index(row, 0);
}
return QModelIndex();
}
QModelIndex CatalogueEventsModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent)) {
return QModelIndex();
}
switch (itemTypeOf(parent)) {
case CatalogueEventsModel::ItemType::Root:
return createIndex(row, column);
case CatalogueEventsModel::ItemType::Event: {
auto event = getEvent(parent);
return createIndex(row, column, event.get());
}
case CatalogueEventsModel::ItemType::EventProduct:
break;
default:
break;
}
return QModelIndex();
}
QModelIndex CatalogueEventsModel::parent(const QModelIndex &index) const
{
switch (itemTypeOf(index)) {
case CatalogueEventsModel::ItemType::EventProduct: {
auto parentEvent = static_cast<DBEvent *>(index.internalPointer());
auto it
= std::find_if(impl->m_Events.cbegin(), impl->m_Events.cend(),
[parentEvent](auto event) { return event.get() == parentEvent; });
if (it != impl->m_Events.cend()) {
return createIndex(it - impl->m_Events.cbegin(), 0);
}
else {
return QModelIndex();
}
}
case CatalogueEventsModel::ItemType::Root:
break;
case CatalogueEventsModel::ItemType::Event:
break;
default:
break;
}
return QModelIndex();
}
int CatalogueEventsModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0) {
return 0;
}
switch (itemTypeOf(parent)) {
case CatalogueEventsModel::ItemType::Root:
return impl->m_Events.count();
case CatalogueEventsModel::ItemType::Event: {
auto event = getEvent(parent);
return impl->m_EventProducts[event.get()].count();
}
case CatalogueEventsModel::ItemType::EventProduct:
break;
default:
break;
}
return 0;
}
int CatalogueEventsModel::columnCount(const QModelIndex &parent) const
{
return static_cast<int>(CatalogueEventsModel::Column::NbColumn);
}
Qt::ItemFlags CatalogueEventsModel::flags(const QModelIndex &index) const
{
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
}
QVariant CatalogueEventsModel::data(const QModelIndex &index, int role) const
{
if (index.isValid()) {
auto type = itemTypeOf(index);
if (type == CatalogueEventsModel::ItemType::Event) {
auto event = getEvent(index);
switch (role) {
case Qt::DisplayRole:
return impl->eventData(index.column(), event);
break;
}
}
else if (type == CatalogueEventsModel::ItemType::EventProduct) {
auto product = getEventProduct(index);
switch (role) {
case Qt::DisplayRole:
return impl->eventProductData(index.column(), product);
break;
}
}
}
return QVariant{};
}
QVariant CatalogueEventsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
return impl->columnNames().value(section);
}
return QVariant();
}
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);
auto data2 = impl->sortData(column, e2);
auto result = data1.toString() < data2.toString();
return order == Qt::AscendingOrder ? result : !result;
});
endResetModel();
emit modelSorted();
}
Qt::DropActions CatalogueEventsModel::supportedDragActions() const
{
return Qt::CopyAction;
}
QStringList CatalogueEventsModel::mimeTypes() const
{
return {MIME_TYPE_EVENT_LIST, MIME_TYPE_TIME_RANGE};
}
QMimeData *CatalogueEventsModel::mimeData(const QModelIndexList &indexes) const
{
auto mimeData = new QMimeData;
bool isFirst = true;
QVector<std::shared_ptr<DBEvent> > eventList;
QVector<std::shared_ptr<DBEventProduct> > eventProductList;
SqpRange firstTimeRange;
for (const auto &index : indexes) {
if (index.column() == 0) { // only the first column
auto type = itemTypeOf(index);
if (type == ItemType::Event) {
auto event = getEvent(index);
eventList << event;
if (isFirst) {
isFirst = false;
firstTimeRange.m_TStart = event->getTStart();
firstTimeRange.m_TEnd = event->getTEnd();
}
}
else if (type == ItemType::EventProduct) {
auto product = getEventProduct(index);
eventProductList << product;
if (isFirst) {
isFirst = false;
firstTimeRange.m_TStart = product->getTStart();
firstTimeRange.m_TEnd = product->getTEnd();
}
}
}
}
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
auto timeEncodedData = TimeController::mimeDataForTimeRange(firstTimeRange);
mimeData->setData(MIME_TYPE_TIME_RANGE, timeEncodedData);
}
return mimeData;
}
CatalogueEventsModel::ItemType CatalogueEventsModel::itemTypeOf(const QModelIndex &index) const
{
if (!index.isValid()) {
return ItemType::Root;
}
else if (index.internalPointer() == nullptr) {
return ItemType::Event;
}
else {
return ItemType::EventProduct;
}
}