From ba7128b30850594c7efb258d1794e377eede364a Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sun, 1 Jan 2017 13:37:19 +0100 Subject: Instead of using the action system we use controllers only. It's simpler, and the action system was just too complex to use in a typesafe way. --- framework/domain/CMakeLists.txt | 3 + framework/domain/actions/sinkactions.cpp | 74 ++------- framework/domain/completer.cpp | 26 +++ framework/domain/completer.h | 40 +++++ framework/domain/composercontroller.cpp | 268 +++++++++++++++++++------------ framework/domain/composercontroller.h | 118 +++++--------- framework/domain/controller.cpp | 55 +++++++ framework/domain/controller.h | 75 +++++++++ framework/domain/selector.cpp | 26 +++ framework/domain/selector.h | 50 ++++++ 10 files changed, 489 insertions(+), 246 deletions(-) create mode 100644 framework/domain/completer.cpp create mode 100644 framework/domain/completer.h create mode 100644 framework/domain/controller.cpp create mode 100644 framework/domain/controller.h create mode 100644 framework/domain/selector.cpp create mode 100644 framework/domain/selector.h (limited to 'framework/domain') diff --git a/framework/domain/CMakeLists.txt b/framework/domain/CMakeLists.txt index 481d5908..bb522416 100644 --- a/framework/domain/CMakeLists.txt +++ b/framework/domain/CMakeLists.txt @@ -20,6 +20,9 @@ set(mailplugin_SRCS identitiesmodel.cpp recepientautocompletionmodel.cpp settings/accountsettings.cpp + selector.cpp + completer.cpp + controller.cpp ) find_package(KF5 REQUIRED COMPONENTS Package) diff --git a/framework/domain/actions/sinkactions.cpp b/framework/domain/actions/sinkactions.cpp index fd791a91..a2d4c02c 100644 --- a/framework/domain/actions/sinkactions.cpp +++ b/framework/domain/actions/sinkactions.cpp @@ -16,7 +16,7 @@ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include + #include #include @@ -78,12 +78,18 @@ static ActionHandlerHelper deleteHandler("org.kde.kube.actions.delete", } ); +class FolderContext : public Kube::ContextWrapper { + using Kube::ContextWrapper::ContextWrapper; + KUBE_CONTEXTWRAPPER_PROPERTY(Sink::ApplicationDomain::Folder::Ptr, Folder, folder) +}; + static ActionHandlerHelper synchronizeHandler("org.kde.kube.actions.synchronize", [](Context *context) -> bool { return true; }, - [](Context *context) { - if (auto folder = context->property("folder").value()) { + [](Context *context_) { + auto context = FolderContext{*context_}; + if (auto folder = context.getFolder()) { SinkLog() << "Synchronizing folder " << folder->resourceInstanceIdentifier() << folder->identifier(); auto scope = SyncScope().resourceFilter(folder->resourceInstanceIdentifier()).filter(QVariant::fromValue(folder->identifier())); scope.setType(); @@ -110,65 +116,3 @@ static ActionHandlerHelper sendOutboxHandler("org.kde.kube.actions.sendOutbox", }} ); -static ActionHandlerHelper sendMailHandler("org.kde.kube.actions.sendmail", - [](Context *context) -> bool { - auto accountId = context->property("accountId").value(); - return !accountId.isEmpty(); - }, - ActionHandlerHelper::JobHandler{[](Context *context) -> KAsync::Job { - auto accountId = context->property("accountId").value(); - auto message = context->property("message").value(); - SinkLog() << "Sending a mail: " << *context; - - Query query; - query.containsFilter(ApplicationDomain::ResourceCapabilities::Mail::transport); - query.filter(accountId); - return Store::fetchAll(query) - .then>([=](const QList &resources) -> KAsync::Job { - if (!resources.isEmpty()) { - auto resourceId = resources[0]->identifier(); - SinkTrace() << "Sending message via resource: " << resourceId; - Mail mail(resourceId); - mail.setBlobProperty("mimeMessage", message->encodedContent()); - return Store::create(mail); - } - SinkWarning() << "Failed to find a mailtransport resource"; - return KAsync::error(0, "Failed to find a MailTransport resource."); - }); - }} -); - -static ActionHandlerHelper saveAsDraft("org.kde.kube.actions.save-as-draft", - [](Context *context) -> bool { - auto accountId = context->property("accountId").value(); - return !accountId.isEmpty(); - }, - ActionHandlerHelper::JobHandler([](Context *context) -> KAsync::Job { - SinkLog() << "Executing the save-as-draft action"; - SinkLog() << *context; - const auto accountId = context->property("accountId").value(); - const auto message = context->property("message").value(); - auto existingMail = context->property("existingMail").value(); - if (!message) { - SinkWarning() << "Failed to get the mail: " << context->property("mail"); - return KAsync::error(1, "Failed to get the mail: " + context->property("mail").toString()); - } - - if (existingMail.identifier().isEmpty()) { - Query query; - query.containsFilter(ApplicationDomain::ResourceCapabilities::Mail::drafts); - query.filter(accountId); - return Store::fetchOne(query) - .then([=](const SinkResource &resource) -> KAsync::Job { - Mail mail(resource.identifier()); - mail.setDraft(true); - mail.setMimeMessage(message->encodedContent()); - return Store::create(mail); - }); - } else { - SinkWarning() << "Modifying an existing mail" << existingMail.identifier(); - existingMail.setMimeMessage(message->encodedContent()); - return Store::modify(existingMail); - } - }) -); diff --git a/framework/domain/completer.cpp b/framework/domain/completer.cpp new file mode 100644 index 00000000..cacb4faa --- /dev/null +++ b/framework/domain/completer.cpp @@ -0,0 +1,26 @@ +/* + Copyright (c) 2016 Christian Mollekofp + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#include "completer.h" + +#include + +Completer::Completer(QAbstractItemModel *model) : mModel{model} +{ + QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); +} diff --git a/framework/domain/completer.h b/framework/domain/completer.h new file mode 100644 index 00000000..a672b809 --- /dev/null +++ b/framework/domain/completer.h @@ -0,0 +1,40 @@ +/* + Copyright (c) 2016 Christian Mollekofp + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#pragma once + +#include +#include +#include + +class Completer : public QObject { + Q_OBJECT + Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT) + Q_PROPERTY (QString searchString WRITE setSearchString READ searchString) + +public: + Completer(QAbstractItemModel *model); + QAbstractItemModel *model() { return mModel; } + virtual void setSearchString(const QString &s) { mSearchString = s; } + QString searchString() const { return mSearchString; } + +private: + QAbstractItemModel *mModel = nullptr; + QString mSearchString; +}; + diff --git a/framework/domain/composercontroller.cpp b/framework/domain/composercontroller.cpp index 57d386c6..4ce356a9 100644 --- a/framework/domain/composercontroller.cpp +++ b/framework/domain/composercontroller.cpp @@ -19,9 +19,6 @@ #include "composercontroller.h" -#include -#include -#include #include #include #include @@ -40,41 +37,9 @@ SINK_DEBUG_AREA("composercontroller"); -Q_DECLARE_METATYPE(KMime::Types::Mailbox) - -ComposerController::ComposerController(QObject *parent) : QObject(parent) -{ - QQmlEngine::setObjectOwnership(&mContext, QQmlEngine::CppOwnership); -} - - -Kube::Context* ComposerController::mailContext() -{ - return &mContext; -} - -class RecipientCompleter : public Completer { -public: - RecipientCompleter() : Completer(new RecipientAutocompletionModel) - { - } - - void setSearchString(const QString &s) { - static_cast(model())->setFilter(s); - Completer::setSearchString(s); - } -}; - -Completer *ComposerController::recipientCompleter() const -{ - static auto selector = new RecipientCompleter(); - QQmlEngine::setObjectOwnership(selector, QQmlEngine::CppOwnership); - return selector; -} - class IdentitySelector : public Selector { public: - IdentitySelector(ComposerContext &context) : Selector(new IdentitiesModel), mContext(context) + IdentitySelector(ComposerController &controller) : Selector(new IdentitiesModel), mController(controller) { } @@ -88,33 +53,72 @@ public: mb.setAddress(index.data(IdentitiesModel::Address).toString().toUtf8()); SinkLog() << "Setting current identity: " << mb.prettyAddress() << "Account: " << currentAccountId; - mContext.setProperty("identity", QVariant::fromValue(mb)); - mContext.setProperty("accountId", QVariant::fromValue(currentAccountId)); + mController.setIdentity(mb); + mController.setAccountId(currentAccountId); } else { SinkWarning() << "No valid identity for index: " << index; - mContext.setProperty("identity", QVariant{}); - mContext.setProperty("accountId", QVariant{}); + mController.clearIdentity(); + mController.clearAccountId(); } } private: - ComposerContext &mContext; + ComposerController &mController; }; +class RecipientCompleter : public Completer { +public: + RecipientCompleter() : Completer(new RecipientAutocompletionModel) + { + } + + void setSearchString(const QString &s) { + static_cast(model())->setFilter(s); + Completer::setSearchString(s); + } +}; + + +ComposerController::ComposerController() + : Kube::Controller(), + mSendAction{new Kube::ControllerAction}, + mSaveAsDraftAction{new Kube::ControllerAction}, + mRecipientCompleter{new RecipientCompleter}, + mIdentitySelector{new IdentitySelector{*this}} +{ + QObject::connect(mSaveAsDraftAction.data(), &Kube::ControllerAction::triggered, this, &ComposerController::saveAsDraft); + updateSaveAsDraftAction(); + // mSendAction->monitorProperty(); + // mSendAction->monitorProperty([] (const QString &) -> bool{ + // //validate + // }); + // registerAction(&ComposerController::send); + // actionDepends(); + // TODO do in constructor + QObject::connect(mSendAction.data(), &Kube::ControllerAction::triggered, this, &ComposerController::send); + + QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSendAction); + QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction); + updateSendAction(); +} + +Completer *ComposerController::recipientCompleter() const +{ + return mRecipientCompleter.data(); +} + Selector *ComposerController::identitySelector() const { - static auto selector = new IdentitySelector(*const_cast(&mContext)); - QQmlEngine::setObjectOwnership(selector, QQmlEngine::CppOwnership); - return selector; + return mIdentitySelector.data(); } void ComposerController::setMessage(const KMime::Message::Ptr &msg) { - mContext.setTo(msg->to(true)->asUnicodeString()); - mContext.setCc(msg->cc(true)->asUnicodeString()); - mContext.setSubject(msg->subject(true)->asUnicodeString()); - mContext.setBody(msg->body()); - mContext.setProperty("existingMessage", QVariant::fromValue(msg)); + setTo(msg->to(true)->asUnicodeString()); + setCc(msg->cc(true)->asUnicodeString()); + setSubject(msg->subject(true)->asUnicodeString()); + setBody(msg->body()); + setExistingMessage(msg); } void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) @@ -122,18 +126,20 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) Sink::Query query(*message.value()); query.request(); Sink::Store::fetchOne(query).syncThen([this, loadAsDraft](const Sink::ApplicationDomain::Mail &mail) { - mContext.setProperty("existingMail", QVariant::fromValue(mail)); + setExistingMail(mail); + + //TODO this should probably happen as reaction to the property being set. const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); if (!mailData.isEmpty()) { KMime::Message::Ptr mail(new KMime::Message); mail->setContent(mailData); mail->parse(); if (loadAsDraft) { + setMessage(mail); + } else { auto reply = MailTemplates::reply(mail); //We assume reply setMessage(reply); - } else { - setMessage(mail); } } else { qWarning() << "Retrieved empty message"; @@ -159,68 +165,122 @@ void applyAddresses(const QString &list, std::functionto(true)->addAddress(addrSpec, displayName); + recordForAutocompletion(addrSpec, displayName); + }); + applyAddresses(getCc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { + mail->cc(true)->addAddress(addrSpec, displayName); + recordForAutocompletion(addrSpec, displayName); + }); + applyAddresses(getBcc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { + mail->bcc(true)->addAddress(addrSpec, displayName); + recordForAutocompletion(addrSpec, displayName); + }); + + mail->from(true)->addAddress(getIdentity()); + + mail->subject(true)->fromUnicodeString(getSubject(), "utf-8"); + mail->setBody(getBody().toUtf8()); + mail->assemble(); + return mail; +} -Kube::ActionHandler *ComposerController::messageHandler() +void ComposerController::updateSendAction() { - return new Kube::ActionHandlerHelper( - [](Kube::Context *context) { - auto identity = context->property("identity"); - return identity.isValid(); - }, - [this](Kube::Context *context) { - auto mail = context->property("existingMessage").value(); - if (!mail) { - mail = KMime::Message::Ptr::create(); + auto enabled = !getTo().isEmpty() && !getSubject().isEmpty(); + mSendAction->setEnabled(enabled); +} + +void ComposerController::send() +{ + // verify() + // && verify(); + auto message = assembleMessage(); + + auto accountId = getAccountId(); + //SinkLog() << "Sending a mail: " << *this; + using namespace Sink; + using namespace Sink::ApplicationDomain; + + Query query; + query.containsFilter(ApplicationDomain::ResourceCapabilities::Mail::transport); + query.filter(accountId); + auto job = Store::fetchAll(query) + .then>([=](const QList &resources) -> KAsync::Job { + if (!resources.isEmpty()) { + auto resourceId = resources[0]->identifier(); + SinkTrace() << "Sending message via resource: " << resourceId; + Mail mail(resourceId); + mail.setBlobProperty("mimeMessage", message->encodedContent()); + return Store::create(mail); } - applyAddresses(context->property(ComposerContext::To::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { - mail->to(true)->addAddress(addrSpec, displayName); - recordForAutocompletion(addrSpec, displayName); - }); - applyAddresses(context->property(ComposerContext::Cc::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { - mail->cc(true)->addAddress(addrSpec, displayName); - recordForAutocompletion(addrSpec, displayName); - }); - applyAddresses(context->property(ComposerContext::Bcc::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { - mail->bcc(true)->addAddress(addrSpec, displayName); - recordForAutocompletion(addrSpec, displayName); - }); - - mail->from(true)->addAddress(context->property("identity").value()); - - mail->subject(true)->fromUnicodeString(context->property(ComposerContext::Subject::name).toString(), "utf-8"); - mail->setBody(context->property(ComposerContext::Body::name).toString().toUtf8()); - mail->assemble(); - - context->setProperty("message", QVariant::fromValue(mail)); - } - ); + return KAsync::error(0, "Failed to find a MailTransport resource."); + }); + run(job); + job = job.syncThen([&] { + emit done(); + }); } -Kube::Action* ComposerController::saveAsDraftAction() +void ComposerController::updateSaveAsDraftAction() { - auto action = new Kube::Action("org.kde.kube.actions.save-as-draft", mContext); - action->addPreHandler(messageHandler()); - action->addPostHandler(new Kube::ActionHandlerHelper( - [this](Kube::Context *context) { - emit done(); - })); - return action; + mSendAction->setEnabled(true); } -Kube::Action* ComposerController::sendAction() +void ComposerController::saveAsDraft() { - auto action = new Kube::Action("org.kde.kube.actions.sendmail", mContext); - // action->addPreHandler(identityHandler()); - action->addPreHandler(messageHandler()); - // action->addPreHandler(encryptionHandler()); - action->addPostHandler(new Kube::ActionHandlerHelper( - [this](Kube::Context *context) { - emit done(); - })); - return action; + const auto accountId = getAccountId(); + auto existingMail = getExistingMail(); + + auto message = assembleMessage(); + //FIXME this is something for the validation + if (!message) { + SinkWarning() << "Failed to get the mail: "; + return; + // return KAsync::error(1, "Failed to get the mail."); + } + + using namespace Sink; + using namespace Sink::ApplicationDomain; + + auto job = [&] { + if (existingMail.identifier().isEmpty()) { + Query query; + query.containsFilter(ApplicationDomain::ResourceCapabilities::Mail::drafts); + query.filter(accountId); + return Store::fetchOne(query) + .then([=](const SinkResource &resource) -> KAsync::Job { + Mail mail(resource.identifier()); + mail.setDraft(true); + mail.setMimeMessage(message->encodedContent()); + return Store::create(mail); + }); + } else { + SinkWarning() << "Modifying an existing mail" << existingMail.identifier(); + existingMail.setDraft(true); + existingMail.setMimeMessage(message->encodedContent()); + return Store::modify(existingMail); + } + }(); + job = job.syncThen([&] { + emit done(); + }); + run(job); } diff --git a/framework/domain/composercontroller.h b/framework/domain/composercontroller.h index 3e701ed1..c5046306 100644 --- a/framework/domain/composercontroller.h +++ b/framework/domain/composercontroller.h @@ -23,110 +23,74 @@ #include #include #include -#include -#include #include +#include -#include -#include +#include "completer.h" +#include "selector.h" +#include "controller.h" -namespace KMime { -class Message; +inline bool operator !=(const KMime::Types::Mailbox &l, const KMime::Types::Mailbox &r) +{ + return !(l.prettyAddress() == r.prettyAddress()); } -class ComposerContext : public Kube::Context { - Q_OBJECT - KUBE_CONTEXT_PROPERTY(QString, To, to) - KUBE_CONTEXT_PROPERTY(QString, Cc, cc) - KUBE_CONTEXT_PROPERTY(QString, Bcc, bcc) - KUBE_CONTEXT_PROPERTY(QString, From, from) - KUBE_CONTEXT_PROPERTY(QString, Subject, subject) - KUBE_CONTEXT_PROPERTY(QString, Body, body) -}; - -class Completer : public QObject { - Q_OBJECT - Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT) - Q_PROPERTY (QString searchString WRITE setSearchString READ searchString) +Q_DECLARE_METATYPE(KMime::Types::Mailbox); -public: - Completer(QAbstractItemModel *model) : mModel{model} - { - QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); - } - QAbstractItemModel *model() { return mModel; } - virtual void setSearchString(const QString &s) { mSearchString = s; } - QString searchString() const { return mSearchString; } - -private: - QAbstractItemModel *mModel = nullptr; - QString mSearchString; -}; +namespace KMime { +class Message; +} -/** - * Exposes a model and maintains a current index selection. - */ -class Selector : public QObject { +class ComposerController : public Kube::Controller +{ Q_OBJECT - Q_PROPERTY (int currentIndex READ currentIndex WRITE setCurrentIndex) - Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT) - -public: - Selector(QAbstractItemModel *model) : mModel{model} - { - QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); - } - - virtual QAbstractItemModel *model() { return mModel; } - - void setCurrentIndex(int i) { - mCurrentIndex = i; - Q_ASSERT(mModel); - setCurrent(mModel->index(mCurrentIndex, 0)); - } - int currentIndex() { return mCurrentIndex; } + //Interface properties + KUBE_CONTROLLER_PROPERTY(QString, To, to) + KUBE_CONTROLLER_PROPERTY(QString, Cc, cc) + KUBE_CONTROLLER_PROPERTY(QString, Bcc, bcc) + KUBE_CONTROLLER_PROPERTY(QString, Subject, subject) + KUBE_CONTROLLER_PROPERTY(QString, Body, body) - virtual void setCurrent(const QModelIndex &) = 0; -private: - QAbstractItemModel *mModel = nullptr; - int mCurrentIndex = 0; -}; + //Set by identitySelector + KUBE_CONTROLLER_PROPERTY(KMime::Types::Mailbox, Identity, identity) + KUBE_CONTROLLER_PROPERTY(QByteArray, AccountId, accountId) -class ComposerController : public QObject -{ - Q_OBJECT - Q_PROPERTY (Kube::Context* mailContext READ mailContext CONSTANT) + //Set by loadMessage + KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) + KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT) Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT) + //Q_PROPERTY (QValidator* subjectValidator READ subjectValidator CONSTANT) - Q_PROPERTY (Kube::Action* sendAction READ sendAction) - Q_PROPERTY (Kube::Action* saveAsDraftAction READ saveAsDraftAction) + Q_PROPERTY (Kube::ControllerAction* sendAction READ sendAction CONSTANT) + Q_PROPERTY (Kube::ControllerAction* saveAsDraftAction READ saveAsDraftAction CONSTANT) public: - explicit ComposerController(QObject *parent = Q_NULLPTR); - - Kube::Context* mailContext(); + explicit ComposerController(); Completer *recipientCompleter() const; Selector *identitySelector() const; Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); - Kube::Action* sendAction(); - Kube::Action* saveAsDraftAction(); - -public slots: - void clear(); + Kube::ControllerAction* sendAction(); + Kube::ControllerAction* saveAsDraftAction(); -signals: - void done(); +private slots: + void updateSendAction(); + void send(); + void updateSaveAsDraftAction(); + void saveAsDraft(); private: - Kube::ActionHandler *messageHandler(); void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); void setMessage(const QSharedPointer &msg); + KMime::Message::Ptr assembleMessage(); - ComposerContext mContext; + QScopedPointer mSendAction; + QScopedPointer mSaveAsDraftAction; + QScopedPointer mRecipientCompleter; + QScopedPointer mIdentitySelector; }; diff --git a/framework/domain/controller.cpp b/framework/domain/controller.cpp new file mode 100644 index 00000000..fb971136 --- /dev/null +++ b/framework/domain/controller.cpp @@ -0,0 +1,55 @@ +/* + Copyright (c) 2016 Christian Mollekopf + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#include "controller.h" + +#include +#include + +using namespace Kube; + +ControllerAction::ControllerAction() + : QObject() +{ + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); +} + +void ControllerAction::execute() +{ + emit triggered(); +} + +void Controller::clear() +{ + auto meta = metaObject(); + for (auto i = meta->propertyOffset(); i < meta->propertyCount(); i++) { + auto property = meta->property(i); + setProperty(property.name(), QVariant()); + } + for (const auto &p : dynamicPropertyNames()) { + setProperty(p, QVariant()); + } +} + +void Controller::run(const KAsync::Job &job) +{ + auto jobToExec = job; + //TODO handle error + //TODO attach a log context to the execution that we can gather from the job? + jobToExec.exec(); +} diff --git a/framework/domain/controller.h b/framework/domain/controller.h new file mode 100644 index 00000000..c152a588 --- /dev/null +++ b/framework/domain/controller.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2016 Christian Mollekopf + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#pragma once + +#include +#include +#include + +#define KUBE_CONTROLLER_PROPERTY(TYPE, NAME, LOWERCASENAME) \ + public: Q_PROPERTY(TYPE LOWERCASENAME MEMBER m##NAME NOTIFY LOWERCASENAME##Changed) \ + Q_SIGNALS: void LOWERCASENAME##Changed(); \ + private: TYPE m##NAME; \ + public: \ + struct NAME { \ + static constexpr const char *name = #LOWERCASENAME; \ + typedef TYPE Type; \ + }; \ + void set##NAME(const TYPE &value) { setProperty(NAME::name, QVariant::fromValue(value)); } \ + void clear##NAME() { setProperty(NAME::name, QVariant{}); } \ + TYPE get##NAME() const { return m##NAME; } \ + +namespace Kube { + +class ControllerAction : public QObject { + Q_OBJECT + Q_PROPERTY(bool enabled MEMBER mEnabled NOTIFY enabledChanged) +public: + ControllerAction(); + ~ControllerAction() = default; + + Q_INVOKABLE void execute(); + void setEnabled(bool enabled) { setProperty("enabled", enabled); } + +signals: + void enabledChanged(); + void triggered(); + +private: + bool mEnabled = true; +}; + +class Controller : public QObject { + Q_OBJECT +public: + Controller() = default; + virtual ~Controller() = default; + +public slots: + void clear(); + +signals: + void done(); + void error(); + +protected: + void run(const KAsync::Job &job); +}; + +} diff --git a/framework/domain/selector.cpp b/framework/domain/selector.cpp new file mode 100644 index 00000000..ddb23744 --- /dev/null +++ b/framework/domain/selector.cpp @@ -0,0 +1,26 @@ +/* + Copyright (c) 2016 Christian Mollekofp + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#include "selector.h" + +#include + +Selector::Selector(QAbstractItemModel *model) : mModel{model} +{ + QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); +} diff --git a/framework/domain/selector.h b/framework/domain/selector.h new file mode 100644 index 00000000..77c47ba7 --- /dev/null +++ b/framework/domain/selector.h @@ -0,0 +1,50 @@ +/* + Copyright (c) 2016 Christian Mollekofp + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#pragma once + +#include +#include + +/** + * Exposes a model and maintains a current index selection. + */ +class Selector : public QObject { + Q_OBJECT + Q_PROPERTY (int currentIndex READ currentIndex WRITE setCurrentIndex) + Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT) + +public: + Selector(QAbstractItemModel *model); + + virtual QAbstractItemModel *model() { return mModel; } + + void setCurrentIndex(int i) { + mCurrentIndex = i; + Q_ASSERT(mModel); + setCurrent(mModel->index(mCurrentIndex, 0)); + } + + int currentIndex() { return mCurrentIndex; } + + virtual void setCurrent(const QModelIndex &) = 0; +private: + QAbstractItemModel *mModel = nullptr; + int mCurrentIndex = 0; +}; + -- cgit v1.2.3