diff --git a/src/akonadi/akonadilivequeryhelpers.h b/src/akonadi/akonadilivequeryhelpers.h --- a/src/akonadi/akonadilivequeryhelpers.h +++ b/src/akonadi/akonadilivequeryhelpers.h @@ -46,6 +46,7 @@ CollectionFetchFunction fetchAllCollections(StorageInterface::FetchContentTypes contentTypes) const; CollectionFetchFunction fetchCollections(const Collection &root, StorageInterface::FetchContentTypes contentTypes) const; + CollectionFetchFunction fetchItemCollection(const Item &item) const; ItemFetchFunction fetchItems(StorageInterface::FetchContentTypes contentTypes) const; ItemFetchFunction fetchItems(const Collection &collection) const; diff --git a/src/akonadi/akonadilivequeryhelpers.cpp b/src/akonadi/akonadilivequeryhelpers.cpp --- a/src/akonadi/akonadilivequeryhelpers.cpp +++ b/src/akonadi/akonadilivequeryhelpers.cpp @@ -27,6 +27,7 @@ #include "akonadi/akonadicollectionfetchjobinterface.h" #include "akonadi/akonadiitemfetchjobinterface.h" #include "akonadi/akonaditagfetchjobinterface.h" +#include "akonadi/akonadistorageinterface.h" #include "utils/jobhandler.h" @@ -193,6 +194,21 @@ }; } +LiveQueryHelpers::CollectionFetchFunction LiveQueryHelpers::fetchItemCollection(const Item& item) const +{ + auto storage = m_storage; + return [storage, item] (const Domain::LiveQueryInput::AddFunction &add) { + auto job = storage->fetchCollections(item.parentCollection(), StorageInterface::Base, StorageInterface::AllContent); + Utils::JobHandler::install(job->kjob(), [storage, job, add] { + if (job->kjob()->error() != KJob::NoError) + return; + auto collection = job->collections().at(0); + add(collection); + }); + }; +} + + LiveQueryHelpers::ItemFetchFunction LiveQueryHelpers::fetchSiblings(const Item &item) const { auto storage = m_storage; diff --git a/src/akonadi/akonadilivequeryintegrator.h b/src/akonadi/akonadilivequeryintegrator.h --- a/src/akonadi/akonadilivequeryintegrator.h +++ b/src/akonadi/akonadilivequeryintegrator.h @@ -312,6 +312,24 @@ return m_serializer->representsItem(output, input); } +template<> +inline Domain::DataSource::Ptr LiveQueryIntegrator::create(const Item &input) +{ + return m_serializer->createDataSourceFromCollection(input.parentCollection(), SerializerInterface::BaseName); +} + +template<> +inline void LiveQueryIntegrator::update(const Item &input, Domain::DataSource::Ptr &output) +{ + m_serializer->updateDataSourceFromCollection(output, input.parentCollection(), SerializerInterface::BaseName); +} + +template<> +inline bool LiveQueryIntegrator::represents(const Item &input, const Domain::DataSource::Ptr &output) +{ + return m_serializer->representsCollection(output, input.parentCollection()); +} + template<> inline Domain::Project::Ptr LiveQueryIntegrator::create(const Item &input) { diff --git a/src/akonadi/akonaditaskqueries.h b/src/akonadi/akonaditaskqueries.h --- a/src/akonadi/akonaditaskqueries.h +++ b/src/akonadi/akonaditaskqueries.h @@ -51,6 +51,9 @@ typedef Domain::QueryResult ProjectResult; typedef Domain::LiveQueryOutput ProjectQueryOutput; + typedef Domain::QueryResult DataSourceResult; + typedef Domain::LiveQueryOutput DataSourceQueryOutput; + TaskQueries(const StorageInterface::Ptr &storage, const SerializerInterface::Ptr &serializer, const MonitorInterface::Ptr &monitor, @@ -66,6 +69,8 @@ TaskResult::Ptr findWorkdayTopLevel() const Q_DECL_OVERRIDE; ContextResult::Ptr findContexts(Domain::Task::Ptr task) const Q_DECL_OVERRIDE; ProjectResult::Ptr findProject(Domain::Task::Ptr task) const Q_DECL_OVERRIDE; + DataSourceResult::Ptr findDataSource(Domain::Task::Ptr task) const Q_DECL_OVERRIDE; + private slots: void onWorkdayPollTimeout(); @@ -81,6 +86,7 @@ mutable TaskQueryOutput::Ptr m_findAll; mutable QHash m_findChildren; mutable QHash m_findProject; + mutable QHash m_findDataSource; mutable TaskQueryOutput::Ptr m_findTopLevel; mutable TaskQueryOutput::Ptr m_findInboxTopLevel; mutable TaskQueryOutput::Ptr m_findWorkdayTopLevel; diff --git a/src/akonadi/akonaditaskqueries.cpp b/src/akonadi/akonaditaskqueries.cpp --- a/src/akonadi/akonaditaskqueries.cpp +++ b/src/akonadi/akonaditaskqueries.cpp @@ -95,6 +95,17 @@ return query->result(); } +TaskQueries::DataSourceResult::Ptr TaskQueries::findDataSource(Domain::Task::Ptr task) const +{ + Akonadi::Item item = m_serializer->createItemFromTask(task); + auto &query = m_findDataSource[item.id()]; + auto fetch = m_helpers->fetchItemCollection(item); + auto predicate = [] (const Akonadi::Collection &) { return true; }; + + m_integrator->bind("TaskQueries::findDataSource", query, fetch, predicate); + return query->result(); +} + TaskQueries::TaskResult::Ptr TaskQueries::findTopLevel() const { auto fetch = m_helpers->fetchItems(StorageInterface::Tasks); diff --git a/src/domain/taskqueries.h b/src/domain/taskqueries.h --- a/src/domain/taskqueries.h +++ b/src/domain/taskqueries.h @@ -28,6 +28,7 @@ #include "queryresult.h" #include "task.h" #include "project.h" +#include "datasource.h" namespace Domain { @@ -52,6 +53,8 @@ virtual QueryResult::Ptr findContexts(Task::Ptr task) const = 0; virtual QueryResult::Ptr findProject(Task::Ptr task) const = 0; + + virtual QueryResult::Ptr findDataSource(Task::Ptr task) const = 0; }; } diff --git a/src/presentation/taskinboxpagemodel.cpp b/src/presentation/taskinboxpagemodel.cpp --- a/src/presentation/taskinboxpagemodel.cpp +++ b/src/presentation/taskinboxpagemodel.cpp @@ -78,6 +78,8 @@ QAbstractItemModel *TaskInboxPageModel::createCentralListModel() { + using AdditionalInfo = Domain::QueryResult::Ptr; + auto query = [this](const Domain::Task::Ptr &task) -> Domain::QueryResultInterface::Ptr { if (!task) return m_taskQueries->findInboxTopLevel(); @@ -94,18 +96,42 @@ | Qt::ItemIsDropEnabled; }; - auto data = [](const Domain::Task::Ptr &task, int role, int) -> QVariant { - if (role != Qt::DisplayRole - && role != Qt::EditRole - && role != Qt::CheckStateRole) { - return QVariant(); + auto data = [](const Domain::Task::Ptr &task, int role, const AdditionalInfo &dataSourceQueryResult) -> QVariant { + switch (role) { + case Qt::DisplayRole: + case Qt::EditRole: + return task->title(); + case Qt::CheckStateRole: + return task->isDone() ? Qt::Checked : Qt::Unchecked; + case Presentation::QueryTreeModelBase::AdditionalInfoRole: + if (dataSourceQueryResult && !dataSourceQueryResult->data().isEmpty()) { + Domain::DataSource::Ptr dataSource = dataSourceQueryResult->data().at(0); + return dataSource->name(); + } + return QString(); + default: + break; } + return QVariant(); + }; - if (role == Qt::DisplayRole || role == Qt::EditRole) { - return task->title(); - } else { - return task->isDone() ? Qt::Checked : Qt::Unchecked; + auto fetchAdditionalInfo = [this](const QModelIndex &index, const Domain::Artifact::Ptr &artifact) -> AdditionalInfo { + if (index.parent().isValid()) // children are in the same collection as their parent, so the same datasource + return nullptr; + if (auto task = artifact.dynamicCast()) { + AdditionalInfo datasourceQueryResult = m_taskQueries->findDataSource(task); + if (datasourceQueryResult) { + QPersistentModelIndex persistentIndex(index); + datasourceQueryResult->addPostInsertHandler([persistentIndex](const Domain::DataSource::Ptr &, int) { + // When a datasource was found (inserted into the result), update the rendering of the item + auto model = const_cast(persistentIndex.model()); + model->dataChanged(persistentIndex, persistentIndex); + }); + } + return datasourceQueryResult; } + return nullptr; + }; auto setData = [this](const Domain::Task::Ptr &task, const QVariant &value, int role) { @@ -172,5 +198,5 @@ return data; }; - return new QueryTreeModel(query, flags, data, setData, drop, drag, nullptr, this); + return new QueryTreeModel(query, flags, data, setData, drop, drag, fetchAdditionalInfo, this); } diff --git a/tests/units/presentation/taskinboxpagemodeltest.cpp b/tests/units/presentation/taskinboxpagemodeltest.cpp --- a/tests/units/presentation/taskinboxpagemodeltest.cpp +++ b/tests/units/presentation/taskinboxpagemodeltest.cpp @@ -78,6 +78,7 @@ taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(inboxResult); taskQueriesMock(&Domain::TaskQueries::findChildren).when(rootTask).thenReturn(taskResult); taskQueriesMock(&Domain::TaskQueries::findChildren).when(childTask).thenReturn(Domain::QueryResult::Ptr()); + taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); Utils::MockObject taskRepositoryMock; @@ -209,6 +210,7 @@ taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(taskResult); taskQueriesMock(&Domain::TaskQueries::findChildren).when(task1).thenReturn(Domain::QueryResult::Ptr()); taskQueriesMock(&Domain::TaskQueries::findChildren).when(task2).thenReturn(Domain::QueryResult::Ptr()); + taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); Utils::MockObject taskRepositoryMock; taskRepositoryMock(&Domain::TaskRepository::createChild).when(any(), @@ -274,6 +276,7 @@ taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(taskResult); taskQueriesMock(&Domain::TaskQueries::findChildren).when(task1).thenReturn(Domain::QueryResult::Ptr()); taskQueriesMock(&Domain::TaskQueries::findChildren).when(task2).thenReturn(Domain::QueryResult::Ptr()); + taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); Utils::MockObject taskRepositoryMock; taskRepositoryMock(&Domain::TaskRepository::remove).when(task2).thenReturn(new FakeJob(this)); @@ -306,6 +309,7 @@ taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(inboxResult); taskQueriesMock(&Domain::TaskQueries::findChildren).when(task1).thenReturn(Domain::QueryResult::Ptr()); taskQueriesMock(&Domain::TaskQueries::findChildren).when(task2).thenReturn(Domain::QueryResult::Ptr()); + taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); Utils::MockObject taskRepositoryMock; auto job = new FakeJob(this); @@ -342,6 +346,7 @@ taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(taskResult); taskQueriesMock(&Domain::TaskQueries::findChildren).when(task1).thenReturn(Domain::QueryResult::Ptr()); taskQueriesMock(&Domain::TaskQueries::findChildren).when(task2).thenReturn(Domain::QueryResult::Ptr()); + taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); Utils::MockObject taskRepositoryMock; taskRepositoryMock(&Domain::TaskRepository::promoteToProject).when(task2).thenReturn(new FakeJob(this)); @@ -374,6 +379,7 @@ taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(inboxResult); taskQueriesMock(&Domain::TaskQueries::findChildren).when(task1).thenReturn(Domain::QueryResult::Ptr()); taskQueriesMock(&Domain::TaskQueries::findChildren).when(task2).thenReturn(Domain::QueryResult::Ptr()); + taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); Utils::MockObject taskRepositoryMock; auto job = new FakeJob(this); @@ -410,6 +416,7 @@ Utils::MockObject taskQueriesMock; taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(inboxResult); taskQueriesMock(&Domain::TaskQueries::findChildren).when(rootTask).thenReturn(taskResult); + taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); Utils::MockObject taskRepositoryMock; Presentation::TaskInboxPageModel inbox(taskQueriesMock.getInstance(), @@ -448,6 +455,7 @@ Utils::MockObject taskQueriesMock; taskQueriesMock(&Domain::TaskQueries::findInboxTopLevel).when().thenReturn(inboxResult); taskQueriesMock(&Domain::TaskQueries::findChildren).when(rootTask).thenReturn(taskResult); + taskQueriesMock(&Domain::TaskQueries::findDataSource).when(any()).thenReturn(Domain::QueryResult::Ptr()); Utils::MockObject taskRepositoryMock; Presentation::TaskInboxPageModel inbox(taskQueriesMock.getInstance(), @@ -505,6 +513,7 @@ taskQueriesMock(&Domain::TaskQueries::findChildren).when(topLevelTask).thenReturn(taskResult); taskQueriesMock(&Domain::TaskQueries::findChildren).when(childTask).thenReturn(Domain::QueryResult::Ptr()); taskQueriesMock(&Domain::TaskQueries::findChildren).when(childTask2).thenReturn(Domain::QueryResult::Ptr()); + taskQueriesMock(&Domain::TaskQueries::findDataSource).when(childTask).thenReturn(Domain::QueryResult::Ptr()); Utils::MockObject taskRepositoryMock;