diff options
Diffstat (limited to 'framework')
-rw-r--r-- | framework/src/domain/composercontroller.cpp | 426 | ||||
-rw-r--r-- | framework/src/domain/composercontroller.h | 57 | ||||
-rw-r--r-- | framework/src/domain/controller.cpp | 108 | ||||
-rw-r--r-- | framework/src/domain/controller.h | 68 | ||||
-rw-r--r-- | framework/src/frameworkplugin.cpp | 2 |
5 files changed, 309 insertions, 352 deletions
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 @@ | |||
41 | #include "mime/mailtemplates.h" | 41 | #include "mime/mailtemplates.h" |
42 | #include "mime/mailcrypto.h" | 42 | #include "mime/mailcrypto.h" |
43 | 43 | ||
44 | Q_DECLARE_METATYPE(GpgME::Key); | 44 | std::vector<GpgME::Key> &operator+=(std::vector<GpgME::Key> &list, const std::vector<GpgME::Key> &add) |
45 | { | ||
46 | list.insert(std::end(list), std::begin(add), std::end(add)); | ||
47 | return list; | ||
48 | } | ||
45 | 49 | ||
46 | class IdentitySelector : public Selector { | 50 | class IdentitySelector : public Selector { |
47 | public: | 51 | public: |
@@ -93,14 +97,6 @@ public: | |||
93 | } | 97 | } |
94 | }; | 98 | }; |
95 | 99 | ||
96 | static void traverse(const QStandardItemModel *model, const std::function<void(QStandardItem *item)> &f) | ||
97 | { | ||
98 | auto root = model->invisibleRootItem(); | ||
99 | for (int row = 0; row < root->rowCount(); row++) { | ||
100 | f(root->child(row, 0)); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | template<typename T> | 100 | template<typename T> |
105 | void asyncRun(QObject *object, std::function<T()> run, std::function<void(T)> continuation) | 101 | void asyncRun(QObject *object, std::function<T()> run, std::function<void(T)> continuation) |
106 | { | 102 | { |
@@ -116,33 +112,22 @@ void asyncRun(QObject *object, std::function<T()> run, std::function<void(T)> co | |||
116 | watcher->setFuture(future); | 112 | watcher->setFuture(future); |
117 | } | 113 | } |
118 | 114 | ||
119 | class AddresseeModel : public QStandardItemModel | 115 | class AddresseeController : public Kube::ListPropertyController |
120 | { | 116 | { |
121 | public: | 117 | public: |
122 | AddresseeModel() | ||
123 | :QStandardItemModel() | ||
124 | { | ||
125 | setItemRoleNames({{ComposerController::AddresseeNameRole, "addresseeName"}, | ||
126 | {ComposerController::KeyFoundRole, "keyFound"}, | ||
127 | {ComposerController::KeyMissingRole, "keyMissing"}, | ||
128 | {ComposerController::KeyRole, "key"}}); | ||
129 | } | ||
130 | 118 | ||
131 | void add(const QString &addressee) | 119 | QSet<QByteArray> mMissingKeys; |
120 | AddresseeController() | ||
121 | : Kube::ListPropertyController{{"name", "keyFound", "key"}} | ||
132 | { | 122 | { |
133 | auto item = new QStandardItem; | 123 | QObject::connect(this, &Kube::ListPropertyController::added, this, [this] (const QByteArray &id, const QVariantMap &map) { |
134 | item->setText(addressee); | 124 | findKey(id, map.value("name").toString()); |
135 | item->setData(QVariant::fromValue(addressee), ComposerController::AddresseeNameRole); | 125 | }); |
136 | item->setData(false, ComposerController::KeyFoundRole); | ||
137 | item->setData(mCryptoEnabled, ComposerController::KeyMissingRole); | ||
138 | appendRow(QList<QStandardItem*>() << item); | ||
139 | if (mCryptoEnabled) { | ||
140 | findKey(addressee); | ||
141 | } | ||
142 | } | 126 | } |
143 | 127 | ||
144 | void findKey(const QString &addressee) | 128 | void findKey(const QByteArray &id, const QString &addressee) |
145 | { | 129 | { |
130 | mMissingKeys << id; | ||
146 | KMime::Types::Mailbox mb; | 131 | KMime::Types::Mailbox mb; |
147 | mb.fromUnicodeString(addressee); | 132 | mb.fromUnicodeString(addressee); |
148 | 133 | ||
@@ -150,126 +135,88 @@ public: | |||
150 | asyncRun<std::vector<GpgME::Key>>(this, [mb] { | 135 | asyncRun<std::vector<GpgME::Key>>(this, [mb] { |
151 | return MailCrypto::findKeys(QStringList{} << mb.address(), false, false, MailCrypto::OPENPGP); | 136 | return MailCrypto::findKeys(QStringList{} << mb.address(), false, false, MailCrypto::OPENPGP); |
152 | }, | 137 | }, |
153 | [this, addressee](const std::vector<GpgME::Key> &keys) { | 138 | [this, addressee, id](const std::vector<GpgME::Key> &keys) { |
154 | if (!keys.empty()) { | 139 | if (!keys.empty()) { |
155 | if (keys.size() > 1 ) { | 140 | if (keys.size() > 1 ) { |
156 | SinkWarning() << "Found more than one key, encrypting to all of them."; | 141 | SinkWarning() << "Found more than one key, encrypting to all of them."; |
157 | } | 142 | } |
158 | SinkLog() << "Found key: " << keys.front().primaryFingerprint(); | 143 | SinkLog() << "Found key: " << keys.front().primaryFingerprint(); |
159 | for (auto item : findItems(addressee)) { | 144 | setValue(id, "keyFound", true); |
160 | item->setData(true, ComposerController::KeyFoundRole); | 145 | setValue(id, "key", QVariant::fromValue(keys)); |
161 | item->setData(QVariant::fromValue(keys), ComposerController::KeyRole); | 146 | mMissingKeys.remove(id); |
162 | } | 147 | setProperty("foundAllKeys", mMissingKeys.isEmpty()); |
163 | } else { | 148 | } else { |
164 | SinkWarning() << "Failed to find key for recipient."; | 149 | SinkWarning() << "Failed to find key for recipient."; |
165 | } | 150 | } |
166 | }); | 151 | }); |
167 | } | 152 | } |
168 | 153 | ||
169 | void remove(const QString &addressee) | 154 | void set(const QStringList &list) |
170 | { | 155 | { |
171 | auto root = invisibleRootItem(); | 156 | for (const auto &email: list) { |
172 | for (int row = 0; row < root->rowCount(); row++) { | 157 | add({{"name", email}}); |
173 | if (root->child(row, 0)->text() == addressee) { | ||
174 | root->removeRow(row); | ||
175 | return; | ||
176 | } | ||
177 | } | 158 | } |
178 | } | 159 | } |
160 | }; | ||
179 | 161 | ||
180 | bool foundAllKeys() | 162 | class AttachmentController : public Kube::ListPropertyController |
181 | { | 163 | { |
182 | std::vector<GpgME::Key> keys; | 164 | public: |
183 | auto root = invisibleRootItem(); | ||
184 | for (int row = 0; row < root->rowCount(); row++) { | ||
185 | auto item = root->child(row, 0); | ||
186 | if (!item->data(ComposerController::KeyFoundRole).toBool()) { | ||
187 | return false; | ||
188 | } | ||
189 | } | ||
190 | return true; | ||
191 | } | ||
192 | 165 | ||
193 | std::vector<GpgME::Key> getKeys() | 166 | AttachmentController() |
167 | : Kube::ListPropertyController{{"name", "filename", "content", "mimetype", "description", "iconname", "url", "inline"}} | ||
194 | { | 168 | { |
195 | std::vector<GpgME::Key> keys; | 169 | QObject::connect(this, &Kube::ListPropertyController::added, this, [this] (const QByteArray &id, const QVariantMap &map) { |
196 | traverse(this, [&] (QStandardItem *item) { | 170 | auto url = map.value("url").toUrl(); |
197 | auto l = item->data(ComposerController::KeyRole).value<std::vector<GpgME::Key>>(); | 171 | setAttachmentProperties(id, url); |
198 | keys.insert(std::end(keys), std::begin(l), std::end(l)); | ||
199 | }); | 172 | }); |
200 | return keys; | ||
201 | } | 173 | } |
202 | 174 | ||
203 | void setStringList(const QStringList &list) | 175 | void setAttachmentProperties(const QByteArray &id, const QUrl &url) |
204 | { | 176 | { |
205 | clear(); | 177 | QMimeDatabase db; |
206 | for (const auto &s : list) { | 178 | auto mimeType = db.mimeTypeForUrl(url); |
207 | add(s); | 179 | if (mimeType.name() == QLatin1String("inode/directory")) { |
208 | } | 180 | qWarning() << "Can't deal with directories yet."; |
209 | } | 181 | } else { |
210 | 182 | if (!url.isLocalFile()) { | |
211 | QStringList stringList() const | 183 | qWarning() << "Cannot attach remote file: " << url; |
212 | { | 184 | return; |
213 | QStringList list; | 185 | } |
214 | traverse(this, [&] (QStandardItem *item) { | ||
215 | list << item->text(); | ||
216 | }); | ||
217 | return list; | ||
218 | } | ||
219 | 186 | ||
220 | void setCryptoEnabled(bool enabled) | 187 | QFileInfo fileInfo(url.toLocalFile()); |
221 | { | 188 | if (!fileInfo.exists()) { |
222 | mCryptoEnabled = enabled; | 189 | qWarning() << "The file doesn't exist: " << url; |
223 | traverse(this, [&] (QStandardItem *item) { | ||
224 | item->setData(mCryptoEnabled, ComposerController::KeyMissingRole); | ||
225 | if (mCryptoEnabled) { | ||
226 | findKey(item->text()); | ||
227 | } | 190 | } |
228 | }); | ||
229 | } | ||
230 | 191 | ||
231 | bool mCryptoEnabled = false; | 192 | QFile file{fileInfo.filePath()}; |
193 | file.open(QIODevice::ReadOnly); | ||
194 | const auto data = file.readAll(); | ||
195 | QVariantMap map; | ||
196 | map.insert("filename", fileInfo.fileName()); | ||
197 | map.insert("mimetype", mimeType.name().toLatin1()); | ||
198 | map.insert("filename", fileInfo.fileName().toLatin1()); | ||
199 | map.insert("inline", false); | ||
200 | map.insert("iconname", mimeType.iconName()); | ||
201 | map.insert("url", url); | ||
202 | map.insert("content", data); | ||
203 | setValues(id, map); | ||
204 | } | ||
205 | } | ||
232 | }; | 206 | }; |
233 | 207 | ||
234 | |||
235 | ComposerController::ComposerController() | 208 | ComposerController::ComposerController() |
236 | : Kube::Controller(), | 209 | : Kube::Controller(), |
210 | controller_to{new AddresseeController}, | ||
211 | controller_cc{new AddresseeController}, | ||
212 | controller_bcc{new AddresseeController}, | ||
213 | controller_attachments{new AttachmentController}, | ||
237 | action_send{new Kube::ControllerAction{this, &ComposerController::send}}, | 214 | action_send{new Kube::ControllerAction{this, &ComposerController::send}}, |
238 | action_saveAsDraft{new Kube::ControllerAction{this, &ComposerController::saveAsDraft}}, | 215 | action_saveAsDraft{new Kube::ControllerAction{this, &ComposerController::saveAsDraft}}, |
239 | mRecipientCompleter{new RecipientCompleter}, | 216 | mRecipientCompleter{new RecipientCompleter}, |
240 | mIdentitySelector{new IdentitySelector{*this}}, | 217 | mIdentitySelector{new IdentitySelector{*this}} |
241 | mToModel{new AddresseeModel}, | ||
242 | mCcModel{new AddresseeModel}, | ||
243 | mBccModel{new AddresseeModel}, | ||
244 | mAttachmentModel{new QStandardItemModel} | ||
245 | { | 218 | { |
246 | mAttachmentModel->setItemRoleNames({{NameRole, "name"}, | ||
247 | {FilenameRole, "filename"}, | ||
248 | {ContentRole, "content"}, | ||
249 | {MimeTypeRole, "mimetype"}, | ||
250 | {DescriptionRole, "description"}, | ||
251 | {IconNameRole, "iconName"}, | ||
252 | {UrlRole, "url"}, | ||
253 | {InlineRole, "inline"}}); | ||
254 | updateSaveAsDraftAction(); | ||
255 | // mSendAction->monitorProperty<To>(); | ||
256 | // mSendAction->monitorProperty<Send>([] (const QString &) -> bool{ | ||
257 | // //validate | ||
258 | // }); | ||
259 | // registerAction<ControllerAction>(&ComposerController::send); | ||
260 | // actionDepends<ControllerAction, To, Subject>(); | ||
261 | // TODO do in constructor | ||
262 | |||
263 | QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSendAction); | ||
264 | QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSendAction); | ||
265 | QObject::connect(this, &ComposerController::subjectChanged, &ComposerController::updateSaveAsDraftAction); | ||
266 | QObject::connect(this, &ComposerController::accountIdChanged, &ComposerController::updateSaveAsDraftAction); | ||
267 | QObject::connect(this, &ComposerController::identityChanged, &ComposerController::findPersonalKey); | 219 | QObject::connect(this, &ComposerController::identityChanged, &ComposerController::findPersonalKey); |
268 | updateSendAction(); | ||
269 | |||
270 | QObject::connect(this, &ComposerController::encryptChanged, [&] () { mToModel->setCryptoEnabled(getEncrypt()); }); | ||
271 | QObject::connect(this, &ComposerController::encryptChanged, [&] () { mCcModel->setCryptoEnabled(getEncrypt()); }); | ||
272 | QObject::connect(this, &ComposerController::encryptChanged, [&] () { mBccModel->setCryptoEnabled(getEncrypt()); }); | ||
273 | } | 220 | } |
274 | 221 | ||
275 | void ComposerController::findPersonalKey() | 222 | void ComposerController::findPersonalKey() |
@@ -280,18 +227,17 @@ void ComposerController::findPersonalKey() | |||
280 | return MailCrypto::findKeys(QStringList{} << identity.address(), true); | 227 | return MailCrypto::findKeys(QStringList{} << identity.address(), true); |
281 | }, | 228 | }, |
282 | [this](const std::vector<GpgME::Key> &keys) { | 229 | [this](const std::vector<GpgME::Key> &keys) { |
283 | mPersonalKeys = keys; | 230 | if (keys.empty()) { |
284 | if (mPersonalKeys.empty()) { | ||
285 | SinkWarning() << "Failed to find a personal key."; | 231 | SinkWarning() << "Failed to find a personal key."; |
286 | } | 232 | } |
287 | if (mPersonalKeys.size() > 1) { | 233 | if (keys.size() > 1) { |
288 | SinkWarning() << "Found multiple keys, using first one:"; | 234 | SinkWarning() << "Found multiple keys, using first one:"; |
289 | SinkWarning() << " " << mPersonalKeys.front().primaryFingerprint(); | 235 | SinkWarning() << " " << keys.front().primaryFingerprint(); |
290 | } else { | 236 | } else { |
291 | SinkLog() << "Found personal key: " << mPersonalKeys.front().primaryFingerprint(); | 237 | SinkLog() << "Found personal key: " << keys.front().primaryFingerprint(); |
292 | } | 238 | } |
293 | updateSendAction(); | 239 | setPersonalKeys(QVariant::fromValue(keys)); |
294 | setEncryptionAvailable(!mPersonalKeys.empty()); | 240 | setFoundPersonalKeys(!keys.empty()); |
295 | }); | 241 | }); |
296 | } | 242 | } |
297 | 243 | ||
@@ -300,111 +246,10 @@ void ComposerController::clear() | |||
300 | Controller::clear(); | 246 | Controller::clear(); |
301 | //Reapply account and identity from selection | 247 | //Reapply account and identity from selection |
302 | mIdentitySelector->reapplyCurrentIndex(); | 248 | mIdentitySelector->reapplyCurrentIndex(); |
303 | mToModel->clear(); | 249 | //FIXME implement in Controller::clear instead |
304 | mCcModel->clear(); | 250 | toController()->clear(); |
305 | mBccModel->clear(); | 251 | ccController()->clear(); |
306 | } | 252 | bccController()->clear(); |
307 | |||
308 | QAbstractItemModel *ComposerController::toModel() const | ||
309 | { | ||
310 | return mToModel.data(); | ||
311 | } | ||
312 | |||
313 | void ComposerController::addTo(const QString &s) | ||
314 | { | ||
315 | mToModel->add(s); | ||
316 | updateSendAction(); | ||
317 | } | ||
318 | |||
319 | void ComposerController::removeTo(const QString &s) | ||
320 | { | ||
321 | mToModel->remove(s); | ||
322 | updateSendAction(); | ||
323 | } | ||
324 | |||
325 | QAbstractItemModel *ComposerController::ccModel() const | ||
326 | { | ||
327 | return mCcModel.data(); | ||
328 | } | ||
329 | |||
330 | void ComposerController::addCc(const QString &s) | ||
331 | { | ||
332 | mCcModel->add(s); | ||
333 | updateSendAction(); | ||
334 | } | ||
335 | |||
336 | void ComposerController::removeCc(const QString &s) | ||
337 | { | ||
338 | mCcModel->remove(s); | ||
339 | updateSendAction(); | ||
340 | } | ||
341 | |||
342 | QAbstractItemModel *ComposerController::bccModel() const | ||
343 | { | ||
344 | return mBccModel.data(); | ||
345 | } | ||
346 | |||
347 | void ComposerController::addBcc(const QString &s) | ||
348 | { | ||
349 | mBccModel->add(s); | ||
350 | updateSendAction(); | ||
351 | } | ||
352 | |||
353 | void ComposerController::removeBcc(const QString &s) | ||
354 | { | ||
355 | mBccModel->remove(s); | ||
356 | updateSendAction(); | ||
357 | } | ||
358 | |||
359 | QAbstractItemModel *ComposerController::attachmentModel() const | ||
360 | { | ||
361 | return mAttachmentModel.data(); | ||
362 | } | ||
363 | |||
364 | void ComposerController::addAttachment(const QUrl &url) | ||
365 | { | ||
366 | QMimeDatabase db; | ||
367 | auto mimeType = db.mimeTypeForUrl(url); | ||
368 | if (mimeType.name() == QLatin1String("inode/directory")) { | ||
369 | qWarning() << "Can't deal with directories yet."; | ||
370 | } else { | ||
371 | if (!url.isLocalFile()) { | ||
372 | qWarning() << "Cannot attach remote file: " << url; | ||
373 | return; | ||
374 | } | ||
375 | |||
376 | QFileInfo fileInfo(url.toLocalFile()); | ||
377 | if (!fileInfo.exists()) { | ||
378 | qWarning() << "The file doesn't exist: " << url; | ||
379 | } | ||
380 | |||
381 | QFile file{fileInfo.filePath()}; | ||
382 | file.open(QIODevice::ReadOnly); | ||
383 | const auto data = file.readAll(); | ||
384 | auto item = new QStandardItem; | ||
385 | item->setData(fileInfo.fileName(), NameRole); | ||
386 | item->setData(mimeType.name().toLatin1(), MimeTypeRole); | ||
387 | item->setData(fileInfo.fileName(), FilenameRole); | ||
388 | item->setData(false, InlineRole); | ||
389 | item->setData(mimeType.iconName(), IconNameRole); | ||
390 | item->setData(url, UrlRole); | ||
391 | item->setData(data, ContentRole); | ||
392 | mAttachmentModel->appendRow(item); | ||
393 | } | ||
394 | } | ||
395 | |||
396 | void ComposerController::removeAttachment(const QUrl &url) | ||
397 | { | ||
398 | auto root = mAttachmentModel->invisibleRootItem(); | ||
399 | if (root->hasChildren()) { | ||
400 | for (int row = 0; row < root->rowCount(); row++) { | ||
401 | auto item = root->child(row, 0); | ||
402 | if (url == item->data(UrlRole).toUrl()) { | ||
403 | root->removeRow(row); | ||
404 | return; | ||
405 | } | ||
406 | } | ||
407 | } | ||
408 | } | 253 | } |
409 | 254 | ||
410 | Completer *ComposerController::recipientCompleter() const | 255 | Completer *ComposerController::recipientCompleter() const |
@@ -450,24 +295,23 @@ static QStringList getStringListFromAddresses(const KMime::Types::Mailbox::List | |||
450 | 295 | ||
451 | void ComposerController::addAttachmentPart(KMime::Content *partToAttach) | 296 | void ComposerController::addAttachmentPart(KMime::Content *partToAttach) |
452 | { | 297 | { |
453 | auto item = new QStandardItem; | 298 | QVariantMap map; |
454 | |||
455 | if (partToAttach->contentType()->mimeType() == "multipart/digest" || | 299 | if (partToAttach->contentType()->mimeType() == "multipart/digest" || |
456 | partToAttach->contentType()->mimeType() == "message/rfc822") { | 300 | partToAttach->contentType()->mimeType() == "message/rfc822") { |
457 | // if it is a digest or a full message, use the encodedContent() of the attachment, | 301 | // if it is a digest or a full message, use the encodedContent() of the attachment, |
458 | // which already has the proper headers | 302 | // which already has the proper headers |
459 | item->setData(partToAttach->encodedContent(), ContentRole); | 303 | map.insert("content", partToAttach->encodedContent()); |
460 | } else { | 304 | } else { |
461 | item->setData(partToAttach->decodedContent(), ContentRole); | 305 | map.insert("content", partToAttach->decodedContent()); |
462 | } | 306 | } |
463 | item->setData(partToAttach->contentType()->mimeType(), MimeTypeRole); | 307 | map.insert("mimetype", partToAttach->contentType()->mimeType()); |
464 | 308 | ||
465 | QMimeDatabase db; | 309 | QMimeDatabase db; |
466 | auto mimeType = db.mimeTypeForName(partToAttach->contentType()->mimeType()); | 310 | auto mimeType = db.mimeTypeForName(partToAttach->contentType()->mimeType()); |
467 | item->setData(mimeType.iconName(), IconNameRole); | 311 | map.insert("iconname", mimeType.iconName()); |
468 | 312 | ||
469 | if (partToAttach->contentDescription(false)) { | 313 | if (partToAttach->contentDescription(false)) { |
470 | item->setData(partToAttach->contentDescription()->asUnicodeString(), DescriptionRole); | 314 | map.insert("description", partToAttach->contentDescription()->asUnicodeString()); |
471 | } | 315 | } |
472 | QString name; | 316 | QString name; |
473 | QString filename; | 317 | QString filename; |
@@ -478,7 +322,7 @@ void ComposerController::addAttachmentPart(KMime::Content *partToAttach) | |||
478 | } | 322 | } |
479 | if (partToAttach->contentDisposition(false)) { | 323 | if (partToAttach->contentDisposition(false)) { |
480 | filename = partToAttach->contentDisposition()->filename(); | 324 | filename = partToAttach->contentDisposition()->filename(); |
481 | item->setData(partToAttach->contentDisposition()->disposition() == KMime::Headers::CDinline, InlineRole); | 325 | map.insert("inline", partToAttach->contentDisposition()->disposition() == KMime::Headers::CDinline); |
482 | } | 326 | } |
483 | 327 | ||
484 | if (name.isEmpty() && !filename.isEmpty()) { | 328 | if (name.isEmpty() && !filename.isEmpty()) { |
@@ -489,20 +333,19 @@ void ComposerController::addAttachmentPart(KMime::Content *partToAttach) | |||
489 | } | 333 | } |
490 | 334 | ||
491 | if (!filename.isEmpty()) { | 335 | if (!filename.isEmpty()) { |
492 | item->setData(filename, FilenameRole); | 336 | map.insert("filename", filename); |
493 | } | 337 | } |
494 | if (!name.isEmpty()) { | 338 | if (!name.isEmpty()) { |
495 | item->setData(name, NameRole); | 339 | map.insert("name", name); |
496 | } | 340 | } |
497 | 341 | attachmentsController()->add(map); | |
498 | mAttachmentModel->appendRow(item); | ||
499 | } | 342 | } |
500 | 343 | ||
501 | void ComposerController::setMessage(const KMime::Message::Ptr &msg) | 344 | void ComposerController::setMessage(const KMime::Message::Ptr &msg) |
502 | { | 345 | { |
503 | mToModel->setStringList(getStringListFromAddresses(msg->to(true)->mailboxes())); | 346 | static_cast<AddresseeController*>(toController())->set(getStringListFromAddresses(msg->to(true)->mailboxes())); |
504 | mCcModel->setStringList(getStringListFromAddresses(msg->cc(true)->mailboxes())); | 347 | static_cast<AddresseeController*>(ccController())->set(getStringListFromAddresses(msg->cc(true)->mailboxes())); |
505 | mBccModel->setStringList(getStringListFromAddresses(msg->bcc(true)->mailboxes())); | 348 | static_cast<AddresseeController*>(bccController())->set(getStringListFromAddresses(msg->bcc(true)->mailboxes())); |
506 | 349 | ||
507 | setSubject(msg->subject(true)->asUnicodeString()); | 350 | setSubject(msg->subject(true)->asUnicodeString()); |
508 | bool isHtml = false; | 351 | bool isHtml = false; |
@@ -563,75 +406,66 @@ void ComposerController::recordForAutocompletion(const QByteArray &addrSpec, con | |||
563 | } | 406 | } |
564 | } | 407 | } |
565 | 408 | ||
409 | std::vector<GpgME::Key> ComposerController::getRecipientKeys() | ||
410 | { | ||
411 | std::vector<GpgME::Key> keys; | ||
412 | { | ||
413 | const auto list = toController()->getList<std::vector<GpgME::Key>>("key"); | ||
414 | for (const auto &l: list) { | ||
415 | keys.insert(std::end(keys), std::begin(l), std::end(l)); | ||
416 | } | ||
417 | } | ||
418 | { | ||
419 | const auto list = ccController()->getList<std::vector<GpgME::Key>>("key"); | ||
420 | for (const auto &l: list) { | ||
421 | keys.insert(std::end(keys), std::begin(l), std::end(l)); | ||
422 | } | ||
423 | } | ||
424 | { | ||
425 | const auto list = bccController()->getList<std::vector<GpgME::Key>>("key"); | ||
426 | for (const auto &l: list) { | ||
427 | keys.insert(std::end(keys), std::begin(l), std::end(l)); | ||
428 | } | ||
429 | } | ||
430 | return keys; | ||
431 | } | ||
432 | |||
566 | KMime::Message::Ptr ComposerController::assembleMessage() | 433 | KMime::Message::Ptr ComposerController::assembleMessage() |
567 | { | 434 | { |
568 | applyAddresses(mToModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | 435 | auto toAddresses = toController()->getList<QString>("name"); |
569 | recordForAutocompletion(addrSpec, displayName); | 436 | auto ccAddresses = toController()->getList<QString>("name"); |
570 | }); | 437 | auto bccAddresses = toController()->getList<QString>("name"); |
571 | applyAddresses(mCcModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | 438 | applyAddresses(toAddresses + ccAddresses + bccAddresses, [&](const QByteArray &addrSpec, const QByteArray &displayName) { |
572 | recordForAutocompletion(addrSpec, displayName); | ||
573 | }); | ||
574 | applyAddresses(mBccModel->stringList(), [&](const QByteArray &addrSpec, const QByteArray &displayName) { | ||
575 | recordForAutocompletion(addrSpec, displayName); | 439 | recordForAutocompletion(addrSpec, displayName); |
576 | }); | 440 | }); |
577 | 441 | ||
578 | QList<Attachment> attachments; | 442 | QList<Attachment> attachments; |
579 | auto root = mAttachmentModel->invisibleRootItem(); | 443 | attachmentsController()->traverse([&](const QVariantMap &value) { |
580 | for (int row = 0; row < root->rowCount(); row++) { | ||
581 | auto item = root->child(row, 0); | ||
582 | attachments << Attachment{ | 444 | attachments << Attachment{ |
583 | item->data(NameRole).toString(), | 445 | value["name"].toString(), |
584 | item->data(FilenameRole).toString(), | 446 | value["filename"].toString(), |
585 | item->data(MimeTypeRole).toByteArray(), | 447 | value["mimetype"].toByteArray(), |
586 | item->data(InlineRole).toBool(), | 448 | value["inline"].toBool(), |
587 | item->data(ContentRole).toByteArray() | 449 | value["content"].toByteArray() |
588 | }; | 450 | }; |
589 | } | 451 | }); |
590 | 452 | ||
591 | std::vector<GpgME::Key> signingKeys; | 453 | std::vector<GpgME::Key> signingKeys; |
592 | if (getSign()) { | 454 | if (getSign()) { |
593 | signingKeys = mPersonalKeys; | 455 | signingKeys = getPersonalKeys().value<std::vector<GpgME::Key>>(); |
594 | } | 456 | } |
595 | std::vector<GpgME::Key> encryptionKeys; | 457 | std::vector<GpgME::Key> encryptionKeys; |
596 | if (getEncrypt()) { | 458 | if (getEncrypt()) { |
597 | if (!mToModel->foundAllKeys() || !mCcModel->foundAllKeys() || !mBccModel->foundAllKeys()) { | ||
598 | qWarning() << "Can't encrypt with missing keys"; | ||
599 | return nullptr; | ||
600 | } | ||
601 | //Encrypt to self so we can read the sent message | 459 | //Encrypt to self so we can read the sent message |
602 | encryptionKeys.insert(std::end(encryptionKeys), std::begin(mPersonalKeys), std::end(mPersonalKeys)); | 460 | encryptionKeys += getPersonalKeys().value<std::vector<GpgME::Key>>(); |
603 | auto toKeys = mToModel->getKeys(); | 461 | encryptionKeys += getRecipientKeys(); |
604 | encryptionKeys.insert(std::end(encryptionKeys), std::begin(toKeys), std::end(toKeys)); | ||
605 | auto ccKeys = mCcModel->getKeys(); | ||
606 | encryptionKeys.insert(std::end(encryptionKeys), std::begin(ccKeys), std::end(ccKeys)); | ||
607 | auto bccKeys = mBccModel->getKeys(); | ||
608 | encryptionKeys.insert(std::end(encryptionKeys), std::begin(bccKeys), std::end(bccKeys)); | ||
609 | } | 462 | } |
610 | 463 | ||
611 | return MailTemplates::createMessage(mExistingMessage, mToModel->stringList(), mCcModel->stringList(), mBccModel->stringList(), getIdentity(), getSubject(), getBody(), getHtmlBody(), attachments, signingKeys, encryptionKeys); | 464 | return MailTemplates::createMessage(mExistingMessage, toAddresses, ccAddresses, bccAddresses, getIdentity(), getSubject(), getBody(), getHtmlBody(), attachments, signingKeys, encryptionKeys); |
612 | } | ||
613 | |||
614 | void ComposerController::updateSendAction() | ||
615 | { | ||
616 | auto enabled = !mToModel->stringList().isEmpty() && !getSubject().isEmpty() && !getAccountId().isEmpty(); | ||
617 | if (getEncrypt()) { | ||
618 | if (!mToModel->foundAllKeys() || !mCcModel->foundAllKeys() || !mBccModel->foundAllKeys()) { | ||
619 | SinkWarning() << "Don't have all keys"; | ||
620 | enabled = false; | ||
621 | } | ||
622 | } | ||
623 | if (getSign()) { | ||
624 | if (mPersonalKeys.empty()) { | ||
625 | enabled = false; | ||
626 | } | ||
627 | } | ||
628 | sendAction()->setEnabled(enabled); | ||
629 | } | 465 | } |
630 | 466 | ||
631 | void ComposerController::send() | 467 | void ComposerController::send() |
632 | { | 468 | { |
633 | // verify<To, Subject>() | ||
634 | // && verify<Subject>(); | ||
635 | auto message = assembleMessage(); | 469 | auto message = assembleMessage(); |
636 | if (!message) { | 470 | if (!message) { |
637 | SinkWarning() << "Failed to assemble the message."; | 471 | SinkWarning() << "Failed to assemble the message."; |
@@ -670,12 +504,6 @@ void ComposerController::send() | |||
670 | run(job); | 504 | run(job); |
671 | } | 505 | } |
672 | 506 | ||
673 | void ComposerController::updateSaveAsDraftAction() | ||
674 | { | ||
675 | bool enabled = !getAccountId().isEmpty(); | ||
676 | sendAction()->setEnabled(enabled); | ||
677 | } | ||
678 | |||
679 | void ComposerController::saveAsDraft() | 507 | void ComposerController::saveAsDraft() |
680 | { | 508 | { |
681 | SinkLog() << "Save as draft"; | 509 | SinkLog() << "Save as draft"; |
@@ -683,9 +511,8 @@ void ComposerController::saveAsDraft() | |||
683 | auto existingMail = getExistingMail(); | 511 | auto existingMail = getExistingMail(); |
684 | 512 | ||
685 | auto message = assembleMessage(); | 513 | auto message = assembleMessage(); |
686 | //FIXME this is something for the validation | ||
687 | if (!message) { | 514 | if (!message) { |
688 | SinkWarning() << "Failed to get the mail: "; | 515 | SinkWarning() << "Failed to assemble the message."; |
689 | return; | 516 | return; |
690 | } | 517 | } |
691 | 518 | ||
@@ -720,3 +547,4 @@ void ComposerController::saveAsDraft() | |||
720 | }); | 547 | }); |
721 | run(job); | 548 | run(job); |
722 | } | 549 | } |
550 | |||
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 | |||
41 | 41 | ||
42 | Q_DECLARE_METATYPE(KMime::Types::Mailbox); | 42 | Q_DECLARE_METATYPE(KMime::Types::Mailbox); |
43 | 43 | ||
44 | Q_DECLARE_METATYPE(GpgME::Key); | ||
45 | |||
44 | namespace KMime { | 46 | namespace KMime { |
45 | class Message; | 47 | class Message; |
46 | } | 48 | } |
@@ -57,7 +59,6 @@ class ComposerController : public Kube::Controller | |||
57 | KUBE_CONTROLLER_PROPERTY(bool, HtmlBody, htmlBody) | 59 | KUBE_CONTROLLER_PROPERTY(bool, HtmlBody, htmlBody) |
58 | KUBE_CONTROLLER_PROPERTY(bool, Encrypt, encrypt) | 60 | KUBE_CONTROLLER_PROPERTY(bool, Encrypt, encrypt) |
59 | KUBE_CONTROLLER_PROPERTY(bool, Sign, sign) | 61 | KUBE_CONTROLLER_PROPERTY(bool, Sign, sign) |
60 | KUBE_CONTROLLER_PROPERTY(bool, EncryptionAvailable, encryptionAvailable) | ||
61 | 62 | ||
62 | //Set by identitySelector | 63 | //Set by identitySelector |
63 | KUBE_CONTROLLER_PROPERTY(KMime::Types::Mailbox, Identity, identity) | 64 | KUBE_CONTROLLER_PROPERTY(KMime::Types::Mailbox, Identity, identity) |
@@ -67,26 +68,21 @@ class ComposerController : public Kube::Controller | |||
67 | KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) | 68 | KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) |
68 | KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) | 69 | KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) |
69 | 70 | ||
71 | KUBE_CONTROLLER_PROPERTY(/*std::vector<GpgME::Key>*/QVariant, PersonalKeys, personalKeys) | ||
72 | KUBE_CONTROLLER_PROPERTY(bool, FoundPersonalKeys, foundPersonalKeys) | ||
73 | |||
74 | KUBE_CONTROLLER_LISTCONTROLLER(to) | ||
75 | KUBE_CONTROLLER_LISTCONTROLLER(cc) | ||
76 | KUBE_CONTROLLER_LISTCONTROLLER(bcc) | ||
77 | KUBE_CONTROLLER_LISTCONTROLLER(attachments) | ||
78 | |||
70 | Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT) | 79 | Q_PROPERTY (Completer* recipientCompleter READ recipientCompleter CONSTANT) |
71 | Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT) | 80 | Q_PROPERTY (Selector* identitySelector READ identitySelector CONSTANT) |
72 | //Q_PROPERTY (QValidator* subjectValidator READ subjectValidator CONSTANT) | ||
73 | |||
74 | Q_PROPERTY (QAbstractItemModel* toModel READ toModel CONSTANT) | ||
75 | Q_PROPERTY (QAbstractItemModel* ccModel READ ccModel CONSTANT) | ||
76 | Q_PROPERTY (QAbstractItemModel* bccModel READ bccModel CONSTANT) | ||
77 | Q_PROPERTY (QAbstractItemModel* attachmentModel READ attachmentModel CONSTANT) | ||
78 | 81 | ||
79 | KUBE_CONTROLLER_ACTION(send) | 82 | KUBE_CONTROLLER_ACTION(send) |
80 | KUBE_CONTROLLER_ACTION(saveAsDraft) | 83 | KUBE_CONTROLLER_ACTION(saveAsDraft) |
81 | 84 | ||
82 | public: | 85 | public: |
83 | enum AddresseeRoles { | ||
84 | KeyFoundRole = Qt::UserRole + 1, | ||
85 | KeyMissingRole, | ||
86 | KeyRole, | ||
87 | AddresseeNameRole | ||
88 | }; | ||
89 | |||
90 | explicit ComposerController(); | 86 | explicit ComposerController(); |
91 | 87 | ||
92 | Completer *recipientCompleter() const; | 88 | Completer *recipientCompleter() const; |
@@ -94,50 +90,19 @@ public: | |||
94 | 90 | ||
95 | Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); | 91 | Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); |
96 | 92 | ||
97 | QAbstractItemModel *toModel() const; | ||
98 | QAbstractItemModel *ccModel() const; | ||
99 | QAbstractItemModel *bccModel() const; | ||
100 | QAbstractItemModel *attachmentModel() const; | ||
101 | |||
102 | Q_INVOKABLE void addTo(const QString &); | ||
103 | Q_INVOKABLE void removeTo(const QString &); | ||
104 | Q_INVOKABLE void addCc(const QString &); | ||
105 | Q_INVOKABLE void removeCc(const QString &); | ||
106 | Q_INVOKABLE void addBcc(const QString &); | ||
107 | Q_INVOKABLE void removeBcc(const QString &); | ||
108 | Q_INVOKABLE void addAttachment(const QUrl &); | ||
109 | Q_INVOKABLE void removeAttachment(const QUrl &); | ||
110 | |||
111 | public slots: | 93 | public slots: |
112 | virtual void clear() Q_DECL_OVERRIDE; | 94 | virtual void clear() Q_DECL_OVERRIDE; |
113 | 95 | ||
114 | private slots: | 96 | private slots: |
115 | void updateSendAction(); | ||
116 | void updateSaveAsDraftAction(); | ||
117 | void findPersonalKey(); | 97 | void findPersonalKey(); |
118 | 98 | ||
119 | private: | 99 | private: |
120 | enum AttachmentRoles { | ||
121 | NameRole = Qt::UserRole + 1, | ||
122 | FilenameRole, | ||
123 | ContentRole, | ||
124 | MimeTypeRole, | ||
125 | DescriptionRole, | ||
126 | InlineRole, | ||
127 | IconNameRole, | ||
128 | UrlRole | ||
129 | }; | ||
130 | |||
131 | void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); | 100 | void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); |
132 | void setMessage(const QSharedPointer<KMime::Message> &msg); | 101 | void setMessage(const QSharedPointer<KMime::Message> &msg); |
133 | void addAttachmentPart(KMime::Content *partToAttach); | 102 | void addAttachmentPart(KMime::Content *partToAttach); |
134 | KMime::Message::Ptr assembleMessage(); | 103 | KMime::Message::Ptr assembleMessage(); |
104 | std::vector<GpgME::Key> getRecipientKeys(); | ||
135 | 105 | ||
136 | QScopedPointer<Completer> mRecipientCompleter; | 106 | QScopedPointer<Completer> mRecipientCompleter; |
137 | QScopedPointer<Selector> mIdentitySelector; | 107 | QScopedPointer<Selector> mIdentitySelector; |
138 | QSharedPointer<AddresseeModel> mToModel; | ||
139 | QSharedPointer<AddresseeModel> mCcModel; | ||
140 | QSharedPointer<AddresseeModel> mBccModel; | ||
141 | QScopedPointer<QStandardItemModel> mAttachmentModel; | ||
142 | std::vector<GpgME::Key> mPersonalKeys; | ||
143 | }; | 108 | }; |
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 @@ | |||
20 | 20 | ||
21 | #include <QQmlEngine> | 21 | #include <QQmlEngine> |
22 | #include <QMetaProperty> | 22 | #include <QMetaProperty> |
23 | #include <QStandardItemModel> | ||
24 | #include <QStandardItem> | ||
25 | #include <QUuid> | ||
23 | #include <sink/log.h> | 26 | #include <sink/log.h> |
24 | 27 | ||
25 | using namespace Kube; | 28 | using namespace Kube; |
26 | 29 | ||
27 | ControllerAction::ControllerAction() | 30 | ControllerState::ControllerState() |
28 | : QObject() | 31 | : QObject() |
29 | { | 32 | { |
30 | QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); | 33 | QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); |
31 | } | 34 | } |
32 | 35 | ||
36 | ControllerAction::ControllerAction() | ||
37 | : ControllerState() | ||
38 | { | ||
39 | } | ||
40 | |||
33 | void ControllerAction::execute() | 41 | void ControllerAction::execute() |
34 | { | 42 | { |
35 | emit triggered(); | 43 | emit triggered(); |
@@ -57,3 +65,101 @@ void Controller::run(const KAsync::Job<void> &job) | |||
57 | //TODO attach a log context to the execution that we can gather from the job? | 65 | //TODO attach a log context to the execution that we can gather from the job? |
58 | jobToExec.exec(); | 66 | jobToExec.exec(); |
59 | } | 67 | } |
68 | |||
69 | |||
70 | static void traverse(const QStandardItemModel *model, const std::function<bool(QStandardItem *item)> &f) | ||
71 | { | ||
72 | auto root = model->invisibleRootItem(); | ||
73 | for (int row = 0; row < root->rowCount(); row++) { | ||
74 | if (!f(root->child(row, 0))) { | ||
75 | return; | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | ListPropertyController::ListPropertyController(const QStringList &roles) | ||
81 | : QObject(), | ||
82 | mModel(new QStandardItemModel) | ||
83 | { | ||
84 | //Generate a set of roles for the names. We're not using any enum, so the actual role value doesn't matter. | ||
85 | int role = Qt::UserRole + 1; | ||
86 | mRoles.insert("id", role); | ||
87 | role++; | ||
88 | for (const auto &r : roles) { | ||
89 | mRoles.insert(r, role); | ||
90 | role++; | ||
91 | } | ||
92 | |||
93 | QHash<int, QByteArray> roleNames; | ||
94 | for (const auto r : mRoles.keys()) { | ||
95 | roleNames.insert(mRoles[r], r.toLatin1()); | ||
96 | } | ||
97 | mModel->setItemRoleNames(roleNames); | ||
98 | } | ||
99 | |||
100 | void ListPropertyController::add(const QVariantMap &value) | ||
101 | { | ||
102 | auto item = new QStandardItem; | ||
103 | auto id = QUuid::createUuid().toByteArray(); | ||
104 | item->setData(id, mRoles["id"]); | ||
105 | for (const auto &k : value.keys()) { | ||
106 | item->setData(value.value(k), mRoles[k]); | ||
107 | } | ||
108 | mModel->appendRow(QList<QStandardItem*>() << item); | ||
109 | emit added(id, value); | ||
110 | } | ||
111 | |||
112 | void ListPropertyController::remove(const QByteArray &id) | ||
113 | { | ||
114 | auto root = mModel->invisibleRootItem(); | ||
115 | const auto idRole = mRoles["id"]; | ||
116 | for (int row = 0; row < root->rowCount(); row++) { | ||
117 | if (root->child(row, 0)->data(idRole).toByteArray() == id) { | ||
118 | root->removeRow(row); | ||
119 | return; | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
124 | void ListPropertyController::clear() | ||
125 | { | ||
126 | mModel->clear(); | ||
127 | } | ||
128 | |||
129 | QAbstractItemModel *ListPropertyController::model() | ||
130 | { | ||
131 | QQmlEngine::setObjectOwnership(mModel.data(), QQmlEngine::CppOwnership); | ||
132 | return mModel.data(); | ||
133 | } | ||
134 | |||
135 | void ListPropertyController::setValue(const QByteArray &id, const QString &key, const QVariant &value) | ||
136 | { | ||
137 | setValues(id, {{key, value}}); | ||
138 | } | ||
139 | |||
140 | void ListPropertyController::setValues(const QByteArray &id, const QVariantMap &values) | ||
141 | { | ||
142 | const auto idRole = mRoles["id"]; | ||
143 | ::traverse(mModel.data(), [&] (QStandardItem *item) { | ||
144 | if (item->data(idRole).toByteArray() == id) { | ||
145 | for (const auto &key : values.keys()) { | ||
146 | item->setData(values.value(key), mRoles[key]); | ||
147 | } | ||
148 | return false; | ||
149 | } | ||
150 | return true; | ||
151 | }); | ||
152 | } | ||
153 | |||
154 | void ListPropertyController::traverse(const std::function<void(const QVariantMap &)> &f) | ||
155 | { | ||
156 | ::traverse(mModel.data(), [&] (QStandardItem *item) { | ||
157 | QVariantMap map; | ||
158 | for (const auto &key : mRoles.keys()) { | ||
159 | map.insert(key, item->data(mRoles[key])); | ||
160 | } | ||
161 | f(map); | ||
162 | return true; | ||
163 | }); | ||
164 | } | ||
165 | |||
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,13 +42,36 @@ | |||
42 | public: Kube::ControllerAction* NAME##Action() const { Q_ASSERT(action_##NAME); return action_##NAME.data(); } \ | 42 | public: Kube::ControllerAction* NAME##Action() const { Q_ASSERT(action_##NAME); return action_##NAME.data(); } \ |
43 | private slots: void NAME(); \ | 43 | private slots: void NAME(); \ |
44 | 44 | ||
45 | #define KUBE_CONTROLLER_LISTCONTROLLER(NAME) \ | ||
46 | Q_PROPERTY (Kube::ListPropertyController* NAME READ NAME##Controller CONSTANT) \ | ||
47 | private: QScopedPointer<Kube::ListPropertyController> controller_##NAME; \ | ||
48 | public: Kube::ListPropertyController* NAME##Controller() const { Q_ASSERT(controller_##NAME); return controller_##NAME.data(); } \ | ||
49 | |||
50 | |||
51 | class QAbstractItemModel; | ||
52 | class QStandardItemModel; | ||
45 | 53 | ||
46 | namespace Kube { | 54 | namespace Kube { |
47 | 55 | ||
48 | class ControllerAction : public QObject { | 56 | class ControllerState : public QObject { |
49 | Q_OBJECT | 57 | Q_OBJECT |
50 | Q_PROPERTY(bool enabled MEMBER mEnabled NOTIFY enabledChanged) | 58 | Q_PROPERTY(bool enabled MEMBER mEnabled NOTIFY enabledChanged) |
51 | public: | 59 | public: |
60 | ControllerState(); | ||
61 | ~ControllerState() = default; | ||
62 | |||
63 | void setEnabled(bool enabled) { setProperty("enabled", enabled); } | ||
64 | |||
65 | signals: | ||
66 | void enabledChanged(); | ||
67 | |||
68 | private: | ||
69 | bool mEnabled = false; | ||
70 | }; | ||
71 | |||
72 | class ControllerAction : public ControllerState { | ||
73 | Q_OBJECT | ||
74 | public: | ||
52 | ControllerAction(); | 75 | ControllerAction(); |
53 | template <typename Func> | 76 | template <typename Func> |
54 | ControllerAction(const typename QtPrivate::FunctionPointer<Func>::Object *obj, Func slot) | 77 | ControllerAction(const typename QtPrivate::FunctionPointer<Func>::Object *obj, Func slot) |
@@ -60,14 +83,9 @@ public: | |||
60 | ~ControllerAction() = default; | 83 | ~ControllerAction() = default; |
61 | 84 | ||
62 | Q_INVOKABLE void execute(); | 85 | Q_INVOKABLE void execute(); |
63 | void setEnabled(bool enabled) { setProperty("enabled", enabled); } | ||
64 | 86 | ||
65 | signals: | 87 | signals: |
66 | void enabledChanged(); | ||
67 | void triggered(); | 88 | void triggered(); |
68 | |||
69 | private: | ||
70 | bool mEnabled = true; | ||
71 | }; | 89 | }; |
72 | 90 | ||
73 | class Controller : public QObject { | 91 | class Controller : public QObject { |
@@ -87,4 +105,42 @@ protected: | |||
87 | void run(const KAsync::Job<void> &job); | 105 | void run(const KAsync::Job<void> &job); |
88 | }; | 106 | }; |
89 | 107 | ||
108 | class ListPropertyController : public QObject | ||
109 | { | ||
110 | Q_OBJECT | ||
111 | Q_PROPERTY(QAbstractItemModel* model READ model CONSTANT) | ||
112 | |||
113 | public: | ||
114 | ListPropertyController(const QStringList &roles); | ||
115 | Q_INVOKABLE virtual void add(const QVariantMap &value); | ||
116 | Q_INVOKABLE virtual void remove(const QByteArray &id); | ||
117 | Q_INVOKABLE void clear(); | ||
118 | |||
119 | QAbstractItemModel *model(); | ||
120 | |||
121 | void setValue(const QByteArray &id, const QString &key, const QVariant &); | ||
122 | void setValues(const QByteArray &id, const QVariantMap &values); | ||
123 | void traverse(const std::function<void(const QVariantMap &)> &f); | ||
124 | |||
125 | template<typename T> | ||
126 | QList<T> getList(const QString &property) | ||
127 | { | ||
128 | QList<T> list; | ||
129 | traverse([&] (const QVariantMap &map) { | ||
130 | list << map[property].value<T>(); | ||
131 | }); | ||
132 | return list; | ||
133 | } | ||
134 | |||
135 | Q_SIGNALS: | ||
136 | void added(QByteArray, QVariantMap); | ||
137 | |||
138 | protected: | ||
139 | QScopedPointer<QStandardItemModel> mModel; | ||
140 | |||
141 | private: | ||
142 | QHash<QString, int> mRoles; | ||
143 | }; | ||
144 | |||
145 | |||
90 | } | 146 | } |
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 @@ | |||
39 | #include "webengineprofile.h" | 39 | #include "webengineprofile.h" |
40 | #include "startupcheck.h" | 40 | #include "startupcheck.h" |
41 | #include "keyring.h" | 41 | #include "keyring.h" |
42 | #include "controller.h" | ||
42 | 43 | ||
43 | #include <QtQml> | 44 | #include <QtQml> |
44 | 45 | ||
@@ -70,6 +71,7 @@ void FrameworkPlugin::registerTypes (const char *uri) | |||
70 | qmlRegisterType<FolderListModel>(uri, 1, 0, "FolderListModel"); | 71 | qmlRegisterType<FolderListModel>(uri, 1, 0, "FolderListModel"); |
71 | qmlRegisterType<MailListModel>(uri, 1, 0, "MailListModel"); | 72 | qmlRegisterType<MailListModel>(uri, 1, 0, "MailListModel"); |
72 | qmlRegisterType<ComposerController>(uri, 1, 0, "ComposerController"); | 73 | qmlRegisterType<ComposerController>(uri, 1, 0, "ComposerController"); |
74 | qmlRegisterType<Kube::ControllerAction>(uri, 1, 0, "ControllerAction"); | ||
73 | qmlRegisterType<MessageParser>(uri, 1, 0, "MessageParser"); | 75 | qmlRegisterType<MessageParser>(uri, 1, 0, "MessageParser"); |
74 | qmlRegisterType<Retriever>(uri, 1, 0, "Retriever"); | 76 | qmlRegisterType<Retriever>(uri, 1, 0, "Retriever"); |
75 | qmlRegisterType<OutboxModel>(uri, 1, 0, "OutboxModel"); | 77 | qmlRegisterType<OutboxModel>(uri, 1, 0, "OutboxModel"); |