From 243f18fed2ee9b8f2778e83ee4fbe2b3275b0370 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Mon, 4 Dec 2017 00:02:58 +0100 Subject: Subcontrollers for list properties --- framework/src/domain/composercontroller.cpp | 426 +++++++++------------------- framework/src/domain/composercontroller.h | 57 +--- framework/src/domain/controller.cpp | 108 ++++++- framework/src/domain/controller.h | 68 ++++- framework/src/frameworkplugin.cpp | 2 + 5 files changed, 309 insertions(+), 352 deletions(-) (limited to 'framework') diff --git a/framework/src/domain/composercontroller.cpp b/framework/src/domain/composercontroller.cpp index f1e84019..d09dcd79 100644 --- a/framework/src/domain/composercontroller.cpp +++ b/framework/src/domain/composercontroller.cpp @@ -41,7 +41,11 @@ #include "mime/mailtemplates.h" #include "mime/mailcrypto.h" -Q_DECLARE_METATYPE(GpgME::Key); +std::vector &operator+=(std::vector &list, const std::vector &add) +{ + list.insert(std::end(list), std::begin(add), std::end(add)); + return list; +} class IdentitySelector : public Selector { public: @@ -93,14 +97,6 @@ public: } }; -static void traverse(const QStandardItemModel *model, const std::function &f) -{ - auto root = model->invisibleRootItem(); - for (int row = 0; row < root->rowCount(); row++) { - f(root->child(row, 0)); - } -} - template void asyncRun(QObject *object, std::function run, std::function continuation) { @@ -116,33 +112,22 @@ void asyncRun(QObject *object, std::function run, std::function co watcher->setFuture(future); } -class AddresseeModel : public QStandardItemModel +class AddresseeController : public Kube::ListPropertyController { public: - AddresseeModel() - :QStandardItemModel() - { - setItemRoleNames({{ComposerController::AddresseeNameRole, "addresseeName"}, - {ComposerController::KeyFoundRole, "keyFound"}, - {ComposerController::KeyMissingRole, "keyMissing"}, - {ComposerController::KeyRole, "key"}}); - } - void add(const QString &addressee) + QSet mMissingKeys; + AddresseeController() + : Kube::ListPropertyController{{"name", "keyFound", "key"}} { - auto item = new QStandardItem; - item->setText(addressee); - item->setData(QVariant::fromValue(addressee), ComposerController::AddresseeNameRole); - item->setData(false, ComposerController::KeyFoundRole); - item->setData(mCryptoEnabled, ComposerController::KeyMissingRole); - appendRow(QList() << item); - if (mCryptoEnabled) { - findKey(addressee); - } + QObject::connect(this, &Kube::ListPropertyController::added, this, [this] (const QByteArray &id, const QVariantMap &map) { + findKey(id, map.value("name").toString()); + }); } - void findKey(const QString &addressee) + void findKey(const QByteArray &id, const QString &addressee) { + mMissingKeys << id; KMime::Types::Mailbox mb; mb.fromUnicodeString(addressee); @@ -150,126 +135,88 @@ public: asyncRun>(this, [mb] { return MailCrypto::findKeys(QStringList{} << mb.address(), false, false, MailCrypto::OPENPGP); }, - [this, addressee](const std::vector &keys) { + [this, addressee, id](const std::vector &keys) { if (!keys.empty()) { if (keys.size() > 1 ) { SinkWarning() << "Found more than one key, encrypting to all of them."; } SinkLog() << "Found key: " << keys.front().primaryFingerprint(); - for (auto item : findItems(addressee)) { - item->setData(true, ComposerController::KeyFoundRole); - item->setData(QVariant::fromValue(keys), ComposerController::KeyRole); - } + setValue(id, "keyFound", true); + setValue(id, "key", QVariant::fromValue(keys)); + mMissingKeys.remove(id); + setProperty("foundAllKeys", mMissingKeys.isEmpty()); } else { SinkWarning() << "Failed to find key for recipient."; } }); } - void remove(const QString &addressee) + void set(const QStringList &list) { - auto root = invisibleRootItem(); - for (int row = 0; row < root->rowCount(); row++) { - if (root->child(row, 0)->text() == addressee) { - root->removeRow(row); - return; - } + for (const auto &email: list) { + add({{"name", email}}); } } +}; - bool foundAllKeys() - { - std::vector keys; - auto root = invisibleRootItem(); - for (int row = 0; row < root->rowCount(); row++) { - auto item = root->child(row, 0); - if (!item->data(ComposerController::KeyFoundRole).toBool()) { - return false; - } - } - return true; - } +class AttachmentController : public Kube::ListPropertyController +{ +public: - std::vector getKeys() + AttachmentController() + : Kube::ListPropertyController{{"name", "filename", "content", "mimetype", "description", "iconname", "url", "inline"}} { - std::vector keys; - traverse(this, [&] (QStandardItem *item) { - auto l = item->data(ComposerController::KeyRole).value>(); - keys.insert(std::end(keys), std::begin(l), std::end(l)); + QObject::connect(this, &Kube::ListPropertyController::added, this, [this] (const QByteArray &id, const QVariantMap &map) { + auto url = map.value("url").toUrl(); + setAttachmentProperties(id, url); }); - return keys; } - void setStringList(const QStringList &list) + void setAttachmentProperties(const QByteArray &id, const QUrl &url) { - clear(); - for (const auto &s : list) { - add(s); - } - } - - QStringList stringList() const - { - QStringList list; - traverse(this, [&] (QStandardItem *item) { - list << item->text(); - }); - return list; - } + QMimeDatabase db; + auto mimeType = db.mimeTypeForUrl(url); + if (mimeType.name() == QLatin1String("inode/directory")) { + qWarning() << "Can't deal with directories yet."; + } else { + if (!url.isLocalFile()) { + qWarning() << "Cannot attach remote file: " << url; + return; + } - void setCryptoEnabled(bool enabled) - { - mCryptoEnabled = enabled; - traverse(this, [&] (QStandardItem *item) { - item->setData(mCryptoEnabled, ComposerController::KeyMissingRole); - if (mCryptoEnabled) { - findKey(item->text()); + QFileInfo fileInfo(url.toLocalFile()); + if (!fileInfo.exists()) { + qWarning() << "The file doesn't exist: " << url; } - }); - } - bool mCryptoEnabled = false; + QFile file{fileInfo.filePath()}; + file.open(QIODevice::ReadOnly); + const auto data = file.readAll(); + QVariantMap map; + map.insert("filename", fileInfo.fileName()); + map.insert("mimetype", mimeType.name().toLatin1()); + map.insert("filename", fileInfo.fileName().toLatin1()); + map.insert("inline", false); + map.insert("iconname", mimeType.iconName()); + map.insert("url", url); + map.insert("content", data); + setValues(id, map); + } + } }; - ComposerController::ComposerController() : Kube::Controller(), + controller_to{new AddresseeController}, + controller_cc{new AddresseeController}, + controller_bcc{new AddresseeController}, + controller_attachments{new AttachmentController}, action_send{new Kube::ControllerAction{this, &ComposerController::send}}, action_saveAsDraft{new Kube::ControllerAction{this, &ComposerController::saveAsDraft}}, mRecipientCompleter{new RecipientCompleter}, - mIdentitySelector{new IdentitySelector{*this}}, - mToModel{new AddresseeModel}, - mCcModel{new AddresseeModel}, - mBccModel{new AddresseeModel}, - mAttachmentModel{new QStandardItemModel} + mIdentitySelector{new IdentitySelector{*this}} { - mAttachmentModel->setItemRoleNames({{NameRole, "name"}, - {FilenameRole, "filename"}, - {ContentRole, "content"}, - {MimeTypeRole, "mimetype"}, - {DescriptionRole, "description"}, - {IconNameRole, "iconName"}, - {UrlRole, "url"}, - {InlineRole, "inline"}}); - updateSaveAsDraftAction(); - // mSendAction->monitorProperty(); - // mSendAction->monitorProperty([] (const QString &) -> bool{ - // //validate - // }); - // registerAction(&ComposerController::send); - // actionDepends(); - // TODO do in constructor - - QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction); - QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSendAction); - QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSaveAsDraftAction); - QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSaveAsDraftAction); QObject::connect(this, &ComposerController::identityChanged, &ComposerController::findPersonalKey); - updateSendAction(); - - QObject::connect(this, &ComposerController::encryptChanged, [&] () { mToModel->setCryptoEnabled(getEncrypt()); }); - QObject::connect(this, &ComposerController::encryptChanged, [&] () { mCcModel->setCryptoEnabled(getEncrypt()); }); - QObject::connect(this, &ComposerController::encryptChanged, [&] () { mBccModel->setCryptoEnabled(getEncrypt()); }); } void ComposerController::findPersonalKey() @@ -280,18 +227,17 @@ void ComposerController::findPersonalKey() return MailCrypto::findKeys(QStringList{} << identity.address(), true); }, [this](const std::vector &keys) { - mPersonalKeys = keys; - if (mPersonalKeys.empty()) { + if (keys.empty()) { SinkWarning() << "Failed to find a personal key."; } - if (mPersonalKeys.size() > 1) { + if (keys.size() > 1) { SinkWarning() << "Found multiple keys, using first one:"; - SinkWarning() << " " << mPersonalKeys.front().primaryFingerprint(); + SinkWarning() << " " << keys.front().primaryFingerprint(); } else { - SinkLog() << "Found personal key: " << mPersonalKeys.front().primaryFingerprint(); + SinkLog() << "Found personal key: " << keys.front().primaryFingerprint(); } - updateSendAction(); - setEncryptionAvailable(!mPersonalKeys.empty()); + setPersonalKeys(QVariant::fromValue(keys)); + setFoundPersonalKeys(!keys.empty()); }); } @@ -300,111 +246,10 @@ void ComposerController::clear() Controller::clear(); //Reapply account and identity from selection mIdentitySelector->reapplyCurrentIndex(); - mToModel->clear(); - mCcModel->clear(); - mBccModel->clear(); -} - -QAbstractItemModel *ComposerController::toModel() const -{ - return mToModel.data(); -} - -void ComposerController::addTo(const QString &s) -{ - mToModel->add(s); - updateSendAction(); -} - -void ComposerController::removeTo(const QString &s) -{ - mToModel->remove(s); - updateSendAction(); -} - -QAbstractItemModel *ComposerController::ccModel() const -{ - return mCcModel.data(); -} - -void ComposerController::addCc(const QString &s) -{ - mCcModel->add(s); - updateSendAction(); -} - -void ComposerController::removeCc(const QString &s) -{ - mCcModel->remove(s); - updateSendAction(); -} - -QAbstractItemModel *ComposerController::bccModel() const -{ - return mBccModel.data(); -} - -void ComposerController::addBcc(const QString &s) -{ - mBccModel->add(s); - updateSendAction(); -} - -void ComposerController::removeBcc(const QString &s) -{ - mBccModel->remove(s); - updateSendAction(); -} - -QAbstractItemModel *ComposerController::attachmentModel() const -{ - return mAttachmentModel.data(); -} - -void ComposerController::addAttachment(const QUrl &url) -{ - QMimeDatabase db; - auto mimeType = db.mimeTypeForUrl(url); - if (mimeType.name() == QLatin1String("inode/directory")) { - qWarning() << "Can't deal with directories yet."; - } else { - if (!url.isLocalFile()) { - qWarning() << "Cannot attach remote file: " << url; - return; - } - - QFileInfo fileInfo(url.toLocalFile()); - if (!fileInfo.exists()) { - qWarning() << "The file doesn't exist: " << url; - } - - QFile file{fileInfo.filePath()}; - file.open(QIODevice::ReadOnly); - const auto data = file.readAll(); - auto item = new QStandardItem; - item->setData(fileInfo.fileName(), NameRole); - item->setData(mimeType.name().toLatin1(), MimeTypeRole); - item->setData(fileInfo.fileName(), FilenameRole); - item->setData(false, InlineRole); - item->setData(mimeType.iconName(), IconNameRole); - item->setData(url, UrlRole); - item->setData(data, ContentRole); - mAttachmentModel->appendRow(item); - } -} - -void ComposerController::removeAttachment(const QUrl &url) -{ - auto root = mAttachmentModel->invisibleRootItem(); - if (root->hasChildren()) { - for (int row = 0; row < root->rowCount(); row++) { - auto item = root->child(row, 0); - if (url == item->data(UrlRole).toUrl()) { - root->removeRow(row); - return; - } - } - } + //FIXME implement in Controller::clear instead + toController()->clear(); + ccController()->clear(); + bccController()->clear(); } Completer *ComposerController::recipientCompleter() const @@ -450,24 +295,23 @@ static QStringList getStringListFromAddresses(const KMime::Types::Mailbox::List void ComposerController::addAttachmentPart(KMime::Content *partToAttach) { - auto item = new QStandardItem; - + QVariantMap map; if (partToAttach->contentType()->mimeType() == "multipart/digest" || partToAttach->contentType()->mimeType() == "message/rfc822") { // if it is a digest or a full message, use the encodedContent() of the attachment, // which already has the proper headers - item->setData(partToAttach->encodedContent(), ContentRole); + map.insert("content", partToAttach->encodedContent()); } else { - item->setData(partToAttach->decodedContent(), ContentRole); + map.insert("content", partToAttach->decodedContent()); } - item->setData(partToAttach->contentType()->mimeType(), MimeTypeRole); + map.insert("mimetype", partToAttach->contentType()->mimeType()); QMimeDatabase db; auto mimeType = db.mimeTypeForName(partToAttach->contentType()->mimeType()); - item->setData(mimeType.iconName(), IconNameRole); + map.insert("iconname", mimeType.iconName()); if (partToAttach->contentDescription(false)) { - item->setData(partToAttach->contentDescription()->asUnicodeString(), DescriptionRole); + map.insert("description", partToAttach->contentDescription()->asUnicodeString()); } QString name; QString filename; @@ -478,7 +322,7 @@ void ComposerController::addAttachmentPart(KMime::Content *partToAttach) } if (partToAttach->contentDisposition(false)) { filename = partToAttach->contentDisposition()->filename(); - item->setData(partToAttach->contentDisposition()->disposition() == KMime::Headers::CDinline, InlineRole); + map.insert("inline", partToAttach->contentDisposition()->disposition() == KMime::Headers::CDinline); } if (name.isEmpty() && !filename.isEmpty()) { @@ -489,20 +333,19 @@ void ComposerController::addAttachmentPart(KMime::Content *partToAttach) } if (!filename.isEmpty()) { - item->setData(filename, FilenameRole); + map.insert("filename", filename); } if (!name.isEmpty()) { - item->setData(name, NameRole); + map.insert("name", name); } - - mAttachmentModel->appendRow(item); + attachmentsController()->add(map); } void ComposerController::setMessage(const KMime::Message::Ptr &msg) { - mToModel->setStringList(getStringListFromAddresses(msg->to(true)->mailboxes())); - mCcModel->setStringList(getStringListFromAddresses(msg->cc(true)->mailboxes())); - mBccModel->setStringList(getStringListFromAddresses(msg->bcc(true)->mailboxes())); + static_cast(toController())->set(getStringListFromAddresses(msg->to(true)->mailboxes())); + static_cast(ccController())->set(getStringListFromAddresses(msg->cc(true)->mailboxes())); + static_cast(bccController())->set(getStringListFromAddresses(msg->bcc(true)->mailboxes())); setSubject(msg->subject(true)->asUnicodeString()); bool isHtml = false; @@ -563,75 +406,66 @@ void ComposerController::recordForAutocompletion(const QByteArray &addrSpec, con } } +std::vector ComposerController::getRecipientKeys() +{ + std::vector keys; + { + const auto list = toController()->getList>("key"); + for (const auto &l: list) { + keys.insert(std::end(keys), std::begin(l), std::end(l)); + } + } + { + const auto list = ccController()->getList>("key"); + for (const auto &l: list) { + keys.insert(std::end(keys), std::begin(l), std::end(l)); + } + } + { + const auto list = bccController()->getList>("key"); + for (const auto &l: list) { + keys.insert(std::end(keys), std::begin(l), std::end(l)); + } + } + return keys; +} + KMime::Message::Ptr ComposerController::assembleMessage() { - applyAddresses(mToModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { - recordForAutocompletion(addrSpec, displayName); - }); - applyAddresses(mCcModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { - recordForAutocompletion(addrSpec, displayName); - }); - applyAddresses(mBccModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { + auto toAddresses = toController()->getList("name"); + auto ccAddresses = toController()->getList("name"); + auto bccAddresses = toController()->getList("name"); + applyAddresses(toAddresses + ccAddresses + bccAddresses, [&](const QByteArray &addrSpec, const QByteArray &displayName) { recordForAutocompletion(addrSpec, displayName); }); QList attachments; - auto root = mAttachmentModel->invisibleRootItem(); - for (int row = 0; row < root->rowCount(); row++) { - auto item = root->child(row, 0); + attachmentsController()->traverse([&](const QVariantMap &value) { attachments << Attachment{ - item->data(NameRole).toString(), - item->data(FilenameRole).toString(), - item->data(MimeTypeRole).toByteArray(), - item->data(InlineRole).toBool(), - item->data(ContentRole).toByteArray() + value["name"].toString(), + value["filename"].toString(), + value["mimetype"].toByteArray(), + value["inline"].toBool(), + value["content"].toByteArray() }; - } + }); std::vector signingKeys; if (getSign()) { - signingKeys = mPersonalKeys; + signingKeys = getPersonalKeys().value>(); } std::vector encryptionKeys; if (getEncrypt()) { - if (!mToModel->foundAllKeys() || !mCcModel->foundAllKeys() || !mBccModel->foundAllKeys()) { - qWarning() << "Can't encrypt with missing keys"; - return nullptr; - } //Encrypt to self so we can read the sent message - encryptionKeys.insert(std::end(encryptionKeys), std::begin(mPersonalKeys), std::end(mPersonalKeys)); - auto toKeys = mToModel->getKeys(); - encryptionKeys.insert(std::end(encryptionKeys), std::begin(toKeys), std::end(toKeys)); - auto ccKeys = mCcModel->getKeys(); - encryptionKeys.insert(std::end(encryptionKeys), std::begin(ccKeys), std::end(ccKeys)); - auto bccKeys = mBccModel->getKeys(); - encryptionKeys.insert(std::end(encryptionKeys), std::begin(bccKeys), std::end(bccKeys)); + encryptionKeys += getPersonalKeys().value>(); + encryptionKeys += getRecipientKeys(); } - return MailTemplates::createMessage(mExistingMessage, mToModel->stringList(), mCcModel->stringList(), mBccModel->stringList(), getIdentity(), getSubject(), getBody(), getHtmlBody(), attachments, signingKeys, encryptionKeys); -} - -void ComposerController::updateSendAction() -{ - auto enabled = !mToModel->stringList().isEmpty() && !getSubject().isEmpty() && !getAccountId().isEmpty(); - if (getEncrypt()) { - if (!mToModel->foundAllKeys() || !mCcModel->foundAllKeys() || !mBccModel->foundAllKeys()) { - SinkWarning() << "Don't have all keys"; - enabled = false; - } - } - if (getSign()) { - if (mPersonalKeys.empty()) { - enabled = false; - } - } - sendAction()->setEnabled(enabled); + return MailTemplates::createMessage(mExistingMessage, toAddresses, ccAddresses, bccAddresses, getIdentity(), getSubject(), getBody(), getHtmlBody(), attachments, signingKeys, encryptionKeys); } void ComposerController::send() { - // verify() - // && verify(); auto message = assembleMessage(); if (!message) { SinkWarning() << "Failed to assemble the message."; @@ -670,12 +504,6 @@ void ComposerController::send() run(job); } -void ComposerController::updateSaveAsDraftAction() -{ - bool enabled = !getAccountId().isEmpty(); - sendAction()->setEnabled(enabled); -} - void ComposerController::saveAsDraft() { SinkLog() << "Save as draft"; @@ -683,9 +511,8 @@ void ComposerController::saveAsDraft() auto existingMail = getExistingMail(); auto message = assembleMessage(); - //FIXME this is something for the validation if (!message) { - SinkWarning() << "Failed to get the mail: "; + SinkWarning() << "Failed to assemble the message."; return; } @@ -720,3 +547,4 @@ void ComposerController::saveAsDraft() }); run(job); } + diff --git a/framework/src/domain/composercontroller.h b/framework/src/domain/composercontroller.h index 995f4a2e..70a88900 100644 --- a/framework/src/domain/composercontroller.h +++ b/framework/src/domain/composercontroller.h @@ -41,6 +41,8 @@ inline bool operator !=(const KMime::Types::Mailbox &l, const KMime::Types::Mail Q_DECLARE_METATYPE(KMime::Types::Mailbox); +Q_DECLARE_METATYPE(GpgME::Key); + namespace KMime { class Message; } @@ -57,7 +59,6 @@ class ComposerController : public Kube::Controller KUBE_CONTROLLER_PROPERTY(bool, HtmlBody, htmlBody) KUBE_CONTROLLER_PROPERTY(bool, Encrypt, encrypt) KUBE_CONTROLLER_PROPERTY(bool, Sign, sign) - KUBE_CONTROLLER_PROPERTY(bool, EncryptionAvailable, encryptionAvailable) //Set by identitySelector KUBE_CONTROLLER_PROPERTY(KMime::Types::Mailbox, Identity, identity) @@ -67,26 +68,21 @@ class ComposerController : public Kube::Controller KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) + KUBE_CONTROLLER_PROPERTY(/*std::vector*/QVariant, PersonalKeys, personalKeys) + KUBE_CONTROLLER_PROPERTY(bool, FoundPersonalKeys, foundPersonalKeys) + + KUBE_CONTROLLER_LISTCONTROLLER(to) + KUBE_CONTROLLER_LISTCONTROLLER(cc) + KUBE_CONTROLLER_LISTCONTROLLER(bcc) + KUBE_CONTROLLER_LISTCONTROLLER(attachments) + Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT) Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT) - //Q_PROPERTY (QValidator* subjectValidator READ subjectValidator CONSTANT) - - Q_PROPERTY (QAbstractItemModel* toModel READ toModel CONSTANT) - Q_PROPERTY (QAbstractItemModel* ccModel READ ccModel CONSTANT) - Q_PROPERTY (QAbstractItemModel* bccModel READ bccModel CONSTANT) - Q_PROPERTY (QAbstractItemModel* attachmentModel READ attachmentModel CONSTANT) KUBE_CONTROLLER_ACTION(send) KUBE_CONTROLLER_ACTION(saveAsDraft) public: - enum AddresseeRoles { - KeyFoundRole = Qt::UserRole + 1, - KeyMissingRole, - KeyRole, - AddresseeNameRole - }; - explicit ComposerController(); Completer *recipientCompleter() const; @@ -94,50 +90,19 @@ public: Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); - QAbstractItemModel *toModel() const; - QAbstractItemModel *ccModel() const; - QAbstractItemModel *bccModel() const; - QAbstractItemModel *attachmentModel() const; - - Q_INVOKABLE void addTo(const QString &); - Q_INVOKABLE void removeTo(const QString &); - Q_INVOKABLE void addCc(const QString &); - Q_INVOKABLE void removeCc(const QString &); - Q_INVOKABLE void addBcc(const QString &); - Q_INVOKABLE void removeBcc(const QString &); - Q_INVOKABLE void addAttachment(const QUrl &); - Q_INVOKABLE void removeAttachment(const QUrl &); - public slots: virtual void clear() Q_DECL_OVERRIDE; private slots: - void updateSendAction(); - void updateSaveAsDraftAction(); void findPersonalKey(); private: - enum AttachmentRoles { - NameRole = Qt::UserRole + 1, - FilenameRole, - ContentRole, - MimeTypeRole, - DescriptionRole, - InlineRole, - IconNameRole, - UrlRole - }; - void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); void setMessage(const QSharedPointer &msg); void addAttachmentPart(KMime::Content *partToAttach); KMime::Message::Ptr assembleMessage(); + std::vector getRecipientKeys(); QScopedPointer mRecipientCompleter; QScopedPointer mIdentitySelector; - QSharedPointer mToModel; - QSharedPointer mCcModel; - QSharedPointer mBccModel; - QScopedPointer mAttachmentModel; - std::vector mPersonalKeys; }; diff --git a/framework/src/domain/controller.cpp b/framework/src/domain/controller.cpp index 52f4cd1f..82a761f6 100644 --- a/framework/src/domain/controller.cpp +++ b/framework/src/domain/controller.cpp @@ -20,16 +20,24 @@ #include #include +#include +#include +#include #include using namespace Kube; -ControllerAction::ControllerAction() +ControllerState::ControllerState() : QObject() { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); } +ControllerAction::ControllerAction() + : ControllerState() +{ +} + void ControllerAction::execute() { emit triggered(); @@ -57,3 +65,101 @@ void Controller::run(const KAsync::Job &job) //TODO attach a log context to the execution that we can gather from the job? jobToExec.exec(); } + + +static void traverse(const QStandardItemModel *model, const std::function &f) +{ + auto root = model->invisibleRootItem(); + for (int row = 0; row < root->rowCount(); row++) { + if (!f(root->child(row, 0))) { + return; + } + } +} + +ListPropertyController::ListPropertyController(const QStringList &roles) + : QObject(), + mModel(new QStandardItemModel) +{ + //Generate a set of roles for the names. We're not using any enum, so the actual role value doesn't matter. + int role = Qt::UserRole + 1; + mRoles.insert("id", role); + role++; + for (const auto &r : roles) { + mRoles.insert(r, role); + role++; + } + + QHash roleNames; + for (const auto r : mRoles.keys()) { + roleNames.insert(mRoles[r], r.toLatin1()); + } + mModel->setItemRoleNames(roleNames); +} + +void ListPropertyController::add(const QVariantMap &value) +{ + auto item = new QStandardItem; + auto id = QUuid::createUuid().toByteArray(); + item->setData(id, mRoles["id"]); + for (const auto &k : value.keys()) { + item->setData(value.value(k), mRoles[k]); + } + mModel->appendRow(QList() << item); + emit added(id, value); +} + +void ListPropertyController::remove(const QByteArray &id) +{ + auto root = mModel->invisibleRootItem(); + const auto idRole = mRoles["id"]; + for (int row = 0; row < root->rowCount(); row++) { + if (root->child(row, 0)->data(idRole).toByteArray() == id) { + root->removeRow(row); + return; + } + } +} + +void ListPropertyController::clear() +{ + mModel->clear(); +} + +QAbstractItemModel *ListPropertyController::model() +{ + QQmlEngine::setObjectOwnership(mModel.data(), QQmlEngine::CppOwnership); + return mModel.data(); +} + +void ListPropertyController::setValue(const QByteArray &id, const QString &key, const QVariant &value) +{ + setValues(id, {{key, value}}); +} + +void ListPropertyController::setValues(const QByteArray &id, const QVariantMap &values) +{ + const auto idRole = mRoles["id"]; + ::traverse(mModel.data(), [&] (QStandardItem *item) { + if (item->data(idRole).toByteArray() == id) { + for (const auto &key : values.keys()) { + item->setData(values.value(key), mRoles[key]); + } + return false; + } + return true; + }); +} + +void ListPropertyController::traverse(const std::function &f) +{ + ::traverse(mModel.data(), [&] (QStandardItem *item) { + QVariantMap map; + for (const auto &key : mRoles.keys()) { + map.insert(key, item->data(mRoles[key])); + } + f(map); + return true; + }); +} + diff --git a/framework/src/domain/controller.h b/framework/src/domain/controller.h index bf2a7ace..ba7ac8fe 100644 --- a/framework/src/domain/controller.h +++ b/framework/src/domain/controller.h @@ -42,12 +42,35 @@ public: Kube::ControllerAction* NAME##Action() const { Q_ASSERT(action_##NAME); return action_##NAME.data(); } \ private slots: void NAME(); \ +#define KUBE_CONTROLLER_LISTCONTROLLER(NAME) \ + Q_PROPERTY (Kube::ListPropertyController* NAME READ NAME##Controller CONSTANT) \ + private: QScopedPointer controller_##NAME; \ + public: Kube::ListPropertyController* NAME##Controller() const { Q_ASSERT(controller_##NAME); return controller_##NAME.data(); } \ + + +class QAbstractItemModel; +class QStandardItemModel; namespace Kube { -class ControllerAction : public QObject { +class ControllerState : public QObject { Q_OBJECT Q_PROPERTY(bool enabled MEMBER mEnabled NOTIFY enabledChanged) +public: + ControllerState(); + ~ControllerState() = default; + + void setEnabled(bool enabled) { setProperty("enabled", enabled); } + +signals: + void enabledChanged(); + +private: + bool mEnabled = false; +}; + +class ControllerAction : public ControllerState { + Q_OBJECT public: ControllerAction(); template @@ -60,14 +83,9 @@ public: ~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 { @@ -87,4 +105,42 @@ protected: void run(const KAsync::Job &job); }; +class ListPropertyController : public QObject +{ + Q_OBJECT + Q_PROPERTY(QAbstractItemModel* model READ model CONSTANT) + +public: + ListPropertyController(const QStringList &roles); + Q_INVOKABLE virtual void add(const QVariantMap &value); + Q_INVOKABLE virtual void remove(const QByteArray &id); + Q_INVOKABLE void clear(); + + QAbstractItemModel *model(); + + void setValue(const QByteArray &id, const QString &key, const QVariant &); + void setValues(const QByteArray &id, const QVariantMap &values); + void traverse(const std::function &f); + + template + QList getList(const QString &property) + { + QList list; + traverse([&] (const QVariantMap &map) { + list << map[property].value(); + }); + return list; + } + +Q_SIGNALS: + void added(QByteArray, QVariantMap); + +protected: + QScopedPointer mModel; + +private: + QHash mRoles; +}; + + } diff --git a/framework/src/frameworkplugin.cpp b/framework/src/frameworkplugin.cpp index 9f7b03c1..4aff5708 100644 --- a/framework/src/frameworkplugin.cpp +++ b/framework/src/frameworkplugin.cpp @@ -39,6 +39,7 @@ #include "webengineprofile.h" #include "startupcheck.h" #include "keyring.h" +#include "controller.h" #include @@ -70,6 +71,7 @@ void FrameworkPlugin::registerTypes (const char *uri) qmlRegisterType(uri, 1, 0, "FolderListModel"); qmlRegisterType(uri, 1, 0, "MailListModel"); qmlRegisterType(uri, 1, 0, "ComposerController"); + qmlRegisterType(uri, 1, 0, "ControllerAction"); qmlRegisterType(uri, 1, 0, "MessageParser"); qmlRegisterType(uri, 1, 0, "Retriever"); qmlRegisterType(uri, 1, 0, "OutboxModel"); -- cgit v1.2.3