File indexing completed on 2025-01-05 04:59:43

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org>
0003  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004  */
0005 
0006 #include "availablepagesmodel.h"
0007 
0008 #include <QIcon>
0009 #include <QMimeData>
0010 
0011 #include <KLocalizedString>
0012 
0013 
0014 #include "presentation/alltaskspagemodel.h"
0015 #include "presentation/availablepagessortfilterproxymodel.h"
0016 #include "presentation/contextpagemodel.h"
0017 #include "presentation/inboxpagemodel.h"
0018 #include "presentation/projectpagemodel.h"
0019 #include "presentation/querytreemodel.h"
0020 #include "presentation/workdaypagemodel.h"
0021 
0022 #include "utils/jobhandler.h"
0023 #include "utils/datetime.h"
0024 
0025 using namespace Presentation;
0026 
0027 AvailablePagesModel::AvailablePagesModel(const Domain::DataSourceQueries::Ptr &dataSourceQueries,
0028                                          const Domain::ProjectQueries::Ptr &projectQueries,
0029                                          const Domain::ProjectRepository::Ptr &projectRepository,
0030                                          const Domain::ContextQueries::Ptr &contextQueries,
0031                                          const Domain::ContextRepository::Ptr &contextRepository,
0032                                          const Domain::TaskQueries::Ptr &taskQueries,
0033                                          const Domain::TaskRepository::Ptr &taskRepository,
0034                                          QObject *parent)
0035     : QObject(parent),
0036       m_pageListModel(nullptr),
0037       m_sortProxyModel(nullptr),
0038       m_dataSourceQueries(dataSourceQueries),
0039       m_projectQueries(projectQueries),
0040       m_projectRepository(projectRepository),
0041       m_contextQueries(contextQueries),
0042       m_contextRepository(contextRepository),
0043       m_taskQueries(taskQueries),
0044       m_taskRepository(taskRepository)
0045 {
0046 }
0047 
0048 QAbstractItemModel *AvailablePagesModel::pageListModel()
0049 {
0050     if (!m_pageListModel)
0051         m_pageListModel = createPageListModel();
0052 
0053     if (!m_sortProxyModel) {
0054         m_sortProxyModel = new AvailablePagesSortFilterProxyModel(this);
0055         m_sortProxyModel->setSourceModel(m_pageListModel);
0056     }
0057 
0058     return m_sortProxyModel;
0059 }
0060 
0061 QObject *AvailablePagesModel::createPageForIndex(const QModelIndex &index)
0062 {
0063     QObjectPtr object = index.data(QueryTreeModelBase::ObjectRole).value<QObjectPtr>();
0064 
0065     if (object == m_inboxObject) {
0066         auto inboxPageModel = new InboxPageModel(m_taskQueries,
0067                                                  m_taskRepository,
0068                                                  this);
0069         inboxPageModel->setErrorHandler(errorHandler());
0070         return inboxPageModel;
0071     } else if (object == m_workdayObject) {
0072         auto workdayPageModel = new WorkdayPageModel(m_taskQueries,
0073                                                      m_taskRepository,
0074                                                      this);
0075         workdayPageModel->setErrorHandler(errorHandler());
0076         return workdayPageModel;
0077     } else if (auto project = object.objectCast<Domain::Project>()) {
0078         auto projectPageModel = new ProjectPageModel(project,
0079                                                      m_projectQueries,
0080                                                      m_projectRepository,
0081                                                      m_taskQueries,
0082                                                      m_taskRepository,
0083                                                      this);
0084         projectPageModel->setErrorHandler(errorHandler());
0085         return projectPageModel;
0086     } else if (auto context = object.objectCast<Domain::Context>()) {
0087         auto contextPageModel = new ContextPageModel(context,
0088                                                      m_contextQueries,
0089                                                      m_contextRepository,
0090                                                      m_taskQueries,
0091                                                      m_taskRepository,
0092                                                      this);
0093         contextPageModel->setErrorHandler(errorHandler());
0094         return contextPageModel;
0095     } else if (object == m_allTasksObject) {
0096         auto allTasksPageModel = new AllTasksPageModel(m_taskQueries,
0097                                                        m_taskRepository,
0098                                                        this);
0099         allTasksPageModel->setErrorHandler(errorHandler());
0100         return allTasksPageModel;
0101     }
0102 
0103     return nullptr;
0104 }
0105 
0106 void AvailablePagesModel::addProject(const QString &name, const Domain::DataSource::Ptr &source)
0107 {
0108     auto project = Domain::Project::Ptr::create();
0109     project->setName(name);
0110     const auto job = m_projectRepository->create(project, source);
0111     installHandler(job, i18n("Cannot add project %1 in dataSource %2", name, source->name()));
0112 }
0113 
0114 void AvailablePagesModel::addContext(const QString &name, const Domain::DataSource::Ptr &source)
0115 {
0116     auto context = Domain::Context::Ptr::create();
0117     context->setName(name);
0118     const auto job = m_contextRepository->create(context, source);
0119     installHandler(job, i18n("Cannot add context %1", name));
0120 }
0121 
0122 void AvailablePagesModel::removeItem(const QModelIndex &index)
0123 {
0124     QObjectPtr object = index.data(QueryTreeModelBase::ObjectRole).value<QObjectPtr>();
0125     if (auto project = object.objectCast<Domain::Project>()) {
0126         const auto job = m_projectRepository->remove(project);
0127         installHandler(job, i18n("Cannot remove project %1", project->name()));
0128     } else if (auto context = object.objectCast<Domain::Context>()) {
0129         const auto job = m_contextRepository->remove(context);
0130         installHandler(job, i18n("Cannot remove context %1", context->name()));
0131     } else {
0132         Q_ASSERT(false);
0133     }
0134 }
0135 
0136 QAbstractItemModel *AvailablePagesModel::createPageListModel()
0137 {
0138     m_inboxObject = QObjectPtr::create();
0139     m_inboxObject->setProperty("name", i18n("Inbox"));
0140     m_workdayObject = QObjectPtr::create();
0141     m_workdayObject->setProperty("name", i18n("Workday"));
0142     m_projectsObject = QObjectPtr::create();
0143     m_projectsObject->setProperty("name", i18n("Projects"));
0144     m_contextsObject = QObjectPtr::create();
0145     m_contextsObject->setProperty("name", i18n("Contexts"));
0146     m_allTasksObject = QObjectPtr::create();
0147     m_allTasksObject->setProperty("name", i18n("All Tasks"));
0148 
0149     m_rootsProvider = Domain::QueryResultProvider<QObjectPtr>::Ptr::create();
0150     m_rootsProvider->append(m_inboxObject);
0151     m_rootsProvider->append(m_workdayObject);
0152     m_rootsProvider->append(m_projectsObject);
0153     m_rootsProvider->append(m_contextsObject);
0154     m_rootsProvider->append(m_allTasksObject);
0155 
0156     auto query = [this](const QObjectPtr &object) -> Domain::QueryResultInterface<QObjectPtr>::Ptr {
0157         if (!object)
0158             return Domain::QueryResult<QObjectPtr>::create(m_rootsProvider);
0159         else if (object == m_projectsObject)
0160             return Domain::QueryResult<Domain::DataSource::Ptr, QObjectPtr>::copy(m_dataSourceQueries->findAllSelected());
0161         else if (object == m_contextsObject)
0162             return Domain::QueryResult<Domain::Context::Ptr, QObjectPtr>::copy(m_contextQueries->findAll());
0163         else if (const auto source = object.objectCast<Domain::DataSource>())
0164             return Domain::QueryResult<Domain::Project::Ptr, QObjectPtr>::copy(m_dataSourceQueries->findProjects(source));
0165         else
0166             return Domain::QueryResult<QObjectPtr>::Ptr();
0167     };
0168 
0169     auto flags = [this](const QObjectPtr &object) {
0170         const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable
0171                                          | Qt::ItemIsEnabled
0172                                          | Qt::ItemIsEditable
0173                                          | Qt::ItemIsDropEnabled;
0174         const Qt::ItemFlags immutableNodeFlags = Qt::ItemIsSelectable
0175                                                | Qt::ItemIsEnabled
0176                                                | Qt::ItemIsDropEnabled;
0177         const Qt::ItemFlags nonDroppableNodeFlags = Qt::ItemIsSelectable
0178                                                   | Qt::ItemIsEnabled;
0179         const Qt::ItemFlags structureNodeFlags = Qt::NoItemFlags;
0180 
0181         return object.objectCast<Domain::Project>() ? defaultFlags
0182              : object.objectCast<Domain::Context>() ? defaultFlags
0183              : object == m_inboxObject ? immutableNodeFlags
0184              : object == m_workdayObject ? immutableNodeFlags
0185              : object == m_allTasksObject ? nonDroppableNodeFlags
0186              : structureNodeFlags;
0187     };
0188 
0189     auto data = [this](const QObjectPtr &object, int role, int) -> QVariant {
0190         if (role != Qt::DisplayRole
0191          && role != Qt::EditRole
0192          && role != Qt::DecorationRole
0193          && role != QueryTreeModelBase::IconNameRole) {
0194             return QVariant();
0195         }
0196 
0197         if (role == Qt::EditRole
0198          && (object == m_inboxObject
0199           || object == m_workdayObject
0200           || object == m_projectsObject
0201           || object == m_contextsObject
0202           || object == m_allTasksObject
0203           || object.objectCast<Domain::DataSource>())) {
0204             return QVariant();
0205         }
0206 
0207         if (role == Qt::DisplayRole || role == Qt::EditRole) {
0208             return object->property("name").toString();
0209         } else if (role == Qt::DecorationRole || role == QueryTreeModelBase::IconNameRole) {
0210             const QString iconName = object == m_inboxObject ? QStringLiteral("mail-folder-inbox")
0211                                    : (object == m_workdayObject)  ? QStringLiteral("go-jump-today")
0212                                    : (object == m_projectsObject) ? QStringLiteral("folder")
0213                                    : (object == m_contextsObject) ? QStringLiteral("folder")
0214                                    : (object == m_allTasksObject)  ? QStringLiteral("view-pim-tasks")
0215                                    : object.objectCast<Domain::DataSource>() ? QStringLiteral("folder")
0216                                    : object.objectCast<Domain::Context>() ? QStringLiteral("view-pim-notes")
0217                                    : QStringLiteral("view-pim-tasks");
0218 
0219             if (role == Qt::DecorationRole)
0220                 return QVariant::fromValue(QIcon::fromTheme(iconName));
0221             else
0222                 return iconName;
0223         } else {
0224             return QVariant();
0225         }
0226     };
0227 
0228     auto setData = [this](const QObjectPtr &object, const QVariant &value, int role) {
0229         if (role != Qt::EditRole) {
0230             return false;
0231         }
0232 
0233         if (object == m_inboxObject
0234          || object == m_workdayObject
0235          || object == m_projectsObject
0236          || object == m_contextsObject
0237          || object == m_allTasksObject
0238          || object.objectCast<Domain::DataSource>()) {
0239             return false;
0240         }
0241 
0242         if (auto project = object.objectCast<Domain::Project>()) {
0243             const auto currentName = project->name();
0244             project->setName(value.toString());
0245             const auto job = m_projectRepository->update(project);
0246             installHandler(job, i18n("Cannot modify project %1", currentName));
0247         } else if (auto context = object.objectCast<Domain::Context>()) {
0248             const auto currentName = context->name();
0249             context->setName(value.toString());
0250             const auto job = m_contextRepository->update(context);
0251             installHandler(job, i18n("Cannot modify context %1", currentName));
0252         } else {
0253             Q_ASSERT(false);
0254         }
0255 
0256         return true;
0257     };
0258 
0259     auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const QObjectPtr &object) {
0260         if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object")))
0261             return false;
0262 
0263         auto droppedTasks = mimeData->property("objects").value<Domain::Task::List>();
0264         if (droppedTasks.isEmpty())
0265             return false;
0266 
0267         if (auto project = object.objectCast<Domain::Project>()) {
0268             foreach (const auto &task, droppedTasks) {
0269                 const auto job = m_projectRepository->associate(project, task);
0270                 installHandler(job, i18n("Cannot add %1 to project %2", task->title(), project->name()));
0271             }
0272             return true;
0273         } else if (auto context = object.objectCast<Domain::Context>()) {
0274             foreach (const auto &task, droppedTasks) {
0275                 const auto job = m_contextRepository->associate(context, task);
0276                 installHandler(job, i18n("Cannot add %1 to context %2", task->title(), context->name()));
0277             }
0278             return true;
0279         } else if (object == m_inboxObject) {
0280             foreach (const auto &task, droppedTasks) {
0281                 const auto job = m_projectRepository->dissociate(task);
0282                 installHandler(job, i18n("Cannot move %1 to Inbox", task->title()));
0283 
0284                 Utils::JobHandler::install(job, [this, task] {
0285                     const auto dissociateJob = m_taskRepository->dissociateAll(task);
0286                     installHandler(dissociateJob, i18n("Cannot move task %1 to Inbox", task->title()));
0287                 });
0288             }
0289             return true;
0290         } else if (object == m_workdayObject) {
0291             foreach (const auto &task, droppedTasks) {
0292                 task->setStartDate(Utils::DateTime::currentDate());
0293                 const auto job = m_taskRepository->update(task);
0294 
0295                 installHandler(job, i18n("Cannot update task %1 to Workday", task->title()));
0296             }
0297             return true;
0298         }
0299 
0300         return false;
0301     };
0302 
0303     auto drag = [](const QObjectPtrList &) -> QMimeData* {
0304         return nullptr;
0305     };
0306 
0307     return new QueryTreeModel<QObjectPtr>(query, flags, data, setData, drop, drag, nullptr, this);
0308 }
0309 
0310 #include "moc_availablepagesmodel.cpp"