summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/kube/qml/Kube.qml9
-rw-r--r--framework/qml/Icons.qml1
-rw-r--r--framework/qml/MailViewer.qml36
-rw-r--r--framework/qml/Messages.qml1
-rw-r--r--framework/src/domain/composercontroller.cpp66
-rw-r--r--framework/src/domain/composercontroller.h13
-rw-r--r--framework/src/domain/mime/mailtemplates.cpp36
-rw-r--r--framework/src/domain/mime/mailtemplates.h1
-rw-r--r--framework/src/domain/mime/tests/mailtemplatetest.cpp24
-rw-r--r--icons/breeze/icons/actions/16/mail-forward-inverted.svg13
-rw-r--r--icons/breeze/icons/actions/16/mail-forward.svg13
-rw-r--r--icons/breeze/icons/actions/22/mail-forward-inverted.svg14
-rw-r--r--icons/breeze/icons/actions/22/mail-forward.svg14
-rw-r--r--icons/breeze/icons/actions/24/mail-forward-inverted.svg13
-rw-r--r--icons/breeze/icons/actions/24/mail-forward.svg13
-rw-r--r--icons/breeze/icons/actions/32/mail-forward-inverted.svg14
-rw-r--r--icons/breeze/icons/actions/32/mail-forward.svg14
-rwxr-xr-xicons/copybreeze.sh1
-rw-r--r--views/composer/qml/View.qml20
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
272void ComposerController::addAttachmentPart(KMime::Content *partToAttach) 272void 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
340void ComposerController::loadMessage(const QVariant &message, bool loadAsDraft) 334void ComposerController::loadDraft(const QVariant &message) {
335 loadMessage(message, [this] (const KMime::Message::Ptr &mail) {
336 mRemoveDraft = true;
337 setMessage(mail);
338 });
339}
340
341void 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
359void 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
367void 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
83public: 83public:
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
91public slots: 100public 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
97private: 106private:
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
867void 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
867QString MailTemplates::plaintextContent(const KMime::Message::Ptr &msg) 895QString 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 {
35namespace MailTemplates 35namespace 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) {