diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-01-01 13:37:19 +0100 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-01-02 00:31:41 +0100 |
commit | ba7128b30850594c7efb258d1794e377eede364a (patch) | |
tree | f805d511f6d12b0799c05b414054db8b8635bfc9 /framework/domain | |
parent | 431c257dd29e2e3d8878db200f0de4d452bffe92 (diff) | |
download | kube-ba7128b30850594c7efb258d1794e377eede364a.tar.gz kube-ba7128b30850594c7efb258d1794e377eede364a.zip |
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.
Diffstat (limited to 'framework/domain')
-rw-r--r-- | framework/domain/CMakeLists.txt | 3 | ||||
-rw-r--r-- | framework/domain/actions/sinkactions.cpp | 74 | ||||
-rw-r--r-- | framework/domain/completer.cpp | 26 | ||||
-rw-r--r-- | framework/domain/completer.h | 40 | ||||
-rw-r--r-- | framework/domain/composercontroller.cpp | 268 | ||||
-rw-r--r-- | framework/domain/composercontroller.h | 118 | ||||
-rw-r--r-- | framework/domain/controller.cpp | 55 | ||||
-rw-r--r-- | framework/domain/controller.h | 75 | ||||
-rw-r--r-- | framework/domain/selector.cpp | 26 | ||||
-rw-r--r-- | framework/domain/selector.h | 50 |
10 files changed, 489 insertions, 246 deletions
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 | |||
20 | identitiesmodel.cpp | 20 | identitiesmodel.cpp |
21 | recepientautocompletionmodel.cpp | 21 | recepientautocompletionmodel.cpp |
22 | settings/accountsettings.cpp | 22 | settings/accountsettings.cpp |
23 | selector.cpp | ||
24 | completer.cpp | ||
25 | controller.cpp | ||
23 | ) | 26 | ) |
24 | find_package(KF5 REQUIRED COMPONENTS Package) | 27 | find_package(KF5 REQUIRED COMPONENTS Package) |
25 | 28 | ||
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 @@ | |||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
17 | 02110-1301, USA. | 17 | 02110-1301, USA. |
18 | */ | 18 | */ |
19 | #include <actions/context.h> | 19 | |
20 | #include <actions/actionhandler.h> | 20 | #include <actions/actionhandler.h> |
21 | 21 | ||
22 | #include <KMime/Message> | 22 | #include <KMime/Message> |
@@ -78,12 +78,18 @@ static ActionHandlerHelper deleteHandler("org.kde.kube.actions.delete", | |||
78 | } | 78 | } |
79 | ); | 79 | ); |
80 | 80 | ||
81 | class FolderContext : public Kube::ContextWrapper { | ||
82 | using Kube::ContextWrapper::ContextWrapper; | ||
83 | KUBE_CONTEXTWRAPPER_PROPERTY(Sink::ApplicationDomain::Folder::Ptr, Folder, folder) | ||
84 | }; | ||
85 | |||
81 | static ActionHandlerHelper synchronizeHandler("org.kde.kube.actions.synchronize", | 86 | static ActionHandlerHelper synchronizeHandler("org.kde.kube.actions.synchronize", |
82 | [](Context *context) -> bool { | 87 | [](Context *context) -> bool { |
83 | return true; | 88 | return true; |
84 | }, | 89 | }, |
85 | [](Context *context) { | 90 | [](Context *context_) { |
86 | if (auto folder = context->property("folder").value<Folder::Ptr>()) { | 91 | auto context = FolderContext{*context_}; |
92 | if (auto folder = context.getFolder()) { | ||
87 | SinkLog() << "Synchronizing folder " << folder->resourceInstanceIdentifier() << folder->identifier(); | 93 | SinkLog() << "Synchronizing folder " << folder->resourceInstanceIdentifier() << folder->identifier(); |
88 | auto scope = SyncScope().resourceFilter(folder->resourceInstanceIdentifier()).filter<Mail::Folder>(QVariant::fromValue(folder->identifier())); | 94 | auto scope = SyncScope().resourceFilter(folder->resourceInstanceIdentifier()).filter<Mail::Folder>(QVariant::fromValue(folder->identifier())); |
89 | scope.setType<ApplicationDomain::Mail>(); | 95 | scope.setType<ApplicationDomain::Mail>(); |
@@ -110,65 +116,3 @@ static ActionHandlerHelper sendOutboxHandler("org.kde.kube.actions.sendOutbox", | |||
110 | }} | 116 | }} |
111 | ); | 117 | ); |
112 | 118 | ||
113 | static ActionHandlerHelper sendMailHandler("org.kde.kube.actions.sendmail", | ||
114 | [](Context *context) -> bool { | ||
115 | auto accountId = context->property("accountId").value<QByteArray>(); | ||
116 | return !accountId.isEmpty(); | ||
117 | }, | ||
118 | ActionHandlerHelper::JobHandler{[](Context *context) -> KAsync::Job<void> { | ||
119 | auto accountId = context->property("accountId").value<QByteArray>(); | ||
120 | auto message = context->property("message").value<KMime::Message::Ptr>(); | ||
121 | SinkLog() << "Sending a mail: " << *context; | ||
122 | |||
123 | Query query; | ||
124 | query.containsFilter<ApplicationDomain::SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::transport); | ||
125 | query.filter<SinkResource::Account>(accountId); | ||
126 | return Store::fetchAll<ApplicationDomain::SinkResource>(query) | ||
127 | .then<void, QList<ApplicationDomain::SinkResource::Ptr>>([=](const QList<ApplicationDomain::SinkResource::Ptr> &resources) -> KAsync::Job<void> { | ||
128 | if (!resources.isEmpty()) { | ||
129 | auto resourceId = resources[0]->identifier(); | ||
130 | SinkTrace() << "Sending message via resource: " << resourceId; | ||
131 | Mail mail(resourceId); | ||
132 | mail.setBlobProperty("mimeMessage", message->encodedContent()); | ||
133 | return Store::create(mail); | ||
134 | } | ||
135 | SinkWarning() << "Failed to find a mailtransport resource"; | ||
136 | return KAsync::error<void>(0, "Failed to find a MailTransport resource."); | ||
137 | }); | ||
138 | }} | ||
139 | ); | ||
140 | |||
141 | static ActionHandlerHelper saveAsDraft("org.kde.kube.actions.save-as-draft", | ||
142 | [](Context *context) -> bool { | ||
143 | auto accountId = context->property("accountId").value<QByteArray>(); | ||
144 | return !accountId.isEmpty(); | ||
145 | }, | ||
146 | ActionHandlerHelper::JobHandler([](Context *context) -> KAsync::Job<void> { | ||
147 | SinkLog() << "Executing the save-as-draft action"; | ||
148 | SinkLog() << *context; | ||
149 | const auto accountId = context->property("accountId").value<QByteArray>(); | ||
150 | const auto message = context->property("message").value<KMime::Message::Ptr>(); | ||
151 | auto existingMail = context->property("existingMail").value<Mail>(); | ||
152 | if (!message) { | ||
153 | SinkWarning() << "Failed to get the mail: " << context->property("mail"); | ||
154 | return KAsync::error<void>(1, "Failed to get the mail: " + context->property("mail").toString()); | ||
155 | } | ||
156 | |||
157 | if (existingMail.identifier().isEmpty()) { | ||
158 | Query query; | ||
159 | query.containsFilter<SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::drafts); | ||
160 | query.filter<SinkResource::Account>(accountId); | ||
161 | return Store::fetchOne<SinkResource>(query) | ||
162 | .then<void, SinkResource>([=](const SinkResource &resource) -> KAsync::Job<void> { | ||
163 | Mail mail(resource.identifier()); | ||
164 | mail.setDraft(true); | ||
165 | mail.setMimeMessage(message->encodedContent()); | ||
166 | return Store::create(mail); | ||
167 | }); | ||
168 | } else { | ||
169 | SinkWarning() << "Modifying an existing mail" << existingMail.identifier(); | ||
170 | existingMail.setMimeMessage(message->encodedContent()); | ||
171 | return Store::modify(existingMail); | ||
172 | } | ||
173 | }) | ||
174 | ); | ||
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 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekofp <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | #include "completer.h" | ||
20 | |||
21 | #include <QQmlEngine> | ||
22 | |||
23 | Completer::Completer(QAbstractItemModel *model) : mModel{model} | ||
24 | { | ||
25 | QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); | ||
26 | } | ||
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 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekofp <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | #pragma once | ||
20 | |||
21 | #include <QAbstractItemModel> | ||
22 | #include <QString> | ||
23 | #include <QQmlEngine> | ||
24 | |||
25 | class Completer : public QObject { | ||
26 | Q_OBJECT | ||
27 | Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT) | ||
28 | Q_PROPERTY (QString searchString WRITE setSearchString READ searchString) | ||
29 | |||
30 | public: | ||
31 | Completer(QAbstractItemModel *model); | ||
32 | QAbstractItemModel *model() { return mModel; } | ||
33 | virtual void setSearchString(const QString &s) { mSearchString = s; } | ||
34 | QString searchString() const { return mSearchString; } | ||
35 | |||
36 | private: | ||
37 | QAbstractItemModel *mModel = nullptr; | ||
38 | QString mSearchString; | ||
39 | }; | ||
40 | |||
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 @@ | |||
19 | 19 | ||
20 | 20 | ||
21 | #include "composercontroller.h" | 21 | #include "composercontroller.h" |
22 | #include <actions/context.h> | ||
23 | #include <actions/action.h> | ||
24 | #include <actions/actionhandler.h> | ||
25 | #include <settings/settings.h> | 22 | #include <settings/settings.h> |
26 | #include <KMime/Message> | 23 | #include <KMime/Message> |
27 | #include <KCodecs/KEmailAddress> | 24 | #include <KCodecs/KEmailAddress> |
@@ -40,41 +37,9 @@ | |||
40 | 37 | ||
41 | SINK_DEBUG_AREA("composercontroller"); | 38 | SINK_DEBUG_AREA("composercontroller"); |
42 | 39 | ||
43 | Q_DECLARE_METATYPE(KMime::Types::Mailbox) | ||
44 | |||
45 | ComposerController::ComposerController(QObject *parent) : QObject(parent) | ||
46 | { | ||
47 | QQmlEngine::setObjectOwnership(&mContext, QQmlEngine::CppOwnership); | ||
48 | } | ||
49 | |||
50 | |||
51 | Kube::Context* ComposerController::mailContext() | ||
52 | { | ||
53 | return &mContext; | ||
54 | } | ||
55 | |||
56 | class RecipientCompleter : public Completer { | ||
57 | public: | ||
58 | RecipientCompleter() : Completer(new RecipientAutocompletionModel) | ||
59 | { | ||
60 | } | ||
61 | |||
62 | void setSearchString(const QString &s) { | ||
63 | static_cast<RecipientAutocompletionModel*>(model())->setFilter(s); | ||
64 | Completer::setSearchString(s); | ||
65 | } | ||
66 | }; | ||
67 | |||
68 | Completer *ComposerController::recipientCompleter() const | ||
69 | { | ||
70 | static auto selector = new RecipientCompleter(); | ||
71 | QQmlEngine::setObjectOwnership(selector, QQmlEngine::CppOwnership); | ||
72 | return selector; | ||
73 | } | ||
74 | |||
75 | class IdentitySelector : public Selector { | 40 | class IdentitySelector : public Selector { |
76 | public: | 41 | public: |
77 | IdentitySelector(ComposerContext &context) : Selector(new IdentitiesModel), mContext(context) | 42 | IdentitySelector(ComposerController &controller) : Selector(new IdentitiesModel), mController(controller) |
78 | { | 43 | { |
79 | } | 44 | } |
80 | 45 | ||
@@ -88,33 +53,72 @@ public: | |||
88 | mb.setAddress(index.data(IdentitiesModel::Address).toString().toUtf8()); | 53 | mb.setAddress(index.data(IdentitiesModel::Address).toString().toUtf8()); |
89 | SinkLog() << "Setting current identity: " << mb.prettyAddress() << "Account: " << currentAccountId; | 54 | SinkLog() << "Setting current identity: " << mb.prettyAddress() << "Account: " << currentAccountId; |
90 | 55 | ||
91 | mContext.setProperty("identity", QVariant::fromValue(mb)); | 56 | mController.setIdentity(mb); |
92 | mContext.setProperty("accountId", QVariant::fromValue(currentAccountId)); | 57 | mController.setAccountId(currentAccountId); |
93 | } else { | 58 | } else { |
94 | SinkWarning() << "No valid identity for index: " << index; | 59 | SinkWarning() << "No valid identity for index: " << index; |
95 | mContext.setProperty("identity", QVariant{}); | 60 | mController.clearIdentity(); |
96 | mContext.setProperty("accountId", QVariant{}); | 61 | mController.clearAccountId(); |
97 | } | 62 | } |
98 | 63 | ||
99 | } | 64 | } |
100 | private: | 65 | private: |
101 | ComposerContext &mContext; | 66 | ComposerController &mController; |
102 | }; | 67 | }; |
103 | 68 | ||
69 | class RecipientCompleter : public Completer { | ||
70 | public: | ||
71 | RecipientCompleter() : Completer(new RecipientAutocompletionModel) | ||
72 | { | ||
73 | } | ||
74 | |||
75 | void setSearchString(const QString &s) { | ||
76 | static_cast<RecipientAutocompletionModel*>(model())->setFilter(s); | ||
77 | Completer::setSearchString(s); | ||
78 | } | ||
79 | }; | ||
80 | |||
81 | |||
82 | ComposerController::ComposerController() | ||
83 | : Kube::Controller(), | ||
84 | mSendAction{new Kube::ControllerAction}, | ||
85 | mSaveAsDraftAction{new Kube::ControllerAction}, | ||
86 | mRecipientCompleter{new RecipientCompleter}, | ||
87 | mIdentitySelector{new IdentitySelector{*this}} | ||
88 | { | ||
89 | QObject::connect(mSaveAsDraftAction.data(), &Kube::ControllerAction::triggered, this, &ComposerController::saveAsDraft); | ||
90 | updateSaveAsDraftAction(); | ||
91 | // mSendAction->monitorProperty<To>(); | ||
92 | // mSendAction->monitorProperty<Send>([] (const QString &) -> bool{ | ||
93 | // //validate | ||
94 | // }); | ||
95 | // registerAction<ControllerAction>(&ComposerController::send); | ||
96 | // actionDepends<ControllerAction, To, Subject>(); | ||
97 | // TODO do in constructor | ||
98 | QObject::connect(mSendAction.data(), &Kube::ControllerAction::triggered, this, &ComposerController::send); | ||
99 | |||
100 | QObject::connect(this, &ComposerController::toChanged, &ComposerController::updateSendAction); | ||
101 | QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction); | ||
102 | updateSendAction(); | ||
103 | } | ||
104 | |||
105 | Completer *ComposerController::recipientCompleter() const | ||
106 | { | ||
107 | return mRecipientCompleter.data(); | ||
108 | } | ||
109 | |||
104 | Selector *ComposerController::identitySelector() const | 110 | Selector *ComposerController::identitySelector() const |
105 | { | 111 | { |
106 | static auto selector = new IdentitySelector(*const_cast<ComposerContext*>(&mContext)); | 112 | return mIdentitySelector.data(); |
107 | QQmlEngine::setObjectOwnership(selector, QQmlEngine::CppOwnership); | ||
108 | return selector; | ||
109 | } | 113 | } |
110 | 114 | ||
111 | void ComposerController::setMessage(const KMime::Message::Ptr &msg) | 115 | void ComposerController::setMessage(const KMime::Message::Ptr &msg) |
112 | { | 116 | { |
113 | mContext.setTo(msg->to(true)->asUnicodeString()); | 117 | setTo(msg->to(true)->asUnicodeString()); |
114 | mContext.setCc(msg->cc(true)->asUnicodeString()); | 118 | setCc(msg->cc(true)->asUnicodeString()); |
115 | mContext.setSubject(msg->subject(true)->asUnicodeString()); | 119 | setSubject(msg->subject(true)->asUnicodeString()); |
116 | mContext.setBody(msg->body()); | 120 | setBody(msg->body()); |
117 | mContext.setProperty("existingMessage", QVariant::fromValue(msg)); | 121 | setExistingMessage(msg); |
118 | } | 122 | } |
119 | 123 | ||
120 | void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | 124 | void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) |
@@ -122,18 +126,20 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | |||
122 | Sink::Query query(*message.value<Sink::ApplicationDomain::Mail::Ptr>()); | 126 | Sink::Query query(*message.value<Sink::ApplicationDomain::Mail::Ptr>()); |
123 | query.request<Sink::ApplicationDomain::Mail::MimeMessage>(); | 127 | query.request<Sink::ApplicationDomain::Mail::MimeMessage>(); |
124 | Sink::Store::fetchOne<Sink::ApplicationDomain::Mail>(query).syncThen<void, Sink::ApplicationDomain::Mail>([this, loadAsDraft](const Sink::ApplicationDomain::Mail &mail) { | 128 | Sink::Store::fetchOne<Sink::ApplicationDomain::Mail>(query).syncThen<void, Sink::ApplicationDomain::Mail>([this, loadAsDraft](const Sink::ApplicationDomain::Mail &mail) { |
125 | mContext.setProperty("existingMail", QVariant::fromValue(mail)); | 129 | setExistingMail(mail); |
130 | |||
131 | //TODO this should probably happen as reaction to the property being set. | ||
126 | const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); | 132 | const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); |
127 | if (!mailData.isEmpty()) { | 133 | if (!mailData.isEmpty()) { |
128 | KMime::Message::Ptr mail(new KMime::Message); | 134 | KMime::Message::Ptr mail(new KMime::Message); |
129 | mail->setContent(mailData); | 135 | mail->setContent(mailData); |
130 | mail->parse(); | 136 | mail->parse(); |
131 | if (loadAsDraft) { | 137 | if (loadAsDraft) { |
138 | setMessage(mail); | ||
139 | } else { | ||
132 | auto reply = MailTemplates::reply(mail); | 140 | auto reply = MailTemplates::reply(mail); |
133 | //We assume reply | 141 | //We assume reply |
134 | setMessage(reply); | 142 | setMessage(reply); |
135 | } else { | ||
136 | setMessage(mail); | ||
137 | } | 143 | } |
138 | } else { | 144 | } else { |
139 | qWarning() << "Retrieved empty message"; | 145 | qWarning() << "Retrieved empty message"; |
@@ -159,68 +165,122 @@ void applyAddresses(const QString &list, std::function<void(const QByteArray &, | |||
159 | } | 165 | } |
160 | } | 166 | } |
161 | 167 | ||
162 | void ComposerController::clear() | 168 | Kube::ControllerAction* ComposerController::saveAsDraftAction() |
169 | { | ||
170 | return mSaveAsDraftAction.data(); | ||
171 | } | ||
172 | |||
173 | Kube::ControllerAction* ComposerController::sendAction() | ||
163 | { | 174 | { |
164 | mContext.clear(); | 175 | return mSendAction.data(); |
165 | } | 176 | } |
166 | 177 | ||
178 | KMime::Message::Ptr ComposerController::assembleMessage() | ||
179 | { | ||
180 | auto mail = mExistingMessage; | ||
181 | if (!mail) { | ||
182 | mail = KMime::Message::Ptr::create(); | ||
183 | } | ||
184 | applyAddresses(getTo(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
185 | mail->to(true)->addAddress(addrSpec, displayName); | ||
186 | recordForAutocompletion(addrSpec, displayName); | ||
187 | }); | ||
188 | applyAddresses(getCc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
189 | mail->cc(true)->addAddress(addrSpec, displayName); | ||
190 | recordForAutocompletion(addrSpec, displayName); | ||
191 | }); | ||
192 | applyAddresses(getBcc(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
193 | mail->bcc(true)->addAddress(addrSpec, displayName); | ||
194 | recordForAutocompletion(addrSpec, displayName); | ||
195 | }); | ||
196 | |||
197 | mail->from(true)->addAddress(getIdentity()); | ||
198 | |||
199 | mail->subject(true)->fromUnicodeString(getSubject(), "utf-8"); | ||
200 | mail->setBody(getBody().toUtf8()); | ||
201 | mail->assemble(); | ||
202 | return mail; | ||
203 | } | ||
167 | 204 | ||
168 | Kube::ActionHandler *ComposerController::messageHandler() | 205 | void ComposerController::updateSendAction() |
169 | { | 206 | { |
170 | return new Kube::ActionHandlerHelper( | 207 | auto enabled = !getTo().isEmpty() && !getSubject().isEmpty(); |
171 | [](Kube::Context *context) { | 208 | mSendAction->setEnabled(enabled); |
172 | auto identity = context->property("identity"); | 209 | } |
173 | return identity.isValid(); | 210 | |
174 | }, | 211 | void ComposerController::send() |
175 | [this](Kube::Context *context) { | 212 | { |
176 | auto mail = context->property("existingMessage").value<KMime::Message::Ptr>(); | 213 | // verify<To, Subject>() |
177 | if (!mail) { | 214 | // && verify<Subject>(); |
178 | mail = KMime::Message::Ptr::create(); | 215 | auto message = assembleMessage(); |
216 | |||
217 | auto accountId = getAccountId(); | ||
218 | //SinkLog() << "Sending a mail: " << *this; | ||
219 | using namespace Sink; | ||
220 | using namespace Sink::ApplicationDomain; | ||
221 | |||
222 | Query query; | ||
223 | query.containsFilter<ApplicationDomain::SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::transport); | ||
224 | query.filter<SinkResource::Account>(accountId); | ||
225 | auto job = Store::fetchAll<ApplicationDomain::SinkResource>(query) | ||
226 | .then<void, QList<ApplicationDomain::SinkResource::Ptr>>([=](const QList<ApplicationDomain::SinkResource::Ptr> &resources) -> KAsync::Job<void> { | ||
227 | if (!resources.isEmpty()) { | ||
228 | auto resourceId = resources[0]->identifier(); | ||
229 | SinkTrace() << "Sending message via resource: " << resourceId; | ||
230 | Mail mail(resourceId); | ||
231 | mail.setBlobProperty("mimeMessage", message->encodedContent()); | ||
232 | return Store::create(mail); | ||
179 | } | 233 | } |
180 | applyAddresses(context->property(ComposerContext::To::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | 234 | return KAsync::error<void>(0, "Failed to find a MailTransport resource."); |
181 | mail->to(true)->addAddress(addrSpec, displayName); | 235 | }); |
182 | recordForAutocompletion(addrSpec, displayName); | 236 | run(job); |
183 | }); | 237 | job = job.syncThen<void>([&] { |
184 | applyAddresses(context->property(ComposerContext::Cc::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | 238 | emit done(); |
185 | mail->cc(true)->addAddress(addrSpec, displayName); | 239 | }); |
186 | recordForAutocompletion(addrSpec, displayName); | ||
187 | }); | ||
188 | applyAddresses(context->property(ComposerContext::Bcc::name).toString(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
189 | mail->bcc(true)->addAddress(addrSpec, displayName); | ||
190 | recordForAutocompletion(addrSpec, displayName); | ||
191 | }); | ||
192 | |||
193 | mail->from(true)->addAddress(context->property("identity").value<KMime::Types::Mailbox>()); | ||
194 | |||
195 | mail->subject(true)->fromUnicodeString(context->property(ComposerContext::Subject::name).toString(), "utf-8"); | ||
196 | mail->setBody(context->property(ComposerContext::Body::name).toString().toUtf8()); | ||
197 | mail->assemble(); | ||
198 | |||
199 | context->setProperty("message", QVariant::fromValue(mail)); | ||
200 | } | ||
201 | ); | ||
202 | } | 240 | } |
203 | 241 | ||
204 | Kube::Action* ComposerController::saveAsDraftAction() | 242 | void ComposerController::updateSaveAsDraftAction() |
205 | { | 243 | { |
206 | auto action = new Kube::Action("org.kde.kube.actions.save-as-draft", mContext); | 244 | mSendAction->setEnabled(true); |
207 | action->addPreHandler(messageHandler()); | ||
208 | action->addPostHandler(new Kube::ActionHandlerHelper( | ||
209 | [this](Kube::Context *context) { | ||
210 | emit done(); | ||
211 | })); | ||
212 | return action; | ||
213 | } | 245 | } |
214 | 246 | ||
215 | Kube::Action* ComposerController::sendAction() | 247 | void ComposerController::saveAsDraft() |
216 | { | 248 | { |
217 | auto action = new Kube::Action("org.kde.kube.actions.sendmail", mContext); | 249 | const auto accountId = getAccountId(); |
218 | // action->addPreHandler(identityHandler()); | 250 | auto existingMail = getExistingMail(); |
219 | action->addPreHandler(messageHandler()); | 251 | |
220 | // action->addPreHandler(encryptionHandler()); | 252 | auto message = assembleMessage(); |
221 | action->addPostHandler(new Kube::ActionHandlerHelper( | 253 | //FIXME this is something for the validation |
222 | [this](Kube::Context *context) { | 254 | if (!message) { |
223 | emit done(); | 255 | SinkWarning() << "Failed to get the mail: "; |
224 | })); | 256 | return; |
225 | return action; | 257 | // return KAsync::error<void>(1, "Failed to get the mail."); |
258 | } | ||
259 | |||
260 | using namespace Sink; | ||
261 | using namespace Sink::ApplicationDomain; | ||
262 | |||
263 | auto job = [&] { | ||
264 | if (existingMail.identifier().isEmpty()) { | ||
265 | Query query; | ||
266 | query.containsFilter<SinkResource::Capabilities>(ApplicationDomain::ResourceCapabilities::Mail::drafts); | ||
267 | query.filter<SinkResource::Account>(accountId); | ||
268 | return Store::fetchOne<SinkResource>(query) | ||
269 | .then<void, SinkResource>([=](const SinkResource &resource) -> KAsync::Job<void> { | ||
270 | Mail mail(resource.identifier()); | ||
271 | mail.setDraft(true); | ||
272 | mail.setMimeMessage(message->encodedContent()); | ||
273 | return Store::create(mail); | ||
274 | }); | ||
275 | } else { | ||
276 | SinkWarning() << "Modifying an existing mail" << existingMail.identifier(); | ||
277 | existingMail.setDraft(true); | ||
278 | existingMail.setMimeMessage(message->encodedContent()); | ||
279 | return Store::modify(existingMail); | ||
280 | } | ||
281 | }(); | ||
282 | job = job.syncThen<void>([&] { | ||
283 | emit done(); | ||
284 | }); | ||
285 | run(job); | ||
226 | } | 286 | } |
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 @@ | |||
23 | #include <QString> | 23 | #include <QString> |
24 | #include <QStringList> | 24 | #include <QStringList> |
25 | #include <QVariant> | 25 | #include <QVariant> |
26 | #include <QQmlEngine> | ||
27 | #include <QAbstractItemModel> | ||
28 | #include <sink/applicationdomaintype.h> | 26 | #include <sink/applicationdomaintype.h> |
27 | #include <KMime/Message> | ||
29 | 28 | ||
30 | #include <actions/context.h> | 29 | #include "completer.h" |
31 | #include <actions/action.h> | 30 | #include "selector.h" |
31 | #include "controller.h" | ||
32 | 32 | ||
33 | namespace KMime { | 33 | inline bool operator !=(const KMime::Types::Mailbox &l, const KMime::Types::Mailbox &r) |
34 | class Message; | 34 | { |
35 | return !(l.prettyAddress() == r.prettyAddress()); | ||
35 | } | 36 | } |
36 | 37 | ||
37 | class ComposerContext : public Kube::Context { | 38 | Q_DECLARE_METATYPE(KMime::Types::Mailbox); |
38 | Q_OBJECT | ||
39 | KUBE_CONTEXT_PROPERTY(QString, To, to) | ||
40 | KUBE_CONTEXT_PROPERTY(QString, Cc, cc) | ||
41 | KUBE_CONTEXT_PROPERTY(QString, Bcc, bcc) | ||
42 | KUBE_CONTEXT_PROPERTY(QString, From, from) | ||
43 | KUBE_CONTEXT_PROPERTY(QString, Subject, subject) | ||
44 | KUBE_CONTEXT_PROPERTY(QString, Body, body) | ||
45 | }; | ||
46 | |||
47 | class Completer : public QObject { | ||
48 | Q_OBJECT | ||
49 | Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT) | ||
50 | Q_PROPERTY (QString searchString WRITE setSearchString READ searchString) | ||
51 | 39 | ||
52 | public: | 40 | namespace KMime { |
53 | Completer(QAbstractItemModel *model) : mModel{model} | 41 | class Message; |
54 | { | 42 | } |
55 | QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); | ||
56 | } | ||
57 | QAbstractItemModel *model() { return mModel; } | ||
58 | virtual void setSearchString(const QString &s) { mSearchString = s; } | ||
59 | QString searchString() const { return mSearchString; } | ||
60 | |||
61 | private: | ||
62 | QAbstractItemModel *mModel = nullptr; | ||
63 | QString mSearchString; | ||
64 | }; | ||
65 | 43 | ||
66 | /** | 44 | class ComposerController : public Kube::Controller |
67 | * Exposes a model and maintains a current index selection. | 45 | { |
68 | */ | ||
69 | class Selector : public QObject { | ||
70 | Q_OBJECT | 46 | Q_OBJECT |
71 | Q_PROPERTY (int currentIndex READ currentIndex WRITE setCurrentIndex) | ||
72 | Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT) | ||
73 | |||
74 | public: | ||
75 | Selector(QAbstractItemModel *model) : mModel{model} | ||
76 | { | ||
77 | QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); | ||
78 | } | ||
79 | |||
80 | virtual QAbstractItemModel *model() { return mModel; } | ||
81 | |||
82 | void setCurrentIndex(int i) { | ||
83 | mCurrentIndex = i; | ||
84 | Q_ASSERT(mModel); | ||
85 | setCurrent(mModel->index(mCurrentIndex, 0)); | ||
86 | } | ||
87 | 47 | ||
88 | int currentIndex() { return mCurrentIndex; } | 48 | //Interface properties |
49 | KUBE_CONTROLLER_PROPERTY(QString, To, to) | ||
50 | KUBE_CONTROLLER_PROPERTY(QString, Cc, cc) | ||
51 | KUBE_CONTROLLER_PROPERTY(QString, Bcc, bcc) | ||
52 | KUBE_CONTROLLER_PROPERTY(QString, Subject, subject) | ||
53 | KUBE_CONTROLLER_PROPERTY(QString, Body, body) | ||
89 | 54 | ||
90 | virtual void setCurrent(const QModelIndex &) = 0; | 55 | //Set by identitySelector |
91 | private: | 56 | KUBE_CONTROLLER_PROPERTY(KMime::Types::Mailbox, Identity, identity) |
92 | QAbstractItemModel *mModel = nullptr; | 57 | KUBE_CONTROLLER_PROPERTY(QByteArray, AccountId, accountId) |
93 | int mCurrentIndex = 0; | ||
94 | }; | ||
95 | 58 | ||
96 | class ComposerController : public QObject | 59 | //Set by loadMessage |
97 | { | 60 | KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) |
98 | Q_OBJECT | 61 | KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) |
99 | Q_PROPERTY (Kube::Context* mailContext READ mailContext CONSTANT) | ||
100 | 62 | ||
101 | Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT) | 63 | Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT) |
102 | Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT) | 64 | Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT) |
65 | //Q_PROPERTY (QValidator* subjectValidator READ subjectValidator CONSTANT) | ||
103 | 66 | ||
104 | Q_PROPERTY (Kube::Action* sendAction READ sendAction) | 67 | Q_PROPERTY (Kube::ControllerAction* sendAction READ sendAction CONSTANT) |
105 | Q_PROPERTY (Kube::Action* saveAsDraftAction READ saveAsDraftAction) | 68 | Q_PROPERTY (Kube::ControllerAction* saveAsDraftAction READ saveAsDraftAction CONSTANT) |
106 | 69 | ||
107 | public: | 70 | public: |
108 | explicit ComposerController(QObject *parent = Q_NULLPTR); | 71 | explicit ComposerController(); |
109 | |||
110 | Kube::Context* mailContext(); | ||
111 | 72 | ||
112 | Completer *recipientCompleter() const; | 73 | Completer *recipientCompleter() const; |
113 | Selector *identitySelector() const; | 74 | Selector *identitySelector() const; |
114 | 75 | ||
115 | Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); | 76 | Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); |
116 | 77 | ||
117 | Kube::Action* sendAction(); | 78 | Kube::ControllerAction* sendAction(); |
118 | Kube::Action* saveAsDraftAction(); | 79 | Kube::ControllerAction* saveAsDraftAction(); |
119 | |||
120 | public slots: | ||
121 | void clear(); | ||
122 | 80 | ||
123 | signals: | 81 | private slots: |
124 | void done(); | 82 | void updateSendAction(); |
83 | void send(); | ||
84 | void updateSaveAsDraftAction(); | ||
85 | void saveAsDraft(); | ||
125 | 86 | ||
126 | private: | 87 | private: |
127 | Kube::ActionHandler *messageHandler(); | ||
128 | void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); | 88 | void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); |
129 | void setMessage(const QSharedPointer<KMime::Message> &msg); | 89 | void setMessage(const QSharedPointer<KMime::Message> &msg); |
90 | KMime::Message::Ptr assembleMessage(); | ||
130 | 91 | ||
131 | ComposerContext mContext; | 92 | QScopedPointer<Kube::ControllerAction> mSendAction; |
93 | QScopedPointer<Kube::ControllerAction> mSaveAsDraftAction; | ||
94 | QScopedPointer<Completer> mRecipientCompleter; | ||
95 | QScopedPointer<Selector> mIdentitySelector; | ||
132 | }; | 96 | }; |
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 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | #include "controller.h" | ||
20 | |||
21 | #include <QQmlEngine> | ||
22 | #include <QMetaProperty> | ||
23 | |||
24 | using namespace Kube; | ||
25 | |||
26 | ControllerAction::ControllerAction() | ||
27 | : QObject() | ||
28 | { | ||
29 | QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); | ||
30 | } | ||
31 | |||
32 | void ControllerAction::execute() | ||
33 | { | ||
34 | emit triggered(); | ||
35 | } | ||
36 | |||
37 | void Controller::clear() | ||
38 | { | ||
39 | auto meta = metaObject(); | ||
40 | for (auto i = meta->propertyOffset(); i < meta->propertyCount(); i++) { | ||
41 | auto property = meta->property(i); | ||
42 | setProperty(property.name(), QVariant()); | ||
43 | } | ||
44 | for (const auto &p : dynamicPropertyNames()) { | ||
45 | setProperty(p, QVariant()); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | void Controller::run(const KAsync::Job<void> &job) | ||
50 | { | ||
51 | auto jobToExec = job; | ||
52 | //TODO handle error | ||
53 | //TODO attach a log context to the execution that we can gather from the job? | ||
54 | jobToExec.exec(); | ||
55 | } | ||
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 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | #pragma once | ||
20 | |||
21 | #include <QObject> | ||
22 | #include <QVariant> | ||
23 | #include <Async/Async> | ||
24 | |||
25 | #define KUBE_CONTROLLER_PROPERTY(TYPE, NAME, LOWERCASENAME) \ | ||
26 | public: Q_PROPERTY(TYPE LOWERCASENAME MEMBER m##NAME NOTIFY LOWERCASENAME##Changed) \ | ||
27 | Q_SIGNALS: void LOWERCASENAME##Changed(); \ | ||
28 | private: TYPE m##NAME; \ | ||
29 | public: \ | ||
30 | struct NAME { \ | ||
31 | static constexpr const char *name = #LOWERCASENAME; \ | ||
32 | typedef TYPE Type; \ | ||
33 | }; \ | ||
34 | void set##NAME(const TYPE &value) { setProperty(NAME::name, QVariant::fromValue(value)); } \ | ||
35 | void clear##NAME() { setProperty(NAME::name, QVariant{}); } \ | ||
36 | TYPE get##NAME() const { return m##NAME; } \ | ||
37 | |||
38 | namespace Kube { | ||
39 | |||
40 | class ControllerAction : public QObject { | ||
41 | Q_OBJECT | ||
42 | Q_PROPERTY(bool enabled MEMBER mEnabled NOTIFY enabledChanged) | ||
43 | public: | ||
44 | ControllerAction(); | ||
45 | ~ControllerAction() = default; | ||
46 | |||
47 | Q_INVOKABLE void execute(); | ||
48 | void setEnabled(bool enabled) { setProperty("enabled", enabled); } | ||
49 | |||
50 | signals: | ||
51 | void enabledChanged(); | ||
52 | void triggered(); | ||
53 | |||
54 | private: | ||
55 | bool mEnabled = true; | ||
56 | }; | ||
57 | |||
58 | class Controller : public QObject { | ||
59 | Q_OBJECT | ||
60 | public: | ||
61 | Controller() = default; | ||
62 | virtual ~Controller() = default; | ||
63 | |||
64 | public slots: | ||
65 | void clear(); | ||
66 | |||
67 | signals: | ||
68 | void done(); | ||
69 | void error(); | ||
70 | |||
71 | protected: | ||
72 | void run(const KAsync::Job<void> &job); | ||
73 | }; | ||
74 | |||
75 | } | ||
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 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekofp <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | #include "selector.h" | ||
20 | |||
21 | #include <QQmlEngine> | ||
22 | |||
23 | Selector::Selector(QAbstractItemModel *model) : mModel{model} | ||
24 | { | ||
25 | QQmlEngine::setObjectOwnership(mModel, QQmlEngine::CppOwnership); | ||
26 | } | ||
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 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekofp <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | #pragma once | ||
20 | |||
21 | #include <QAbstractItemModel> | ||
22 | #include <QString> | ||
23 | |||
24 | /** | ||
25 | * Exposes a model and maintains a current index selection. | ||
26 | */ | ||
27 | class Selector : public QObject { | ||
28 | Q_OBJECT | ||
29 | Q_PROPERTY (int currentIndex READ currentIndex WRITE setCurrentIndex) | ||
30 | Q_PROPERTY (QAbstractItemModel* model READ model CONSTANT) | ||
31 | |||
32 | public: | ||
33 | Selector(QAbstractItemModel *model); | ||
34 | |||
35 | virtual QAbstractItemModel *model() { return mModel; } | ||
36 | |||
37 | void setCurrentIndex(int i) { | ||
38 | mCurrentIndex = i; | ||
39 | Q_ASSERT(mModel); | ||
40 | setCurrent(mModel->index(mCurrentIndex, 0)); | ||
41 | } | ||
42 | |||
43 | int currentIndex() { return mCurrentIndex; } | ||
44 | |||
45 | virtual void setCurrent(const QModelIndex &) = 0; | ||
46 | private: | ||
47 | QAbstractItemModel *mModel = nullptr; | ||
48 | int mCurrentIndex = 0; | ||
49 | }; | ||
50 | |||