diff options
19 files changed, 268 insertions, 48 deletions
diff --git a/components/kube/qml/Kube.qml b/components/kube/qml/Kube.qml index bf38ab72..34c2a59e 100644 --- a/components/kube/qml/Kube.qml +++ b/components/kube/qml/Kube.qml | |||
@@ -225,12 +225,17 @@ Controls2.ApplicationWindow { | |||
225 | 225 | ||
226 | Kube.Listener { | 226 | Kube.Listener { |
227 | filter: Kube.Messages.reply | 227 | filter: Kube.Messages.reply |
228 | onMessageReceived: kubeViews.replaceView("composer", {message: message.mail, loadAsDraft: false}) | 228 | onMessageReceived: kubeViews.replaceView("composer", {message: message.mail, loadType: Kube.ComposerController.Reply}) |
229 | } | ||
230 | |||
231 | Kube.Listener { | ||
232 | filter: Kube.Messages.forward | ||
233 | onMessageReceived: kubeViews.replaceView("composer", {message: message.mail, loadType: Kube.ComposerController.Forward}) | ||
229 | } | 234 | } |
230 | 235 | ||
231 | Kube.Listener { | 236 | Kube.Listener { |
232 | filter: Kube.Messages.edit | 237 | filter: Kube.Messages.edit |
233 | onMessageReceived: kubeViews.replaceView("composer", {message: message.mail, loadAsDraft: true}) | 238 | onMessageReceived: kubeViews.replaceView("composer", {message: message.mail, loadType: Kube.ComposerController.Draft}) |
234 | } | 239 | } |
235 | 240 | ||
236 | Kube.Listener { | 241 | Kube.Listener { |
diff --git a/framework/qml/Icons.qml b/framework/qml/Icons.qml index 3636a192..2afe840e 100644 --- a/framework/qml/Icons.qml +++ b/framework/qml/Icons.qml | |||
@@ -41,6 +41,7 @@ Item { | |||
41 | property string edit: "document-edit" | 41 | property string edit: "document-edit" |
42 | property string edit_inverted: "document-edit-inverted" | 42 | property string edit_inverted: "document-edit-inverted" |
43 | property string replyToSender: "mail-reply-sender" | 43 | property string replyToSender: "mail-reply-sender" |
44 | property string forward: "mail-forward" | ||
44 | property string outbox: "mail-folder-outbox" | 45 | property string outbox: "mail-folder-outbox" |
45 | property string outbox_inverted: "mail-folder-outbox-inverted" | 46 | property string outbox_inverted: "mail-folder-outbox-inverted" |
46 | property string copy: "edit-copy" | 47 | property string copy: "edit-copy" |
diff --git a/framework/qml/MailViewer.qml b/framework/qml/MailViewer.qml index 1a832470..f52e694d 100644 --- a/framework/qml/MailViewer.qml +++ b/framework/qml/MailViewer.qml | |||
@@ -370,24 +370,40 @@ Rectangle { | |||
370 | } | 370 | } |
371 | } | 371 | } |
372 | 372 | ||
373 | Kube.IconButton { | 373 | Grid { |
374 | visible: !model.trash | 374 | anchors { |
375 | anchors{ | ||
376 | verticalCenter: parent.verticalCenter | 375 | verticalCenter: parent.verticalCenter |
377 | right: parent.right | 376 | right: parent.right |
378 | rightMargin: Kube.Units.largeSpacing | 377 | rightMargin: Kube.Units.largeSpacing |
379 | } | 378 | } |
380 | activeFocusOnTab: false | 379 | columns: 2 |
381 | 380 | ||
382 | iconName: model.draft ? Kube.Icons.edit : Kube.Icons.replyToSender | 381 | Kube.IconButton { |
383 | onClicked: { | 382 | visible: !model.trash |
384 | if (model.draft) { | 383 | activeFocusOnTab: false |
385 | Kube.Fabric.postMessage(Kube.Messages.edit, {"mail": model.mail, "isDraft": model.draft}) | 384 | |
386 | } else { | 385 | iconName: model.draft ? Kube.Icons.edit : Kube.Icons.replyToSender |
387 | Kube.Fabric.postMessage(Kube.Messages.reply, {"mail": model.mail, "isDraft": model.draft}) | 386 | onClicked: { |
387 | if (model.draft) { | ||
388 | Kube.Fabric.postMessage(Kube.Messages.edit, {"mail": model.mail}) | ||
389 | } else { | ||
390 | Kube.Fabric.postMessage(Kube.Messages.reply, {"mail": model.mail}) | ||
391 | } | ||
388 | } | 392 | } |
389 | } | 393 | } |
394 | |||
395 | Kube.IconButton { | ||
396 | visible: !model.trash && !model.draft | ||
397 | activeFocusOnTab: false | ||
398 | |||
399 | iconName: Kube.Icons.forward | ||
400 | onClicked: { | ||
401 | Kube.Fabric.postMessage(Kube.Messages.forward, {"mail": model.mail}) | ||
402 | } | ||
403 | } | ||
404 | |||
390 | } | 405 | } |
406 | |||
391 | } | 407 | } |
392 | Rectangle { | 408 | Rectangle { |
393 | anchors.fill: parent | 409 | anchors.fill: parent |
diff --git a/framework/qml/Messages.qml b/framework/qml/Messages.qml index 18db51ff..9df83863 100644 --- a/framework/qml/Messages.qml +++ b/framework/qml/Messages.qml | |||
@@ -44,6 +44,7 @@ Item { | |||
44 | property string search: "search" | 44 | property string search: "search" |
45 | property string synchronize: "synchronize" | 45 | property string synchronize: "synchronize" |
46 | property string reply: "reply" | 46 | property string reply: "reply" |
47 | property string forward: "forward" | ||
47 | property string edit: "edit" | 48 | property string edit: "edit" |
48 | property string compose: "compose" | 49 | property string compose: "compose" |
49 | property string sendOutbox: "sendOutbox" | 50 | property string sendOutbox: "sendOutbox" |
diff --git a/framework/src/domain/composercontroller.cpp b/framework/src/domain/composercontroller.cpp index 60e955ea..4bfc34ab 100644 --- a/framework/src/domain/composercontroller.cpp +++ b/framework/src/domain/composercontroller.cpp | |||
@@ -272,14 +272,8 @@ static QStringList getStringListFromAddresses(const KMime::Types::Mailbox::List | |||
272 | void ComposerController::addAttachmentPart(KMime::Content *partToAttach) | 272 | void ComposerController::addAttachmentPart(KMime::Content *partToAttach) |
273 | { | 273 | { |
274 | QVariantMap map; | 274 | QVariantMap map; |
275 | if (partToAttach->contentType()->mimeType() == "multipart/digest" || | 275 | // May need special care for the multipart/digest MIME type |
276 | partToAttach->contentType()->mimeType() == "message/rfc822") { | 276 | map.insert("content", partToAttach->decodedContent()); |
277 | // if it is a digest or a full message, use the encodedContent() of the attachment, | ||
278 | // which already has the proper headers | ||
279 | map.insert("content", partToAttach->encodedContent()); | ||
280 | } else { | ||
281 | map.insert("content", partToAttach->decodedContent()); | ||
282 | } | ||
283 | map.insert("mimetype", partToAttach->contentType()->mimeType()); | 277 | map.insert("mimetype", partToAttach->contentType()->mimeType()); |
284 | 278 | ||
285 | QMimeDatabase db; | 279 | QMimeDatabase db; |
@@ -337,7 +331,40 @@ void ComposerController::setMessage(const KMime::Message::Ptr &msg) | |||
337 | setExistingMessage(msg); | 331 | setExistingMessage(msg); |
338 | } | 332 | } |
339 | 333 | ||
340 | void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | 334 | void ComposerController::loadDraft(const QVariant &message) { |
335 | loadMessage(message, [this] (const KMime::Message::Ptr &mail) { | ||
336 | mRemoveDraft = true; | ||
337 | setMessage(mail); | ||
338 | }); | ||
339 | } | ||
340 | |||
341 | void ComposerController::loadReply(const QVariant &message) { | ||
342 | loadMessage(message, [this] (const KMime::Message::Ptr &mail) { | ||
343 | //Find all personal email addresses to exclude from reply | ||
344 | KMime::Types::AddrSpecList me; | ||
345 | auto list = static_cast<IdentitySelector*>(mIdentitySelector.data())->getAllAddresses(); | ||
346 | for (const auto &a : list) { | ||
347 | KMime::Types::Mailbox mb; | ||
348 | mb.setAddress(a); | ||
349 | me << mb.addrSpec(); | ||
350 | } | ||
351 | |||
352 | MailTemplates::reply(mail, [this] (const KMime::Message::Ptr &reply) { | ||
353 | //We assume reply | ||
354 | setMessage(reply); | ||
355 | }, me); | ||
356 | }); | ||
357 | } | ||
358 | |||
359 | void ComposerController::loadForward(const QVariant &message) { | ||
360 | loadMessage(message, [this] (const KMime::Message::Ptr &mail) { | ||
361 | MailTemplates::forward(mail, [this] (const KMime::Message::Ptr &fwdMessage) { | ||
362 | setMessage(fwdMessage); | ||
363 | }); | ||
364 | }); | ||
365 | } | ||
366 | |||
367 | void ComposerController::loadMessage(const QVariant &message, std::function<void(const KMime::Message::Ptr&)> callback) | ||
341 | { | 368 | { |
342 | using namespace Sink; | 369 | using namespace Sink; |
343 | using namespace Sink::ApplicationDomain; | 370 | using namespace Sink::ApplicationDomain; |
@@ -346,8 +373,7 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | |||
346 | Q_ASSERT(msg); | 373 | Q_ASSERT(msg); |
347 | Query query(*msg); | 374 | Query query(*msg); |
348 | query.request<Mail::MimeMessage>(); | 375 | query.request<Mail::MimeMessage>(); |
349 | Store::fetchOne<Mail>(query).then([this, loadAsDraft](const Mail &mail) { | 376 | Store::fetchOne<Mail>(query).then([this, callback](const Mail &mail) { |
350 | mRemoveDraft = loadAsDraft; | ||
351 | setExistingMail(mail); | 377 | setExistingMail(mail); |
352 | 378 | ||
353 | const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); | 379 | const auto mailData = KMime::CRLFtoLF(mail.getMimeMessage()); |
@@ -355,23 +381,7 @@ void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) | |||
355 | KMime::Message::Ptr mail(new KMime::Message); | 381 | KMime::Message::Ptr mail(new KMime::Message); |
356 | mail->setContent(mailData); | 382 | mail->setContent(mailData); |
357 | mail->parse(); | 383 | mail->parse(); |
358 | if (loadAsDraft) { | 384 | callback(mail); |
359 | setMessage(mail); | ||
360 | } else { | ||
361 | //Find all personal email addresses to exclude from reply | ||
362 | KMime::Types::AddrSpecList me; | ||
363 | auto list = static_cast<IdentitySelector*>(mIdentitySelector.data())->getAllAddresses(); | ||
364 | for (const auto &a : list) { | ||
365 | KMime::Types::Mailbox mb; | ||
366 | mb.setAddress(a); | ||
367 | me << mb.addrSpec(); | ||
368 | } | ||
369 | |||
370 | MailTemplates::reply(mail, [this] (const KMime::Message::Ptr &reply) { | ||
371 | //We assume reply | ||
372 | setMessage(reply); | ||
373 | }, me); | ||
374 | } | ||
375 | } else { | 385 | } else { |
376 | qWarning() << "Retrieved empty message"; | 386 | qWarning() << "Retrieved empty message"; |
377 | } | 387 | } |
diff --git a/framework/src/domain/composercontroller.h b/framework/src/domain/composercontroller.h index 0ace365b..ac83dfd3 100644 --- a/framework/src/domain/composercontroller.h +++ b/framework/src/domain/composercontroller.h | |||
@@ -81,12 +81,21 @@ class ComposerController : public Kube::Controller | |||
81 | KUBE_CONTROLLER_ACTION(saveAsDraft) | 81 | KUBE_CONTROLLER_ACTION(saveAsDraft) |
82 | 82 | ||
83 | public: | 83 | public: |
84 | enum LoadType { | ||
85 | Draft, | ||
86 | Reply, | ||
87 | Forward, | ||
88 | }; | ||
89 | Q_ENUMS(LoadType); | ||
90 | |||
84 | explicit ComposerController(); | 91 | explicit ComposerController(); |
85 | 92 | ||
86 | Completer *recipientCompleter() const; | 93 | Completer *recipientCompleter() const; |
87 | Selector *identitySelector() const; | 94 | Selector *identitySelector() const; |
88 | 95 | ||
89 | Q_INVOKABLE void loadMessage(const QVariant &draft, bool loadAsDraft); | 96 | Q_INVOKABLE void loadDraft(const QVariant &message); |
97 | Q_INVOKABLE void loadReply(const QVariant &message); | ||
98 | Q_INVOKABLE void loadForward(const QVariant &message); | ||
90 | 99 | ||
91 | public slots: | 100 | public slots: |
92 | virtual void clear() Q_DECL_OVERRIDE; | 101 | virtual void clear() Q_DECL_OVERRIDE; |
@@ -95,6 +104,8 @@ private slots: | |||
95 | void findPersonalKey(); | 104 | void findPersonalKey(); |
96 | 105 | ||
97 | private: | 106 | private: |
107 | void loadMessage(const QVariant &message, std::function<void(const KMime::Message::Ptr&)> callback); | ||
108 | |||
98 | void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); | 109 | void recordForAutocompletion(const QByteArray &addrSpec, const QByteArray &displayName); |
99 | void setMessage(const QSharedPointer<KMime::Message> &msg); | 110 | void setMessage(const QSharedPointer<KMime::Message> &msg); |
100 | void addAttachmentPart(KMime::Content *partToAttach); | 111 | void addAttachmentPart(KMime::Content *partToAttach); |
diff --git a/framework/src/domain/mime/mailtemplates.cpp b/framework/src/domain/mime/mailtemplates.cpp index 9af15e91..8e644b34 100644 --- a/framework/src/domain/mime/mailtemplates.cpp +++ b/framework/src/domain/mime/mailtemplates.cpp | |||
@@ -864,6 +864,34 @@ void MailTemplates::reply(const KMime::Message::Ptr &origMsg, const std::functio | |||
864 | }); | 864 | }); |
865 | } | 865 | } |
866 | 866 | ||
867 | void MailTemplates::forward(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback) | ||
868 | { | ||
869 | KMime::Message::Ptr wrapperMsg(new KMime::Message); | ||
870 | |||
871 | wrapperMsg->to()->clear(); | ||
872 | wrapperMsg->cc()->clear(); | ||
873 | |||
874 | wrapperMsg->subject()->fromUnicodeString(forwardSubject(origMsg->subject()->asUnicodeString()), "utf-8"); | ||
875 | |||
876 | const QByteArray refStr = getRefStr(origMsg); | ||
877 | if (!refStr.isEmpty()) { | ||
878 | wrapperMsg->references()->fromUnicodeString(QString::fromLocal8Bit(refStr), "utf-8"); | ||
879 | } | ||
880 | |||
881 | KMime::Content* fwdAttachment = new KMime::Content; | ||
882 | |||
883 | fwdAttachment->contentDisposition()->setDisposition(KMime::Headers::CDinline); | ||
884 | fwdAttachment->contentType()->setMimeType("message/rfc822"); | ||
885 | fwdAttachment->contentDisposition()->setFilename(origMsg->subject()->asUnicodeString() + ".eml"); | ||
886 | // The mail was parsed in loadMessage before, so no need to assemble it | ||
887 | fwdAttachment->setBody(origMsg->encodedContent()); | ||
888 | |||
889 | wrapperMsg->addContent(fwdAttachment); | ||
890 | wrapperMsg->assemble(); | ||
891 | |||
892 | callback(wrapperMsg); | ||
893 | } | ||
894 | |||
867 | QString MailTemplates::plaintextContent(const KMime::Message::Ptr &msg) | 895 | QString MailTemplates::plaintextContent(const KMime::Message::Ptr &msg) |
868 | { | 896 | { |
869 | MimeTreeParser::ObjectTreeParser otp; | 897 | MimeTreeParser::ObjectTreeParser otp; |
@@ -899,10 +927,14 @@ static KMime::Content *createAttachmentPart(const QByteArray &content, const QSt | |||
899 | } else { | 927 | } else { |
900 | part->contentDisposition(true)->setDisposition(KMime::Headers::CDattachment); | 928 | part->contentDisposition(true)->setDisposition(KMime::Headers::CDattachment); |
901 | } | 929 | } |
930 | |||
902 | part->contentType(true)->setMimeType(mimeType); | 931 | part->contentType(true)->setMimeType(mimeType); |
903 | part->contentType(true)->setName(name, "utf-8"); | 932 | part->contentType(true)->setName(name, "utf-8"); |
904 | //Just always encode attachments base64 so it's safe for binary data | 933 | // Just always encode attachments base64 so it's safe for binary data, |
905 | part->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); | 934 | // except when it's another message |
935 | if(mimeType != "message/rfc822") { | ||
936 | part->contentTransferEncoding(true)->setEncoding(KMime::Headers::CEbase64); | ||
937 | } | ||
906 | part->setBody(content); | 938 | part->setBody(content); |
907 | return part; | 939 | return part; |
908 | } | 940 | } |
diff --git a/framework/src/domain/mime/mailtemplates.h b/framework/src/domain/mime/mailtemplates.h index 21efb5a0..9447e169 100644 --- a/framework/src/domain/mime/mailtemplates.h +++ b/framework/src/domain/mime/mailtemplates.h | |||
@@ -35,6 +35,7 @@ struct Attachment { | |||
35 | namespace MailTemplates | 35 | namespace MailTemplates |
36 | { | 36 | { |
37 | void reply(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback, const KMime::Types::AddrSpecList &me = {}); | 37 | void reply(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback, const KMime::Types::AddrSpecList &me = {}); |
38 | void forward(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback); | ||
38 | QString plaintextContent(const KMime::Message::Ptr &origMsg); | 39 | QString plaintextContent(const KMime::Message::Ptr &origMsg); |
39 | QString body(const KMime::Message::Ptr &msg, bool &isHtml); | 40 | QString body(const KMime::Message::Ptr &msg, bool &isHtml); |
40 | KMime::Message::Ptr createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, const QList<Attachment> &attachments, const std::vector<GpgME::Key> &signingKeys = {}, const std::vector<GpgME::Key> &encryptionKeys = {}); | 41 | KMime::Message::Ptr createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, const QList<Attachment> &attachments, const std::vector<GpgME::Key> &signingKeys = {}, const std::vector<GpgME::Key> &encryptionKeys = {}); |
diff --git a/framework/src/domain/mime/tests/mailtemplatetest.cpp b/framework/src/domain/mime/tests/mailtemplatetest.cpp index 302fae95..8d044608 100644 --- a/framework/src/domain/mime/tests/mailtemplatetest.cpp +++ b/framework/src/domain/mime/tests/mailtemplatetest.cpp | |||
@@ -224,6 +224,30 @@ private slots: | |||
224 | QCOMPARE(result->cc()->addresses(), l); | 224 | QCOMPARE(result->cc()->addresses(), l); |
225 | } | 225 | } |
226 | 226 | ||
227 | void testForwardAsAttachment() | ||
228 | { | ||
229 | auto msg = readMail("plaintext.mbox"); | ||
230 | KMime::Message::Ptr result; | ||
231 | MailTemplates::forward(msg, [&] (const KMime::Message::Ptr &r) { | ||
232 | result = r; | ||
233 | }); | ||
234 | QTRY_VERIFY(result); | ||
235 | QCOMPARE(result->subject(false)->asUnicodeString(), {"FW: A random subject with alternative contenttype"}); | ||
236 | QCOMPARE(result->to()->addresses(), {}); | ||
237 | QCOMPARE(result->cc()->addresses(), {}); | ||
238 | |||
239 | auto attachments = result->attachments(); | ||
240 | QCOMPARE(attachments.size(), 1); | ||
241 | auto attachment = attachments[0]; | ||
242 | QCOMPARE(attachment->contentDisposition(false)->disposition(), KMime::Headers::CDinline); | ||
243 | QCOMPARE(attachment->contentDisposition(false)->filename(), {"A random subject with alternative contenttype.eml"}); | ||
244 | QVERIFY(attachment->bodyIsMessage()); | ||
245 | |||
246 | attachment->parse(); | ||
247 | auto origMsg = attachment->bodyAsMessage(); | ||
248 | QCOMPARE(origMsg->subject(false)->asUnicodeString(), {"A random subject with alternative contenttype"}); | ||
249 | } | ||
250 | |||
227 | void testCreatePlainMail() | 251 | void testCreatePlainMail() |
228 | { | 252 | { |
229 | QStringList to = {{"to@example.org"}}; | 253 | QStringList to = {{"to@example.org"}}; |
diff --git a/icons/breeze/icons/actions/16/mail-forward-inverted.svg b/icons/breeze/icons/actions/16/mail-forward-inverted.svg new file mode 100644 index 00000000..b0819afd --- /dev/null +++ b/icons/breeze/icons/actions/16/mail-forward-inverted.svg | |||
@@ -0,0 +1,13 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> | ||
2 | <defs id="defs3051"> | ||
3 | <style type="text/css" id="current-color-scheme"> | ||
4 | .ColorScheme-Text { | ||
5 | color:#f2f2f2; | ||
6 | } | ||
7 | </style> | ||
8 | </defs> | ||
9 | <path style="fill:currentColor;fill-opacity:1;stroke:none" | ||
10 | d="M 8.484909,2 7.756539,2.7263772 11.995976,6.9542136 2,7 2,8 12.060363,7.9815758 7.756539,12.273622 8.484909,13 14,7.4999998 Z" | ||
11 | class="ColorScheme-Text" | ||
12 | /> | ||
13 | </svg> | ||
diff --git a/icons/breeze/icons/actions/16/mail-forward.svg b/icons/breeze/icons/actions/16/mail-forward.svg new file mode 100644 index 00000000..c46d7b78 --- /dev/null +++ b/icons/breeze/icons/actions/16/mail-forward.svg | |||
@@ -0,0 +1,13 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"> | ||
2 | <defs id="defs3051"> | ||
3 | <style type="text/css" id="current-color-scheme"> | ||
4 | .ColorScheme-Text { | ||
5 | color:#4d4d4d; | ||
6 | } | ||
7 | </style> | ||
8 | </defs> | ||
9 | <path style="fill:currentColor;fill-opacity:1;stroke:none" | ||
10 | d="M 8.484909,2 7.756539,2.7263772 11.995976,6.9542136 2,7 2,8 12.060363,7.9815758 7.756539,12.273622 8.484909,13 14,7.4999998 Z" | ||
11 | class="ColorScheme-Text" | ||
12 | /> | ||
13 | </svg> | ||
diff --git a/icons/breeze/icons/actions/22/mail-forward-inverted.svg b/icons/breeze/icons/actions/22/mail-forward-inverted.svg new file mode 100644 index 00000000..2284ec32 --- /dev/null +++ b/icons/breeze/icons/actions/22/mail-forward-inverted.svg | |||
@@ -0,0 +1,14 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"> | ||
2 | <defs id="defs3051"> | ||
3 | <style type="text/css" id="current-color-scheme"> | ||
4 | .ColorScheme-Text { | ||
5 | color:#f2f2f2; | ||
6 | } | ||
7 | </style> | ||
8 | </defs> | ||
9 | <path | ||
10 | style="fill:currentColor;fill-opacity:1;stroke:none" | ||
11 | d="M 1 3 L 1 4 L 1 12 L 1 18 L 1 18.414062 L 1 19 L 2 19 L 8 19 L 14 19 L 14 18 L 8 18 L 2 18 L 2 17.414062 L 2.4628906 16.951172 L 8.7304688 10.683594 L 10.183594 12.134766 L 10.998047 12.949219 L 11 12.949219 L 11.816406 12.134766 L 13.269531 10.683594 L 14.585938 12 L 16 12 L 13.976562 9.9765625 L 19.728516 4.2226562 L 19.951172 4 L 20 4 L 20 4.2226562 L 20 12 L 21 12 L 21 3 L 20.951172 3 L 20 3 L 19.537109 3 L 2.4628906 3 L 2 3 L 1 3 z M 2 4 L 2.0488281 4 L 2.2714844 4.2226562 L 8.0234375 9.9765625 L 2 16 L 2 12 L 2 4.2226562 L 2 4 z M 3.4628906 4 L 18.537109 4 L 11 11.537109 L 3.4628906 4 z M 17.707031 12 L 17 12.707031 L 19.292969 15 L 15 15 L 15 16 L 19.292969 16 L 17 18.292969 L 17.707031 19 L 21 15.707031 L 21.207031 15.5 L 21 15.292969 L 17.707031 12 z " | ||
12 | class="ColorScheme-Text" | ||
13 | /> | ||
14 | </svg> | ||
diff --git a/icons/breeze/icons/actions/22/mail-forward.svg b/icons/breeze/icons/actions/22/mail-forward.svg new file mode 100644 index 00000000..7b060b2a --- /dev/null +++ b/icons/breeze/icons/actions/22/mail-forward.svg | |||
@@ -0,0 +1,14 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22"> | ||
2 | <defs id="defs3051"> | ||
3 | <style type="text/css" id="current-color-scheme"> | ||
4 | .ColorScheme-Text { | ||
5 | color:#4d4d4d; | ||
6 | } | ||
7 | </style> | ||
8 | </defs> | ||
9 | <path | ||
10 | style="fill:currentColor;fill-opacity:1;stroke:none" | ||
11 | d="M 1 3 L 1 4 L 1 12 L 1 18 L 1 18.414062 L 1 19 L 2 19 L 8 19 L 14 19 L 14 18 L 8 18 L 2 18 L 2 17.414062 L 2.4628906 16.951172 L 8.7304688 10.683594 L 10.183594 12.134766 L 10.998047 12.949219 L 11 12.949219 L 11.816406 12.134766 L 13.269531 10.683594 L 14.585938 12 L 16 12 L 13.976562 9.9765625 L 19.728516 4.2226562 L 19.951172 4 L 20 4 L 20 4.2226562 L 20 12 L 21 12 L 21 3 L 20.951172 3 L 20 3 L 19.537109 3 L 2.4628906 3 L 2 3 L 1 3 z M 2 4 L 2.0488281 4 L 2.2714844 4.2226562 L 8.0234375 9.9765625 L 2 16 L 2 12 L 2 4.2226562 L 2 4 z M 3.4628906 4 L 18.537109 4 L 11 11.537109 L 3.4628906 4 z M 17.707031 12 L 17 12.707031 L 19.292969 15 L 15 15 L 15 16 L 19.292969 16 L 17 18.292969 L 17.707031 19 L 21 15.707031 L 21.207031 15.5 L 21 15.292969 L 17.707031 12 z " | ||
12 | class="ColorScheme-Text" | ||
13 | /> | ||
14 | </svg> | ||
diff --git a/icons/breeze/icons/actions/24/mail-forward-inverted.svg b/icons/breeze/icons/actions/24/mail-forward-inverted.svg new file mode 100644 index 00000000..cb6c3817 --- /dev/null +++ b/icons/breeze/icons/actions/24/mail-forward-inverted.svg | |||
@@ -0,0 +1,13 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | ||
2 | <defs id="defs3051"> | ||
3 | <style type="text/css" id="current-color-scheme"> | ||
4 | .ColorScheme-Text { | ||
5 | color:#f2f2f2; | ||
6 | } | ||
7 | </style> | ||
8 | </defs> | ||
9 | <path style="fill:currentColor;fill-opacity:1;stroke:none" | ||
10 | d="M 2 4 L 2 5 L 2 13 L 2 19 L 2 19.414062 L 2 20 L 3 20 L 9 20 L 15 20 L 15 19 L 9 19 L 3 19 L 3 18.414062 L 3.4628906 17.951172 L 9.7304688 11.683594 L 11.183594 13.134766 L 11.998047 13.949219 L 12 13.949219 L 12.816406 13.134766 L 14.269531 11.683594 L 15.585938 13 L 17 13 L 14.976562 10.976562 L 20.728516 5.2226562 L 20.951172 5 L 21 5 L 21 5.2226562 L 21 13 L 22 13 L 22 4 L 21.951172 4 L 21 4 L 20.537109 4 L 3.4628906 4 L 3 4 L 2 4 z M 3 5 L 3.0488281 5 L 3.2714844 5.2226562 L 9.0234375 10.976562 L 3 17 L 3 13 L 3 5.2226562 L 3 5 z M 4.4628906 5 L 19.537109 5 L 12 12.537109 L 4.4628906 5 z M 18.707031 13 L 18 13.707031 L 20.292969 16 L 16 16 L 16 17 L 20.292969 17 L 18 19.292969 L 18.707031 20 L 22 16.707031 L 22.207031 16.5 L 22 16.292969 L 18.707031 13 z " | ||
11 | class="ColorScheme-Text" | ||
12 | /> | ||
13 | </svg> | ||
diff --git a/icons/breeze/icons/actions/24/mail-forward.svg b/icons/breeze/icons/actions/24/mail-forward.svg new file mode 100644 index 00000000..c92455d9 --- /dev/null +++ b/icons/breeze/icons/actions/24/mail-forward.svg | |||
@@ -0,0 +1,13 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> | ||
2 | <defs id="defs3051"> | ||
3 | <style type="text/css" id="current-color-scheme"> | ||
4 | .ColorScheme-Text { | ||
5 | color:#4d4d4d; | ||
6 | } | ||
7 | </style> | ||
8 | </defs> | ||
9 | <path style="fill:currentColor;fill-opacity:1;stroke:none" | ||
10 | d="M 2 4 L 2 5 L 2 13 L 2 19 L 2 19.414062 L 2 20 L 3 20 L 9 20 L 15 20 L 15 19 L 9 19 L 3 19 L 3 18.414062 L 3.4628906 17.951172 L 9.7304688 11.683594 L 11.183594 13.134766 L 11.998047 13.949219 L 12 13.949219 L 12.816406 13.134766 L 14.269531 11.683594 L 15.585938 13 L 17 13 L 14.976562 10.976562 L 20.728516 5.2226562 L 20.951172 5 L 21 5 L 21 5.2226562 L 21 13 L 22 13 L 22 4 L 21.951172 4 L 21 4 L 20.537109 4 L 3.4628906 4 L 3 4 L 2 4 z M 3 5 L 3.0488281 5 L 3.2714844 5.2226562 L 9.0234375 10.976562 L 3 17 L 3 13 L 3 5.2226562 L 3 5 z M 4.4628906 5 L 19.537109 5 L 12 12.537109 L 4.4628906 5 z M 18.707031 13 L 18 13.707031 L 20.292969 16 L 16 16 L 16 17 L 20.292969 17 L 18 19.292969 L 18.707031 20 L 22 16.707031 L 22.207031 16.5 L 22 16.292969 L 18.707031 13 z " | ||
11 | class="ColorScheme-Text" | ||
12 | /> | ||
13 | </svg> | ||
diff --git a/icons/breeze/icons/actions/32/mail-forward-inverted.svg b/icons/breeze/icons/actions/32/mail-forward-inverted.svg new file mode 100644 index 00000000..55d4e937 --- /dev/null +++ b/icons/breeze/icons/actions/32/mail-forward-inverted.svg | |||
@@ -0,0 +1,14 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> | ||
2 | <defs id="defs3051"> | ||
3 | <style type="text/css" id="current-color-scheme"> | ||
4 | .ColorScheme-Text { | ||
5 | color:#f2f2f2; | ||
6 | } | ||
7 | </style> | ||
8 | </defs> | ||
9 | <path | ||
10 | style="fill:currentColor;fill-opacity:1;stroke:none" | ||
11 | d="M 2 5 L 2 24.683594 L 2 27 L 20 27 L 20 26 L 3 26 L 3 25.111328 L 3.4296875 24.683594 L 12.785156 15.541016 L 16 18.683594 L 19.214844 15.541016 L 21 17.285156 L 21 15.888672 L 19.929688 14.841797 L 28.976562 6 L 29 6 L 29 14 L 30 14 L 30 5 L 2 5 z M 3 6 L 3.0234375 6 L 12.070312 14.841797 L 3 23.707031 L 3 6 z M 4.453125 6 L 27.546875 6 L 16 17.285156 L 4.453125 6 z M 25.484375 16 L 24.755859 16.726562 L 28.996094 20.955078 L 20 21 L 20 22 L 29.060547 21.982422 L 24.755859 26.273438 L 25.484375 27 L 31 21.5 L 25.484375 16 z " | ||
12 | class="ColorScheme-Text" | ||
13 | /> | ||
14 | </svg> | ||
diff --git a/icons/breeze/icons/actions/32/mail-forward.svg b/icons/breeze/icons/actions/32/mail-forward.svg new file mode 100644 index 00000000..2c7a7a4c --- /dev/null +++ b/icons/breeze/icons/actions/32/mail-forward.svg | |||
@@ -0,0 +1,14 @@ | |||
1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> | ||
2 | <defs id="defs3051"> | ||
3 | <style type="text/css" id="current-color-scheme"> | ||
4 | .ColorScheme-Text { | ||
5 | color:#4d4d4d; | ||
6 | } | ||
7 | </style> | ||
8 | </defs> | ||
9 | <path | ||
10 | style="fill:currentColor;fill-opacity:1;stroke:none" | ||
11 | d="M 2 5 L 2 24.683594 L 2 27 L 20 27 L 20 26 L 3 26 L 3 25.111328 L 3.4296875 24.683594 L 12.785156 15.541016 L 16 18.683594 L 19.214844 15.541016 L 21 17.285156 L 21 15.888672 L 19.929688 14.841797 L 28.976562 6 L 29 6 L 29 14 L 30 14 L 30 5 L 2 5 z M 3 6 L 3.0234375 6 L 12.070312 14.841797 L 3 23.707031 L 3 6 z M 4.453125 6 L 27.546875 6 L 16 17.285156 L 4.453125 6 z M 25.484375 16 L 24.755859 16.726562 L 28.996094 20.955078 L 20 21 L 20 22 L 29.060547 21.982422 L 24.755859 26.273438 L 25.484375 27 L 31 21.5 L 25.484375 16 z " | ||
12 | class="ColorScheme-Text" | ||
13 | /> | ||
14 | </svg> | ||
diff --git a/icons/copybreeze.sh b/icons/copybreeze.sh index 86e3f019..693a40c8 100755 --- a/icons/copybreeze.sh +++ b/icons/copybreeze.sh | |||
@@ -24,6 +24,7 @@ wantedIcons = [ | |||
24 | "mail-mark-important.svg", | 24 | "mail-mark-important.svg", |
25 | "mail-mark-unread-new.svg", | 25 | "mail-mark-unread-new.svg", |
26 | "mail-reply-sender.svg", | 26 | "mail-reply-sender.svg", |
27 | "mail-forward.svg", | ||
27 | "mail-folder-outbox.svg", | 28 | "mail-folder-outbox.svg", |
28 | "network-disconnect.svg", | 29 | "network-disconnect.svg", |
29 | "view-refresh.svg", | 30 | "view-refresh.svg", |
diff --git a/views/composer/qml/View.qml b/views/composer/qml/View.qml index 4086f715..1ea67513 100644 --- a/views/composer/qml/View.qml +++ b/views/composer/qml/View.qml | |||
@@ -30,7 +30,7 @@ Kube.View { | |||
30 | id: root | 30 | id: root |
31 | 31 | ||
32 | property bool newMessage: false | 32 | property bool newMessage: false |
33 | property bool loadAsDraft: false | 33 | property int loadType: Kube.ComposerController.Draft |
34 | property variant message: {} | 34 | property variant message: {} |
35 | property variant recipients: [] | 35 | property variant recipients: [] |
36 | 36 | ||
@@ -58,11 +58,21 @@ Kube.View { | |||
58 | 58 | ||
59 | function loadMessage(message, loadAsDraft) { | 59 | function loadMessage(message, loadAsDraft) { |
60 | if (message) { | 60 | if (message) { |
61 | composerController.loadMessage(message, loadAsDraft) | 61 | |
62 | //Forward focus for replies directly | 62 | switch(loadType) { |
63 | if (!loadAsDraft) { | 63 | case Kube.ComposerController.Draft: |
64 | subject.forceActiveFocus() | 64 | composerController.loadDraft(message) |
65 | break; | ||
66 | case Kube.ComposerController.Reply: | ||
67 | composerController.loadReply(message) | ||
68 | subject.forceActiveFocus() | ||
69 | break; | ||
70 | case Kube.ComposerController.Forward: | ||
71 | composerController.loadForward(message) | ||
72 | subject.forceActiveFocus() | ||
73 | break; | ||
65 | } | 74 | } |
75 | |||
66 | } else if (newMessage) { | 76 | } else if (newMessage) { |
67 | composerController.clear() | 77 | composerController.clear() |
68 | if (root.recipients) { | 78 | if (root.recipients) { |