diff --git a/src/presentation/contextpagemodel.cpp b/src/presentation/contextpagemodel.cpp index 5506c6a2..7954620e 100644 --- a/src/presentation/contextpagemodel.cpp +++ b/src/presentation/contextpagemodel.cpp @@ -1,207 +1,207 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens Copyright 2014 Rémi Benoit This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "contextpagemodel.h" #include #include #include "domain/task.h" #include "domain/contextqueries.h" #include "domain/contextrepository.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/querytreemodel.h" using namespace Presentation; ContextPageModel::ContextPageModel(const Domain::Context::Ptr &context, const Domain::ContextQueries::Ptr &contextQueries, const Domain::ContextRepository::Ptr &contextRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : PageModel(parent), m_context(context), m_contextQueries(contextQueries), m_contextRepository(contextRepository), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } Domain::Context::Ptr ContextPageModel::context() const { return m_context; } Domain::Artifact::Ptr ContextPageModel::addItem(const QString &title, const QModelIndex &parentIndex) { - const auto parentData = parentIndex.data(QueryTreeModel::ObjectRole); + const auto parentData = parentIndex.data(QueryTreeModelBase::ObjectRole); const auto parentArtifact = parentData.value(); const auto parentTask = parentArtifact.objectCast(); auto task = Domain::Task::Ptr::create(); task->setTitle(title); const auto job = parentTask ? m_taskRepository->createChild(task, parentTask) : m_taskRepository->createInContext(task, m_context); installHandler(job, i18n("Cannot add task %1 in context %2", title, m_context->name())); return task; } void ContextPageModel::removeItem(const QModelIndex &index) { - QVariant data = index.data(QueryTreeModel::ObjectRole); + QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); const auto job = index.parent().isValid() ? m_taskRepository->dissociate(task) : m_contextRepository->dissociate(m_context, task); installHandler(job, i18n("Cannot remove task %1 from context %2", task->title(), m_context->name())); } void ContextPageModel::promoteItem(const QModelIndex &index) { - QVariant data = index.data(QueryTreeModel::ObjectRole); + QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->promoteToProject(task); installHandler(job, i18n("Cannot promote task %1 to be a project", task->title())); } QAbstractItemModel *ContextPageModel::createCentralListModel() { auto query = [this] (const Domain::Task::Ptr &task) -> Domain::QueryResultInterface::Ptr { if (!task) return m_contextQueries->findTopLevelTasks(m_context); else return m_taskQueries->findChildren(task); }; auto flags = [] (const Domain::Task::Ptr &task) { Q_UNUSED(task); return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled; }; auto data = [] (const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return task->title(); } else if (role == Qt::CheckStateRole){ return task->isDone() ? Qt::Checked : Qt::Unchecked; } else { return QVariant(); } }; auto setData = [this] (const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) return false; const auto currentTitle = task->title(); if (role == Qt::EditRole) task->setTitle(value.toString()); else task->setDone(value.toInt() == Qt::Checked); const auto job = m_taskRepository->update(task); installHandler(job, i18n("Cannot modify task %1 in context %2", currentTitle, m_context->name())); return true; }; auto drop = [this] (const QMimeData *mimeData, Qt::DropAction, const Domain::Task::Ptr &parentTask) { if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedArtifacts = mimeData->property("objects").value(); if (droppedArtifacts.isEmpty()) return false; if (std::any_of(droppedArtifacts.begin(), droppedArtifacts.end(), [](const Domain::Artifact::Ptr &droppedArtifact) { return !droppedArtifact.objectCast(); })) { return false; } using namespace std::placeholders; auto associate = std::function(); auto dissociate = std::function(); auto parentTitle = QString(); if (parentTask) { associate = std::bind(&Domain::TaskRepository::associate, m_taskRepository, parentTask, _1); dissociate = [] (Domain::Task::Ptr) -> KJob* { return Q_NULLPTR; }; parentTitle = parentTask->title(); } else { associate = std::bind(&Domain::ContextRepository::associate, m_contextRepository, m_context, _1); dissociate = std::bind(&Domain::TaskRepository::dissociate, m_taskRepository, _1); parentTitle = m_context->name(); } foreach(const Domain::Artifact::Ptr &droppedArtifact, droppedArtifacts) { auto childTask = droppedArtifact.objectCast(); auto job = associate(childTask); installHandler(job, i18n("Cannot move task %1 as sub-task of %2", childTask->title(), parentTitle)); job = dissociate(childTask); if (job) installHandler(job, i18n("Cannot dissociate task %1 from its parent", childTask->title())); } return true; }; auto drag = [] (const Domain::Task::List &tasks) -> QMimeData* { if (tasks.isEmpty()) return Q_NULLPTR; auto draggedArtifacts = Domain::Artifact::List(); draggedArtifacts.reserve(tasks.size()); foreach (const Domain::Task::Ptr &task, tasks) { draggedArtifacts.append(task.objectCast()); } auto data = new QMimeData(); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(draggedArtifacts)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, this); } diff --git a/src/presentation/noteinboxpagemodel.cpp b/src/presentation/noteinboxpagemodel.cpp index 36fce2c9..2516f40b 100644 --- a/src/presentation/noteinboxpagemodel.cpp +++ b/src/presentation/noteinboxpagemodel.cpp @@ -1,125 +1,125 @@ /* This file is part of Zanshin Copyright 2015 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "noteinboxpagemodel.h" #include #include #include "presentation/querytreemodel.h" using namespace Presentation; NoteInboxPageModel::NoteInboxPageModel(const Domain::NoteQueries::Ptr ¬eQueries, const Domain::NoteRepository::Ptr ¬eRepository, QObject *parent) : PageModel(parent), m_noteQueries(noteQueries), m_noteRepository(noteRepository) { } Domain::Artifact::Ptr NoteInboxPageModel::addItem(const QString &title, const QModelIndex &) { auto note = Domain::Note::Ptr::create(); note->setTitle(title); const auto job = m_noteRepository->create(note); installHandler(job, i18n("Cannot add note %1 in Inbox", title)); return note; } void NoteInboxPageModel::removeItem(const QModelIndex &index) { - QVariant data = index.data(QueryTreeModel::ObjectRole); + QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto artifact = data.value(); auto note = artifact.objectCast(); Q_ASSERT(note); const auto job = m_noteRepository->remove(note); installHandler(job, i18n("Cannot remove note %1 from Inbox", note->title())); } void NoteInboxPageModel::promoteItem(const QModelIndex &) { qFatal("Not supported"); } QAbstractItemModel *NoteInboxPageModel::createCentralListModel() { auto query = [this](const Domain::Note::Ptr ¬e) -> Domain::QueryResultInterface::Ptr { if (!note) return m_noteQueries->findInbox(); else return Domain::QueryResult::Ptr(); }; auto flags = [](const Domain::Note::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled; }; auto data = [](const Domain::Note::Ptr ¬e, int role) -> QVariant { if (role == Qt::DisplayRole || role == Qt::EditRole) { return note->title(); } else { return QVariant(); } }; auto setData = [this](const Domain::Note::Ptr ¬e, const QVariant &value, int role) { if (role != Qt::EditRole) { return false; } const auto currentTitle = note->title(); note->setTitle(value.toString()); const auto job = m_noteRepository->update(note); installHandler(job, i18n("Cannot modify note %1 in Inbox", currentTitle)); return true; }; auto drop = [](const QMimeData *, Qt::DropAction, const Domain::Artifact::Ptr &) { return false; }; auto drag = [](const Domain::Note::List ¬es) -> QMimeData* { if (notes.isEmpty()) return Q_NULLPTR; auto artifacts = Domain::Artifact::List(); artifacts.reserve(notes.size()); std::copy(notes.constBegin(), notes.constEnd(), std::back_inserter(artifacts)); auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(artifacts)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, this); } diff --git a/src/presentation/projectpagemodel.cpp b/src/presentation/projectpagemodel.cpp index b974b1e3..2d79da1a 100644 --- a/src/presentation/projectpagemodel.cpp +++ b/src/presentation/projectpagemodel.cpp @@ -1,192 +1,192 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "projectpagemodel.h" #include #include #include "presentation/querytreemodel.h" using namespace Presentation; ProjectPageModel::ProjectPageModel(const Domain::Project::Ptr &project, const Domain::ProjectQueries::Ptr &projectQueries, const Domain::ProjectRepository::Ptr &projectRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : PageModel(parent), m_projectQueries(projectQueries), m_projectRepository(projectRepository), m_project(project), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } Domain::Project::Ptr ProjectPageModel::project() const { return m_project; } Domain::Artifact::Ptr ProjectPageModel::addItem(const QString &title, const QModelIndex &parentIndex) { - const auto parentData = parentIndex.data(QueryTreeModel::ObjectRole); + const auto parentData = parentIndex.data(QueryTreeModelBase::ObjectRole); const auto parentArtifact = parentData.value(); const auto parentTask = parentArtifact.objectCast(); auto task = Domain::Task::Ptr::create(); task->setTitle(title); const auto job = parentTask ? m_taskRepository->createChild(task, parentTask) : m_taskRepository->createInProject(task, m_project); installHandler(job, i18n("Cannot add task %1 in project %2", title, m_project->name())); return task; } void ProjectPageModel::removeItem(const QModelIndex &index) { - QVariant data = index.data(QueryTreeModel::ObjectRole); + QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->remove(task); installHandler(job, i18n("Cannot remove task %1 from project %2", task->title(), m_project->name())); } void ProjectPageModel::promoteItem(const QModelIndex &index) { - QVariant data = index.data(QueryTreeModel::ObjectRole); + QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->promoteToProject(task); installHandler(job, i18n("Cannot promote task %1 to be a project", task->title())); } QAbstractItemModel *ProjectPageModel::createCentralListModel() { auto query = [this](const Domain::Task::Ptr &task) -> Domain::QueryResultInterface::Ptr { if (!task) return m_projectQueries->findTopLevel(m_project); else return m_taskQueries->findChildren(task); }; auto flags = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled; }; auto data = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return task->title(); } else { return task->isDone() ? Qt::Checked : Qt::Unchecked; } }; auto setData = [this](const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } const auto currentTitle = task->title(); if (role == Qt::EditRole) task->setTitle(value.toString()); else task->setDone(value.toInt() == Qt::Checked); const auto job = m_taskRepository->update(task); installHandler(job, i18n("Cannot modify task %1 in project %2", currentTitle, m_project->name())); return true; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const Domain::Task::Ptr &parentTask) { if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedArtifacts = mimeData->property("objects").value(); if (droppedArtifacts.isEmpty()) return false; if (std::any_of(droppedArtifacts.begin(), droppedArtifacts.end(), [](const Domain::Artifact::Ptr &droppedArtifact) { return !droppedArtifact.objectCast(); })) { return false; } using namespace std::placeholders; auto associate = std::function(); auto parentTitle = QString(); if (parentTask) { associate = std::bind(&Domain::TaskRepository::associate, m_taskRepository, parentTask, _1); parentTitle = parentTask->title(); } else { associate = std::bind(&Domain::ProjectRepository::associate, m_projectRepository, m_project, _1); parentTitle = m_project->name(); } foreach(const Domain::Artifact::Ptr &droppedArtifact, droppedArtifacts) { auto childTask = droppedArtifact.objectCast(); const auto job = associate(childTask); installHandler(job, i18n("Cannot move task %1 as a sub-task of %2", childTask->title(), parentTitle)); } return true; }; auto drag = [](const Domain::Task::List &tasks) -> QMimeData* { if (tasks.isEmpty()) return Q_NULLPTR; auto draggedArtifacts = Domain::Artifact::List(); draggedArtifacts.reserve(tasks.size()); foreach (const Domain::Task::Ptr &task, tasks) { draggedArtifacts.append(task.objectCast()); } auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(draggedArtifacts)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, this); } diff --git a/src/presentation/tagpagemodel.cpp b/src/presentation/tagpagemodel.cpp index fd811b51..62c49710 100644 --- a/src/presentation/tagpagemodel.cpp +++ b/src/presentation/tagpagemodel.cpp @@ -1,146 +1,146 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens Copyright 2014 Rémi Benoit This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "tagpagemodel.h" #include #include #include "domain/noterepository.h" #include "domain/task.h" #include "domain/tagqueries.h" #include "domain/tagrepository.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/querytreemodel.h" using namespace Presentation; TagPageModel::TagPageModel(const Domain::Tag::Ptr &tag, const Domain::TagQueries::Ptr &tagQueries, const Domain::TagRepository::Ptr &tagRepository, const Domain::NoteRepository::Ptr ¬eRepository, QObject *parent) : PageModel(parent), m_tag(tag), m_tagQueries(tagQueries), m_tagRepository(tagRepository), m_noteRepository(noteRepository) { } Domain::Tag::Ptr TagPageModel::tag() const { return m_tag; } Domain::Artifact::Ptr TagPageModel::addItem(const QString &title, const QModelIndex &) { auto note = Domain::Note::Ptr::create(); note->setTitle(title); const auto job = m_noteRepository->createInTag(note, m_tag); installHandler(job, i18n("Cannot add note %1 in tag %2", title, m_tag->name())); return note; } void TagPageModel::removeItem(const QModelIndex &index) { - QVariant data = index.data(QueryTreeModel::ObjectRole); + QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto artifact = data.value(); auto note = artifact.objectCast(); Q_ASSERT(note); const auto job = m_tagRepository->dissociate(m_tag, note); installHandler(job, i18n("Cannot remove note %1 from tag %2", note->title(), m_tag->name())); } void TagPageModel::promoteItem(const QModelIndex &) { qFatal("Not supported"); } QAbstractItemModel *TagPageModel::createCentralListModel() { auto query = [this] (const Domain::Note::Ptr ¬e) -> Domain::QueryResultInterface::Ptr { if (!note) return m_tagQueries->findNotes(m_tag); else return Domain::QueryResult::Ptr(); }; auto flags = [](const Domain::Note::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled; }; auto data = [](const Domain::Note::Ptr ¬e, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return note->title(); } else { return QVariant(); } }; auto setData = [this] (const Domain::Note::Ptr ¬e, const QVariant &value, int role) { if (role != Qt::EditRole) { return false; } const auto currentTitle = note->title(); note->setTitle(value.toString()); const auto job = m_noteRepository->update(note); installHandler(job, i18n("Cannot modify note %1 in tag %2", currentTitle, m_tag->name())); return true; }; auto drop = [] (const QMimeData *, Qt::DropAction, const Domain::Note::Ptr &) { return false; }; auto drag = [] (const Domain::Note::List ¬es) -> QMimeData* { if (notes.isEmpty()) return Q_NULLPTR; auto draggedArtifacts = Domain::Artifact::List(); draggedArtifacts.reserve(notes.count()); std::copy(notes.constBegin(), notes.constEnd(), std::back_inserter(draggedArtifacts)); auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(draggedArtifacts)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, this); } diff --git a/src/presentation/taskinboxpagemodel.cpp b/src/presentation/taskinboxpagemodel.cpp index 3868175a..9099586d 100644 --- a/src/presentation/taskinboxpagemodel.cpp +++ b/src/presentation/taskinboxpagemodel.cpp @@ -1,176 +1,176 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "taskinboxpagemodel.h" #include #include #include "presentation/querytreemodel.h" using namespace Presentation; TaskInboxPageModel::TaskInboxPageModel(const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : PageModel(parent), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } Domain::Artifact::Ptr TaskInboxPageModel::addItem(const QString &title, const QModelIndex &parentIndex) { - const auto parentData = parentIndex.data(QueryTreeModel::ObjectRole); + const auto parentData = parentIndex.data(QueryTreeModelBase::ObjectRole); const auto parentArtifact = parentData.value(); const auto parentTask = parentArtifact.objectCast(); auto task = Domain::Task::Ptr::create(); task->setTitle(title); const auto job = parentTask ? m_taskRepository->createChild(task, parentTask) : m_taskRepository->create(task); installHandler(job, i18n("Cannot add task %1 in Inbox", title)); return task; } void TaskInboxPageModel::removeItem(const QModelIndex &index) { - QVariant data = index.data(QueryTreeModel::ObjectRole); + QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->remove(task); installHandler(job, i18n("Cannot remove task %1 from Inbox", task->title())); } void TaskInboxPageModel::promoteItem(const QModelIndex &index) { - QVariant data = index.data(QueryTreeModel::ObjectRole); + QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->promoteToProject(task); installHandler(job, i18n("Cannot promote task %1 to be a project", task->title())); } QAbstractItemModel *TaskInboxPageModel::createCentralListModel() { auto query = [this](const Domain::Task::Ptr &task) -> Domain::QueryResultInterface::Ptr { if (!task) return m_taskQueries->findInboxTopLevel(); else return m_taskQueries->findChildren(task); }; auto flags = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled; }; auto data = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return task->title(); } else { return task->isDone() ? Qt::Checked : Qt::Unchecked; } }; auto setData = [this](const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } const auto currentTitle = task->title(); if (role == Qt::EditRole) task->setTitle(value.toString()); else task->setDone(value.toInt() == Qt::Checked); const auto job = m_taskRepository->update(task); installHandler(job, i18n("Cannot modify task %1 in Inbox", currentTitle)); return true; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const Domain::Task::Ptr &task) { auto parentTask = task.objectCast(); if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedArtifacts = mimeData->property("objects").value(); if (droppedArtifacts.isEmpty()) return false; if (std::any_of(droppedArtifacts.begin(), droppedArtifacts.end(), [](const Domain::Artifact::Ptr &droppedArtifact) { return !droppedArtifact.objectCast(); })) { return false; } foreach(const auto &droppedArtifact, droppedArtifacts) { auto childTask = droppedArtifact.objectCast(); if (parentTask) { const auto job = m_taskRepository->associate(parentTask, childTask); installHandler(job, i18n("Cannot move task %1 as sub-task of %2", childTask->title(), parentTask->title())); } else { const auto job = m_taskRepository->dissociate(childTask); installHandler(job, i18n("Cannot deparent task %1 from its parent", childTask->title())); } } return true; }; auto drag = [](const Domain::Task::List &tasks) -> QMimeData* { if (tasks.isEmpty()) return Q_NULLPTR; auto draggedArtifacts = Domain::Artifact::List(); draggedArtifacts.reserve(tasks.size()); foreach (const Domain::Task::Ptr &task, tasks) { draggedArtifacts.append(task.objectCast()); } auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(draggedArtifacts)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, this); } diff --git a/src/presentation/workdaypagemodel.cpp b/src/presentation/workdaypagemodel.cpp index a9a51580..988d4c7d 100644 --- a/src/presentation/workdaypagemodel.cpp +++ b/src/presentation/workdaypagemodel.cpp @@ -1,205 +1,205 @@ /* This file is part of Zanshin Copyright 2015 Theo Vaucher This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "workdaypagemodel.h" #include #include #include "domain/noterepository.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/querytreemodel.h" #include "utils/datetime.h" using namespace Presentation; WorkdayPageModel::WorkdayPageModel(const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : PageModel(parent), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } Domain::Artifact::Ptr WorkdayPageModel::addItem(const QString &title, const QModelIndex &parentIndex) { - const auto parentData = parentIndex.data(QueryTreeModel::ObjectRole); + const auto parentData = parentIndex.data(QueryTreeModelBase::ObjectRole); const auto parentArtifact = parentData.value(); const auto parentTask = parentArtifact.objectCast(); auto task = Domain::Task::Ptr::create(); task->setTitle(title); if (!parentTask) task->setStartDate(Utils::DateTime::currentDate()); const auto job = parentTask ? m_taskRepository->createChild(task, parentTask) : m_taskRepository->create(task); installHandler(job, i18n("Cannot add task %1 in Workday", title)); return task; } void WorkdayPageModel::removeItem(const QModelIndex &index) { - QVariant data = index.data(QueryTreeModel::ObjectRole); + QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); if (task) { const auto job = m_taskRepository->remove(task); installHandler(job, i18n("Cannot remove task %1 from Workday", task->title())); } } void WorkdayPageModel::promoteItem(const QModelIndex &index) { - QVariant data = index.data(QueryTreeModel::ObjectRole); + QVariant data = index.data(QueryTreeModelBase::ObjectRole); auto artifact = data.value(); auto task = artifact.objectCast(); Q_ASSERT(task); const auto job = m_taskRepository->promoteToProject(task); installHandler(job, i18n("Cannot promote task %1 to be a project", task->title())); } QAbstractItemModel *WorkdayPageModel::createCentralListModel() { auto query = [this](const Domain::Artifact::Ptr &artifact) -> Domain::QueryResultInterface::Ptr { if (!artifact) return Domain::QueryResult::copy(m_taskQueries->findWorkdayTopLevel()); else if (auto task = artifact.dynamicCast()) return Domain::QueryResult::copy(m_taskQueries->findChildren(task)); else return Domain::QueryResult::Ptr(); }; auto flags = [](const Domain::Artifact::Ptr &artifact) { const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDragEnabled; return artifact.dynamicCast() ? (defaultFlags | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled) : defaultFlags; }; auto data = [this](const Domain::Artifact::Ptr &artifact, int role) -> QVariant { switch (role) { case Qt::DisplayRole: case Qt::EditRole: return artifact->title(); case Qt::CheckStateRole: if (auto task = artifact.dynamicCast()) { return task->isDone() ? Qt::Checked : Qt::Unchecked; } break; case ProjectRole: case Qt::ToolTipRole: if (auto task = artifact.dynamicCast()) { static Domain::QueryResult::Ptr lastProjectResult; auto projectResult = m_taskQueries->findProject(task); if (projectResult) { // keep a refcount to it, for next time we get here... lastProjectResult = projectResult; if (!projectResult->data().isEmpty()) { Domain::Project::Ptr project = projectResult->data().at(0); return i18n("Project: %1", project->name()); } } return i18n("Inbox"); } break; default: break; } return QVariant(); }; auto setData = [this](const Domain::Artifact::Ptr &artifact, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } if (auto task = artifact.dynamicCast()) { const auto currentTitle = task->title(); if (role == Qt::EditRole) task->setTitle(value.toString()); else task->setDone(value.toInt() == Qt::Checked); const auto job = m_taskRepository->update(task); installHandler(job, i18n("Cannot modify task %1 in Workday", currentTitle)); return true; } return false; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const Domain::Artifact::Ptr &artifact) { auto parentTask = artifact.objectCast(); if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedArtifacts = mimeData->property("objects").value(); if (droppedArtifacts.isEmpty()) return false; if (std::any_of(droppedArtifacts.begin(), droppedArtifacts.end(), [](const Domain::Artifact::Ptr &droppedArtifact) { return !droppedArtifact.objectCast(); })) { return false; } foreach(const auto &droppedArtifact, droppedArtifacts) { auto childTask = droppedArtifact.objectCast(); if (parentTask) { const auto job = m_taskRepository->associate(parentTask, childTask); installHandler(job, i18n("Cannot move task %1 as sub-task of %2", childTask->title(), parentTask->title())); } else { childTask->setStartDate(Utils::DateTime::currentDate()); auto job = m_taskRepository->dissociate(childTask); installHandler(job, i18n("Cannot deparent task %1 from its parent", childTask->title())); } } return true; }; auto drag = [](const Domain::Artifact::List &artifacts) -> QMimeData* { if (artifacts.isEmpty()) return Q_NULLPTR; auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(artifacts)); return data; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, this); } diff --git a/src/widgets/datasourcedelegate.cpp b/src/widgets/datasourcedelegate.cpp index eb20ccff..f98644bf 100644 --- a/src/widgets/datasourcedelegate.cpp +++ b/src/widgets/datasourcedelegate.cpp @@ -1,61 +1,61 @@ /* This file is part of Zanshin Copyright 2014 Christian Mollekopf Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "datasourcedelegate.h" #include #include #include "presentation/querytreemodel.h" using namespace Widgets; const int DELEGATE_HEIGHT = 16; DataSourceDelegate::DataSourceDelegate(QObject *parent) : QStyledItemDelegate(parent) { } void DataSourceDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const { Q_ASSERT(index.isValid()); - const auto isDefault = index.data(Presentation::QueryTreeModel::IsDefaultRole).toBool(); + const auto isDefault = index.data(Presentation::QueryTreeModelBase::IsDefaultRole).toBool(); QStyleOptionViewItem option = opt; initStyleOption(&option, index); option.font.setBold(isDefault); QStyledItemDelegate::paint(painter, option, index); } QSize DataSourceDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { QSize size = QStyledItemDelegate::sizeHint(option, index); // Make sure we got a constant height size.setHeight(DELEGATE_HEIGHT + 4); return size; } diff --git a/tests/units/presentation/querytreemodeltest.cpp b/tests/units/presentation/querytreemodeltest.cpp index 6fc713ff..28051013 100644 --- a/tests/units/presentation/querytreemodeltest.cpp +++ b/tests/units/presentation/querytreemodeltest.cpp @@ -1,985 +1,985 @@ /* This file is part of Zanshin Copyright 2014 Mario Bensi Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include "utils/mockobject.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/querytreemodel.h" #include "testlib/modeltest.h" using namespace mockitopp; Q_DECLARE_METATYPE(QModelIndex) Q_DECLARE_METATYPE(QList) class QueryTreeModelTest : public QObject { Q_OBJECT public: explicit QueryTreeModelTest(QObject *parent = Q_NULLPTR) : QObject(parent) { qRegisterMetaType(); } private: Domain::Task::List createTasks() const { Domain::Task::List result; const QStringList titles = {"first", "second", "third"}; const QList doneStates = {true, false, false}; Q_ASSERT(titles.size() == doneStates.size()); result.reserve(titles.size()); for (int i = 0; i < titles.size(); i++) { auto task = Domain::Task::Ptr::create(); task->setTitle(titles.at(i)); task->setDone(doneStates.at(i)); result << task; } return result; } Domain::Task::List createChildrenTasks() const { Domain::Task::List result; const QStringList titles = {"childFirst", "childSecond", "childThird"}; const QList doneStates = {true, false, false}; Q_ASSERT(titles.size() == doneStates.size()); result.reserve(titles.size()); for (int i = 0; i < titles.size(); i++) { auto task = Domain::Task::Ptr::create(); task->setTitle(titles.at(i)); task->setDone(doneStates.at(i)); result << task; } return result; } private slots: void shouldHaveRoleNames() { // GIVEN auto queryGenerator = [](const QColor &) { return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [](const QColor &, int) { return QVariant(); }; auto setDataFunction = [](const QColor &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction); // WHEN auto roles = model.roleNames(); // THEN QCOMPARE(roles.value(Qt::DisplayRole), QByteArray("display")); - QCOMPARE(roles.value(Presentation::QueryTreeModel::ObjectRole), QByteArray("object")); - QCOMPARE(roles.value(Presentation::QueryTreeModel::IconNameRole), QByteArray("icon")); - QCOMPARE(roles.value(Presentation::QueryTreeModel::IsDefaultRole), QByteArray("default")); + QCOMPARE(roles.value(Presentation::QueryTreeModelBase::ObjectRole), QByteArray("object")); + QCOMPARE(roles.value(Presentation::QueryTreeModelBase::IconNameRole), QByteArray("icon")); + QCOMPARE(roles.value(Presentation::QueryTreeModelBase::IsDefaultRole), QByteArray("default")); } void shouldListTasks() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); // WHEN auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model, this); // THEN QCOMPARE(model.rowCount(), 3); QCOMPARE(model.rowCount(model.index(0, 0)), 3); QCOMPARE(model.rowCount(model.index(1, 0)), 0); QCOMPARE(model.rowCount(model.index(2, 0)), 0); QCOMPARE(model.rowCount(model.index(0, 0, model.index(0, 0))), 0); QCOMPARE(model.rowCount(model.index(1, 0, model.index(0, 0))), 0); QCOMPARE(model.rowCount(model.index(2, 0, model.index(0, 0))), 0); QCOMPARE(model.rowCount(model.index(3, 0, model.index(0, 0))), 3); for (int i = 0; i < tasks.size(); i++) { auto task = tasks.at(i); auto index = model.index(i, 0); QCOMPARE(model.data(index), model.data(index, Qt::DisplayRole)); QCOMPARE(model.data(index).toString(), task->title()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Checked, task->isDone()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked, !task->isDone()); } for (int i = 0; i < childrenTasks.size(); i++) { auto task = childrenTasks.at(i); auto index = model.index(i, 0, model.index(0, 0)); QCOMPARE(model.data(index), model.data(index, Qt::DisplayRole)); QCOMPARE(model.data(index).toString(), task->title()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Checked, task->isDone()); QCOMPARE(model.data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked, !task->isDone()); } } void shouldDealWithNullQueriesProperly() { // GIVEN auto queryGenerator = [](const QString &) { return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const QString &) { return Qt::NoItemFlags; }; auto dataFunction = [](const QString &, int) { return QVariant(); }; auto setDataFunction = [](const QString &, const QVariant &, int) { return false; }; // WHEN Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model, this); // THEN QCOMPARE(model.rowCount(), 0); } void shouldReactToTaskAdd() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(tasks.at(1)); provider->append(tasks.at(2)); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model, this); QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); // WHEN queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); provider->insert(0, tasks.at(0)); // THEN QCOMPARE(aboutToBeInsertedSpy.size(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(0).toModelIndex(), QModelIndex()); QCOMPARE(aboutToBeInsertedSpy.first().at(1).toInt(), 0); QCOMPARE(aboutToBeInsertedSpy.first().at(2).toInt(), 0); QCOMPARE(insertedSpy.size(), 1); QCOMPARE(insertedSpy.first().at(0).toModelIndex(), QModelIndex()); QCOMPARE(insertedSpy.first().at(1).toInt(), 0); QCOMPARE(insertedSpy.first().at(2).toInt(), 0); } void shouldReactToChilrenTaskAdd() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); childrenProvider->append(childrenTasks.at(0)); childrenProvider->append(childrenTasks.at(1)); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model, this); QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); // WHEN queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); childrenProvider->insert(1, tasks.at(2)); // THEN QCOMPARE(aboutToBeInsertedSpy.size(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(0).toModelIndex(), model.index(0, 0)); QCOMPARE(aboutToBeInsertedSpy.first().at(1).toInt(), 1); QCOMPARE(aboutToBeInsertedSpy.first().at(2).toInt(), 1); QCOMPARE(insertedSpy.size(), 1); QCOMPARE(insertedSpy.first().at(0).toModelIndex(), model.index(0, 0)); QCOMPARE(insertedSpy.first().at(1).toInt(), 1); QCOMPARE(insertedSpy.first().at(2).toInt(), 1); } void shouldReactToTaskRemove() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model, this); QSignalSpy aboutToBeRemovedSpy(&model, &QAbstractItemModel::rowsAboutToBeRemoved); QSignalSpy removedSpy(&model, &QAbstractItemModel::rowsRemoved); QSignalSpy aboutToBeInsertedSpy(&model, &QAbstractItemModel::rowsAboutToBeInserted); QSignalSpy insertedSpy(&model, &QAbstractItemModel::rowsInserted); QModelIndex removeIndex = model.index(0, 0); // WHEN // Remove children childrenProvider->removeAt(0); childrenProvider->removeAt(0); childrenProvider->removeAt(0); // Move children to Top Level provider->append(childrenTasks.at(0)); provider->append(childrenTasks.at(1)); provider->append(childrenTasks.at(2)); // Remove firt element from topLevel provider->removeAt(0); // THEN QCOMPARE(aboutToBeRemovedSpy.size(), 4); QCOMPARE(removedSpy.size(), 4); for (int i = 0; i < aboutToBeRemovedSpy.size(); i++) { if (i != 3) QCOMPARE(aboutToBeRemovedSpy.at(i).at(0).toModelIndex(), removeIndex); else QCOMPARE(aboutToBeRemovedSpy.at(i).at(0).toModelIndex(), QModelIndex()); QCOMPARE(aboutToBeRemovedSpy.at(i).at(1).toInt(), 0); QCOMPARE(aboutToBeRemovedSpy.at(i).at(2).toInt(), 0); if (i != 3) QCOMPARE(removedSpy.at(i).at(0).toModelIndex(), removeIndex); else QCOMPARE(removedSpy.at(i).at(0).toModelIndex(), QModelIndex()); QCOMPARE(removedSpy.at(i).at(1).toInt(), 0); QCOMPARE(removedSpy.at(i).at(2).toInt(), 0); } QCOMPARE(aboutToBeInsertedSpy.size(), 3); QCOMPARE(insertedSpy.size(), 3); for (int i = 0; i < aboutToBeInsertedSpy.size(); i++) { QCOMPARE(aboutToBeInsertedSpy.at(i).at(0).toModelIndex(), QModelIndex()); QCOMPARE(aboutToBeInsertedSpy.at(i).at(1).toInt(), i + 3); QCOMPARE(aboutToBeInsertedSpy.at(i).at(2).toInt(), i + 3); QCOMPARE(insertedSpy.at(i).at(0).toModelIndex(), QModelIndex()); QCOMPARE(insertedSpy.at(i).at(1).toInt(), i + 3); QCOMPARE(insertedSpy.at(i).at(1).toInt(), i + 3); } } void shouldReactToTaskChange() { // GIVEN // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); // WHEN auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model, this); QSignalSpy dataChangedSpy(&model, &QAbstractItemModel::dataChanged); // WHEN tasks.at(2)->setDone(true); childrenTasks.at(2)->setDone(true); provider->replace(2, tasks.at(2)); childrenProvider->replace(2, tasks.at(2)); // THEN QCOMPARE(dataChangedSpy.size(), 2); QCOMPARE(dataChangedSpy.first().at(0).toModelIndex(), model.index(2, 0)); QCOMPARE(dataChangedSpy.first().at(1).toModelIndex(), model.index(2, 0)); QCOMPARE(dataChangedSpy.last().at(0).toModelIndex(), model.index(2, 0, model.index(0, 0))); QCOMPARE(dataChangedSpy.last().at(1).toModelIndex(), model.index(2, 0, model.index(0, 0))); } void shouldAllowEditsAndChecks() { // GIVEN auto tasks = createTasks(); auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); // WHEN auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model, this); // WHEN // Nothing particular // THEN for (int row = 0; row < tasks.size(); row++) { QVERIFY(model.flags(model.index(row, 0)) & Qt::ItemIsEditable); QVERIFY(model.flags(model.index(row, 0)) & Qt::ItemIsUserCheckable); } for (int row = 0; row < childrenTasks.size(); row++) { QVERIFY(model.flags(model.index(row, 0, model.index(0, 0))) & Qt::ItemIsEditable); QVERIFY(model.flags(model.index(row, 0, model.index(0, 0))) & Qt::ItemIsUserCheckable); } } void shouldSaveChanges() { // GIVEN auto tasks = createTasks(); const int taskPos = 1; const auto task = tasks[taskPos]; auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, tasks) provider->append(task); auto childrenTasks = createChildrenTasks(); auto childrenProvider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, childrenTasks) childrenProvider->append(task); auto childrenList = Domain::QueryResult::create(childrenProvider); auto emptyProvider = Domain::QueryResultProvider::Ptr::create(); auto emptyList = Domain::QueryResult::create(emptyProvider); Utils::MockObject queryMock; queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(0)).thenReturn(childrenList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(tasks.at(2)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(0)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(1)).thenReturn(emptyList); queryMock(&Domain::TaskQueries::findChildren).when(childrenTasks.at(2)).thenReturn(emptyList); Utils::MockObject repositoryMock; repositoryMock(&Domain::TaskRepository::update).when(task).thenReturn(Q_NULLPTR); auto queryGenerator = [&](const Domain::Task::Ptr &task) { if (!task) return Domain::QueryResult::create(provider); else return queryMock.getInstance()->findChildren(task); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsUserCheckable; }; auto dataFunction = [](const Domain::Task::Ptr &task, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::CheckStateRole) { return QVariant(); } if (role == Qt::DisplayRole) return task->title(); else return task->isDone() ? Qt::Checked : Qt::Unchecked; }; auto setDataFunction = [&](const Domain::Task::Ptr &task, const QVariant &value, int role) { if (role != Qt::EditRole && role != Qt::CheckStateRole) { return false; } if (role == Qt::EditRole) { task->setTitle(value.toString()); } else { task->setDone(value.toInt() == Qt::Checked); } repositoryMock.getInstance()->update(task); return true; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, Q_NULLPTR); new ModelTest(&model, this); QSignalSpy titleChangedSpy(task.data(), &Domain::Task::titleChanged); QSignalSpy doneChangedSpy(task.data(), &Domain::Task::doneChanged); // WHEN const auto index = model.index(taskPos, 0); model.setData(index, "alternate second"); model.setData(index, Qt::Checked, Qt::CheckStateRole); // THEN QVERIFY(repositoryMock(&Domain::TaskRepository::update).when(task).exactly(2)); QCOMPARE(titleChangedSpy.size(), 1); QCOMPARE(titleChangedSpy.first().first().toString(), QStringLiteral("alternate second")); QCOMPARE(doneChangedSpy.size(), 1); QCOMPARE(doneChangedSpy.first().first().toBool(), true); } void shouldProvideUnderlyingObject() { // GIVEN auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(Qt::red); provider->append(Qt::green); provider->append(Qt::blue); auto queryGenerator = [&](const QColor &color) { if (!color.isValid()) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [](const QColor &, int) { return QVariant(); }; auto setDataFunction = [](const QColor &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction); new ModelTest(&model, this); // WHEN const QModelIndex index = model.index(1, 0); - const QVariant data = index.data(Presentation::QueryTreeModel::ObjectRole); + const QVariant data = index.data(Presentation::QueryTreeModelBase::ObjectRole); // THEN QVERIFY(data.isValid()); QCOMPARE(data.value(), provider->data().at(1)); } void shouldProvideUnderlyingTaskAsArtifact() { // GIVEN auto provider = Domain::QueryResultProvider::Ptr::create(); foreach (const auto &task, createTasks()) provider->append(task); auto queryGenerator = [&](const Domain::Task::Ptr &artifact) { if (!artifact) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [](const Domain::Task::Ptr &) { return Qt::NoItemFlags; }; auto dataFunction = [](const Domain::Task::Ptr &, int) { return QVariant(); }; auto setDataFunction = [](const Domain::Task::Ptr &, const QVariant &, int) { return false; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction); new ModelTest(&model, this); // WHEN const QModelIndex index = model.index(1, 0); - const QVariant data = index.data(Presentation::QueryTreeModel::ObjectRole); + const QVariant data = index.data(Presentation::QueryTreeModelBase::ObjectRole); // THEN QVERIFY(data.isValid()); // Note it says Artifact and *not* Task here, it should up-cast automatically QVERIFY(!data.value().isNull()); QCOMPARE(data.value(), provider->data().at(1).staticCast()); } void shouldMoveOnlyDuringDragAndDrop() { // GIVEN auto queryGenerator = [&] (const QColor &) { return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QColor &, int) { return QVariant(); }; auto setDataFunction = [] (const QColor &, const QVariant &, int) { return false; }; auto dropFunction = [] (const QMimeData *, Qt::DropAction, const QColor &) { return false; }; auto dragFunction = [] (const QList &) { return Q_NULLPTR; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction); // THEN QCOMPARE(model.supportedDragActions(), Qt::MoveAction); QCOMPARE(model.supportedDropActions(), Qt::MoveAction); } void shouldCreateMimeData() { // GIVEN auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(Qt::red); provider->append(Qt::green); provider->append(Qt::blue); auto queryGenerator = [&] (const QColor &color) { if (!color.isValid()) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QColor &, int) { return QVariant(); }; auto setDataFunction = [] (const QColor &, const QVariant &, int) { return false; }; auto dropFunction = [] (const QMimeData *, Qt::DropAction, const QColor &) { return false; }; auto dragFunction = [] (const QList &colors) { auto mimeData = new QMimeData; mimeData->setColorData(QVariant::fromValue(colors)); return mimeData; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction); new ModelTest(&model, this); // WHEN auto data = std::unique_ptr(model.mimeData(QList() << model.index(1, 0) << model.index(2, 0))); // THEN QVERIFY(data.get()); QVERIFY(model.mimeTypes().contains(QStringLiteral("application/x-zanshin-object"))); QList colors; colors << Qt::green << Qt::blue; QCOMPARE(data->colorData().value>(), colors); } void shouldDropMimeData_data() { QTest::addColumn("row"); QTest::addColumn("column"); QTest::addColumn("parentRow"); QTest::addColumn("callExpected"); QTest::newRow("drop on object") << -1 << -1 << 2 << true; QTest::newRow("drop between object") << 1 << 0 << -1 << true; QTest::newRow("drop in empty area") << -1 << -1 << -1 << true; } void shouldDropMimeData() { // GIVEN QFETCH(int, row); QFETCH(int, column); QFETCH(int, parentRow); QFETCH(bool, callExpected); bool dropCalled = false; const QMimeData *droppedData = Q_NULLPTR; QColor colorSeen; auto provider = Domain::QueryResultProvider::Ptr::create(); provider->append(Qt::red); provider->append(Qt::green); provider->append(Qt::blue); auto queryGenerator = [&] (const QColor &color) { if (!color.isValid()) return Domain::QueryResult::create(provider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QColor &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QColor &, int) { return QVariant(); }; auto setDataFunction = [] (const QColor &, const QVariant &, int) { return false; }; auto dropFunction = [&] (const QMimeData *data, Qt::DropAction, const QColor &color) { dropCalled = true; droppedData = data; colorSeen = color; return false; }; auto dragFunction = [] (const QList &) -> QMimeData* { return Q_NULLPTR; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction); new ModelTest(&model, this); // WHEN auto data = std::make_unique(); const QModelIndex parent = parentRow >= 0 ? model.index(parentRow, 0) : QModelIndex(); model.dropMimeData(data.get(), Qt::MoveAction, row, column, parent); // THEN QCOMPARE(dropCalled, callExpected); if (callExpected) { QCOMPARE(droppedData, data.get()); QCOMPARE(colorSeen, parent.data(Presentation::QueryTreeModelBase::ObjectRole).value()); } } void shouldPreventCyclesByDragAndDrop() { // GIVEN bool dropCalled = false; auto topProvider = Domain::QueryResultProvider::Ptr::create(); topProvider->append(QStringLiteral("1")); topProvider->append(QStringLiteral("2")); topProvider->append(QStringLiteral("3")); auto firstLevelProvider = Domain::QueryResultProvider::Ptr::create(); firstLevelProvider->append(QStringLiteral("2.1")); firstLevelProvider->append(QStringLiteral("2.2")); firstLevelProvider->append(QStringLiteral("2.3")); auto secondLevelProvider = Domain::QueryResultProvider::Ptr::create(); secondLevelProvider->append(QStringLiteral("2.1.1")); secondLevelProvider->append(QStringLiteral("2.1.2")); secondLevelProvider->append(QStringLiteral("2.1.3")); auto queryGenerator = [&] (const QString &string) { if (string.isEmpty()) return Domain::QueryResult::create(topProvider); else if (string == QLatin1String("2")) return Domain::QueryResult::create(firstLevelProvider); else if (string == QLatin1String("2.1")) return Domain::QueryResult::create(secondLevelProvider); else return Domain::QueryResult::Ptr(); }; auto flagsFunction = [] (const QString &) { return Qt::NoItemFlags; }; auto dataFunction = [] (const QString &, int) { return QVariant(); }; auto setDataFunction = [] (const QString &, const QVariant &, int) { return false; }; auto dropFunction = [&] (const QMimeData *, Qt::DropAction, const QString &) { dropCalled = true; return false; }; auto dragFunction = [] (const QStringList &strings) -> QMimeData* { auto data = new QMimeData; data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(strings)); return data; }; Presentation::QueryTreeModel model(queryGenerator, flagsFunction, dataFunction, setDataFunction, dropFunction, dragFunction); new ModelTest(&model, this); const auto indexes = QModelIndexList() << model.index(0, 0) << model.index(1, 0) << model.index(1, 0, model.index(1, 0)); // WHEN auto data = std::unique_ptr(model.mimeData(indexes)); const auto parent = model.index(1, 0, model.index(0, 0, model.index(1, 0))); model.dropMimeData(data.get(), Qt::MoveAction, -1, -1, parent); // THEN QVERIFY(!dropCalled); } }; ZANSHIN_TEST_MAIN(QueryTreeModelTest) #include "querytreemodeltest.moc"