summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/mailviewer/contents/ui/MailDataModel.qml19
-rw-r--r--components/mailviewer/contents/ui/MailPart.qml41
-rw-r--r--components/mailviewer/contents/ui/MailViewer.qml1
-rw-r--r--components/mailviewer/contents/ui/TextContent.qml9
-rw-r--r--framework/qml/MailViewer.qml1
-rw-r--r--framework/src/domain/mime/mimetreeparser/messagepart.cpp15
-rw-r--r--framework/src/domain/mime/mimetreeparser/messagepart.h2
-rw-r--r--framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp38
-rw-r--r--framework/src/domain/mime/mimetreeparser/objecttreeparser.h1
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp18
-rw-r--r--framework/src/domain/mime/mimetreeparser/textplain.cpp7
-rw-r--r--framework/src/domain/mime/partmodel.cpp74
-rw-r--r--framework/src/domain/mime/partmodel.h4
13 files changed, 176 insertions, 54 deletions
diff --git a/components/mailviewer/contents/ui/MailDataModel.qml b/components/mailviewer/contents/ui/MailDataModel.qml
index 46427194..0f64972c 100644
--- a/components/mailviewer/contents/ui/MailDataModel.qml
+++ b/components/mailviewer/contents/ui/MailDataModel.qml
@@ -21,8 +21,7 @@ import QtQml.Models 2.2
21import org.kube.framework 1.0 as Kube 21import org.kube.framework 1.0 as Kube
22 22
23DelegateModel { 23DelegateModel {
24 id: mailDataModel 24 id: root
25 property bool debug: true
26 25
27 delegate: Item { 26 delegate: Item {
28 id: partColumn 27 id: partColumn
@@ -65,13 +64,13 @@ DelegateModel {
65 partLoader.setSource("TextContent.qml", 64 partLoader.setSource("TextContent.qml",
66 {"content": model.content, 65 {"content": model.content,
67 "embedded": model.embeded, 66 "embedded": model.embeded,
68 "type": model.type, 67 "type": model.type
69 "debug": debug}) 68 })
70 break 69 break
71 case "html": 70 case "html":
72 partLoader.setSource("HtmlContent.qml", 71 partLoader.setSource("HtmlContent.qml",
73 {"content": model.content, 72 {"content": model.content,
74 "debug": debug}) 73 })
75 break; 74 break;
76 case "error": 75 case "error":
77 partLoader.setSource("ErrorPart.qml", 76 partLoader.setSource("ErrorPart.qml",
@@ -80,11 +79,13 @@ DelegateModel {
80 "errorString": model.errorString, 79 "errorString": model.errorString,
81 }) 80 })
82 break; 81 break;
83 case "envelope": 82 case "encapsulated":
84 partLoader.setSource("MailPart.qml", 83 partLoader.setSource("MailPart.qml",
85 {"rootIndex": mailDataModel.modelIndex(index), 84 {"rootIndex": root.modelIndex(index),
86 "model": mailDataModel.model, 85 "model": root.model,
87 "debug": debug}) 86 "sender": model.sender,
87 "date": model.date
88 })
88 break; 89 break;
89 } 90 }
90 } 91 }
diff --git a/components/mailviewer/contents/ui/MailPart.qml b/components/mailviewer/contents/ui/MailPart.qml
index bf534e85..7ff426e9 100644
--- a/components/mailviewer/contents/ui/MailPart.qml
+++ b/components/mailviewer/contents/ui/MailPart.qml
@@ -18,29 +18,54 @@
18 18
19import QtQuick 2.4 19import QtQuick 2.4
20 20
21import org.kube.framework 1.0 as Kube
22
21Item { 23Item {
22 id: root 24 id: root
23 property alias rootIndex: visualModel.rootIndex 25 property alias rootIndex: visualModel.rootIndex
24 property alias model: visualModel.model 26 property alias model: visualModel.model
25 property alias debug: visualModel.debug 27 property variant sender
26 height: partListView.height + 10 28 property variant date
27 width: parent.width 29 height: childrenRect.height
28 30
29 MailDataModel { 31 MailDataModel {
30 id: visualModel 32 id: visualModel
31 } 33 }
32 34
35 Rectangle {
36 id: border
37 anchors {
38 top: parent.top
39 left: parent.left
40 leftMargin: Kube.Units.smallSpacing
41 }
42 color: "lightgrey"
43 height: partListView.height
44 width: Kube.Units.smallSpacing
45 }
46
47 Text {
48 id: sender
49 anchors {
50 left: border.right
51 leftMargin: Kube.Units.smallSpacing
52 }
53
54 text: "sent by " + root.sender + " on " + root.date
55 color: "grey"
56 }
33 ListView { 57 ListView {
34 id: partListView 58 id: partListView
35 model: visualModel 59 model: visualModel
36 anchors { 60 anchors {
37 top: parent.top 61 top: sender.bottom
38 left: parent.left 62 left: border.right
39 margins: 5 63 margins: Kube.Units.smallSpacing
64 leftMargin: Kube.Units.smallSpacing
40 } 65 }
41 spacing: 5 66 spacing: 7
42 height: contentHeight 67 height: contentHeight
43 width: parent.width - 10 68 width: parent.width - Kube.Units.smallSpacing * 3
44 interactive: false 69 interactive: false
45 } 70 }
46} 71}
diff --git a/components/mailviewer/contents/ui/MailViewer.qml b/components/mailviewer/contents/ui/MailViewer.qml
index 23307009..9031ec17 100644
--- a/components/mailviewer/contents/ui/MailViewer.qml
+++ b/components/mailviewer/contents/ui/MailViewer.qml
@@ -22,7 +22,6 @@ Item {
22 id: root 22 id: root
23 property alias rootIndex: visualModel.rootIndex 23 property alias rootIndex: visualModel.rootIndex
24 property alias model: visualModel.model 24 property alias model: visualModel.model
25 property alias debug: visualModel.debug
26 height: partListView.height 25 height: partListView.height
27 26
28 MailDataModel { 27 MailDataModel {
diff --git a/components/mailviewer/contents/ui/TextContent.qml b/components/mailviewer/contents/ui/TextContent.qml
index f8ef7f9a..a0094a3e 100644
--- a/components/mailviewer/contents/ui/TextContent.qml
+++ b/components/mailviewer/contents/ui/TextContent.qml
@@ -24,7 +24,6 @@ Item {
24 id: root 24 id: root
25 25
26 property string content 26 property string content
27 property bool debug: true
28 property bool embedded: true 27 property bool embedded: true
29 property string type 28 property string type
30 29
@@ -54,13 +53,5 @@ Item {
54 color: embedded ? Kube.Colors.disabledTextColor : Kube.Colors.textColor 53 color: embedded ? Kube.Colors.disabledTextColor : Kube.Colors.textColor
55 onLinkActivated: Qt.openUrlExternally(link) 54 onLinkActivated: Qt.openUrlExternally(link)
56 } 55 }
57
58 //BEGIN debug
59 Text {
60 width: parent.width
61 visible: root.debug
62 text: type
63 }
64 //END debug
65 } 56 }
66} 57}
diff --git a/framework/qml/MailViewer.qml b/framework/qml/MailViewer.qml
index 9e14fc7f..cd170f8f 100644
--- a/framework/qml/MailViewer.qml
+++ b/framework/qml/MailViewer.qml
@@ -318,7 +318,6 @@ Rectangle {
318 anchors.left: body.left 318 anchors.left: body.left
319 anchors.right: body.right 319 anchors.right: body.right
320 model: messageParser.parts 320 model: messageParser.parts
321 debug: false
322 } 321 }
323 322
324 } 323 }
diff --git a/framework/src/domain/mime/mimetreeparser/messagepart.cpp b/framework/src/domain/mime/mimetreeparser/messagepart.cpp
index 00abb003..df4dfeac 100644
--- a/framework/src/domain/mime/mimetreeparser/messagepart.cpp
+++ b/framework/src/domain/mime/mimetreeparser/messagepart.cpp
@@ -1292,3 +1292,18 @@ QString EncapsulatedRfc822MessagePart::text() const
1292 return renderInternalText(); 1292 return renderInternalText();
1293} 1293}
1294 1294
1295QString EncapsulatedRfc822MessagePart::from() const
1296{
1297 if (auto from = mMessage->from(false)) {
1298 return from->asUnicodeString();
1299 }
1300 return {};
1301}
1302
1303QDateTime EncapsulatedRfc822MessagePart::date() const
1304{
1305 if (auto date = mMessage->date(false)) {
1306 return date->dateTime();
1307 }
1308 return {};
1309}
diff --git a/framework/src/domain/mime/mimetreeparser/messagepart.h b/framework/src/domain/mime/mimetreeparser/messagepart.h
index dd3a842f..7d266ac5 100644
--- a/framework/src/domain/mime/mimetreeparser/messagepart.h
+++ b/framework/src/domain/mime/mimetreeparser/messagepart.h
@@ -281,6 +281,8 @@ public:
281 virtual ~EncapsulatedRfc822MessagePart(); 281 virtual ~EncapsulatedRfc822MessagePart();
282 282
283 QString text() const Q_DECL_OVERRIDE; 283 QString text() const Q_DECL_OVERRIDE;
284 QString from() const;
285 QDateTime date() const;
284private: 286private:
285 const KMime::Message::Ptr mMessage; 287 const KMime::Message::Ptr mMessage;
286 288
diff --git a/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp b/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp
index b137be87..8d8ddf12 100644
--- a/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp
+++ b/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp
@@ -190,20 +190,17 @@ KMime::Content *ObjectTreeParser::find(const std::function<bool(KMime::Content *
190 * Filter to avoid evaluating a subtree. 190 * Filter to avoid evaluating a subtree.
191 * Select parts to include it in the result set. Selecting a part in a branch will keep any parent parts from being selected. 191 * Select parts to include it in the result set. Selecting a part in a branch will keep any parent parts from being selected.
192 */ 192 */
193static QVector<MessagePart::Ptr> collect(MessagePart::Ptr start, const std::function<bool(const MessagePartPtr &)> &filter, const std::function<bool(const MessagePartPtr &)> &select) 193static QVector<MessagePart::Ptr> collect(MessagePart::Ptr start, const std::function<bool(const MessagePartPtr &)> &evaluateSubtree, const std::function<bool(const MessagePartPtr &)> &select)
194{ 194{
195 MessagePartPtr ptr = start.dynamicCast<MessagePart>(); 195 MessagePartPtr ptr = start.dynamicCast<MessagePart>();
196 Q_ASSERT(ptr); 196 Q_ASSERT(ptr);
197 if (!filter(ptr)) {
198 return {};
199 }
200
201 QVector<MessagePart::Ptr> list; 197 QVector<MessagePart::Ptr> list;
202 if (ptr) { 198 if (evaluateSubtree(ptr)) {
203 for (const auto &p: ptr->subParts()) { 199 for (const auto &p: ptr->subParts()) {
204 list << ::collect(p, filter, select); 200 list << ::collect(p, evaluateSubtree, select);
205 } 201 }
206 } 202 }
203
207 //Don't consider this part if we already selected a subpart 204 //Don't consider this part if we already selected a subpart
208 if (list.isEmpty()) { 205 if (list.isEmpty()) {
209 if (select(ptr)) { 206 if (select(ptr)) {
@@ -213,14 +210,25 @@ static QVector<MessagePart::Ptr> collect(MessagePart::Ptr start, const std::func
213 return list; 210 return list;
214} 211}
215 212
216QVector<MessagePart::Ptr> ObjectTreeParser::collectContentParts() 213QVector<MessagePartPtr> ObjectTreeParser::collectContentParts()
217{ 214{
218 QVector<MessagePart::Ptr> contentParts = ::collect(mParsedPart, 215 return collectContentParts(mParsedPart);
219 [] (const MessagePartPtr &part) { 216}
220 // return p->type() != "EncapsulatedPart"; 217
218QVector<MessagePart::Ptr> ObjectTreeParser::collectContentParts(MessagePart::Ptr start)
219{
220 return ::collect(start,
221 [start] (const MessagePartPtr &part) {
222 //Ignore the top-level
223 if (start.data() == part.data()) {
224 return true;
225 }
226 if (auto e = part.dynamicCast<MimeTreeParser::EncapsulatedRfc822MessagePart>()) {
227 return false;
228 }
221 return true; 229 return true;
222 }, 230 },
223 [] (const MessagePartPtr &part) { 231 [start] (const MessagePartPtr &part) {
224 if (dynamic_cast<MimeTreeParser::AttachmentMessagePart*>(part.data())) { 232 if (dynamic_cast<MimeTreeParser::AttachmentMessagePart*>(part.data())) {
225 return false; 233 return false;
226 } else if (const auto text = dynamic_cast<MimeTreeParser::TextMessagePart*>(part.data())) { 234 } else if (const auto text = dynamic_cast<MimeTreeParser::TextMessagePart*>(part.data())) {
@@ -233,6 +241,11 @@ QVector<MessagePart::Ptr> ObjectTreeParser::collectContentParts()
233 return true; 241 return true;
234 } else if (dynamic_cast<MimeTreeParser::HtmlMessagePart*>(part.data())) { 242 } else if (dynamic_cast<MimeTreeParser::HtmlMessagePart*>(part.data())) {
235 return true; 243 return true;
244 } else if (dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart*>(part.data())) {
245 if (start.data() == part.data()) {
246 return false;
247 }
248 return true;
236 } else if (const auto enc = dynamic_cast<MimeTreeParser::EncryptedMessagePart*>(part.data())) { 249 } else if (const auto enc = dynamic_cast<MimeTreeParser::EncryptedMessagePart*>(part.data())) {
237 if (enc->error()) { 250 if (enc->error()) {
238 return true; 251 return true;
@@ -247,7 +260,6 @@ QVector<MessagePart::Ptr> ObjectTreeParser::collectContentParts()
247 } 260 }
248 return false; 261 return false;
249 }); 262 });
250 return contentParts;
251} 263}
252 264
253QVector<MessagePart::Ptr> ObjectTreeParser::collectAttachmentParts() 265QVector<MessagePart::Ptr> ObjectTreeParser::collectAttachmentParts()
diff --git a/framework/src/domain/mime/mimetreeparser/objecttreeparser.h b/framework/src/domain/mime/mimetreeparser/objecttreeparser.h
index 398cbcd7..d219a52b 100644
--- a/framework/src/domain/mime/mimetreeparser/objecttreeparser.h
+++ b/framework/src/domain/mime/mimetreeparser/objecttreeparser.h
@@ -285,6 +285,7 @@ public:
285 MessagePartPtr parsedPart() const; 285 MessagePartPtr parsedPart() const;
286 KMime::Content *find(const std::function<bool(KMime::Content *)> &select); 286 KMime::Content *find(const std::function<bool(KMime::Content *)> &select);
287 QVector<MessagePartPtr> collectContentParts(); 287 QVector<MessagePartPtr> collectContentParts();
288 QVector<MessagePartPtr> collectContentParts(MessagePart::Ptr start);
288 QVector<MessagePartPtr> collectAttachmentParts(); 289 QVector<MessagePartPtr> collectAttachmentParts();
289 void decryptParts(); 290 void decryptParts();
290 void importCertificates(); 291 void importCertificates();
diff --git a/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp b/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp
index f9b557c9..b9fdd356 100644
--- a/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp
+++ b/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp
@@ -319,6 +319,24 @@ private slots:
319 QVERIFY(bool(part)); 319 QVERIFY(bool(part));
320 QVERIFY(part->error()); 320 QVERIFY(part->error());
321 } 321 }
322
323 void testEncapsulated()
324 {
325 MimeTreeParser::ObjectTreeParser otp;
326 otp.parseObjectTree(readMailFromFile("encapsulated-with-attachment.mbox"));
327 otp.decryptParts();
328 auto partList = otp.collectContentParts();
329 QCOMPARE(partList.size(), 2);
330 auto part = partList[1].dynamicCast<MimeTreeParser::EncapsulatedRfc822MessagePart>();
331 QVERIFY(bool(part));
332 QCOMPARE(part->from(), QLatin1String("Thomas McGuire <dontspamme@gmx.net>"));
333 QCOMPARE(part->date().toString(), QLatin1String("Wed Aug 5 10:57:58 2009 GMT+0200"));
334 auto subPartList = otp.collectContentParts(part);
335 QCOMPARE(subPartList.size(), 1);
336 qWarning() << subPartList[0]->metaObject()->className();
337 auto subPart = subPartList[0].dynamicCast<MimeTreeParser::TextMessagePart>();
338 QVERIFY(bool(subPart));
339 }
322}; 340};
323 341
324QTEST_GUILESS_MAIN(InterfaceTest) 342QTEST_GUILESS_MAIN(InterfaceTest)
diff --git a/framework/src/domain/mime/mimetreeparser/textplain.cpp b/framework/src/domain/mime/mimetreeparser/textplain.cpp
index 32201a8a..d62cb2cf 100644
--- a/framework/src/domain/mime/mimetreeparser/textplain.cpp
+++ b/framework/src/domain/mime/mimetreeparser/textplain.cpp
@@ -41,13 +41,12 @@ const Interface::BodyPartFormatter *TextPlainBodyPartFormatter::create()
41MessagePart::Ptr TextPlainBodyPartFormatter::process(Interface::BodyPart &part) const 41MessagePart::Ptr TextPlainBodyPartFormatter::process(Interface::BodyPart &part) const
42{ 42{
43 KMime::Content *node = part.content(); 43 KMime::Content *node = part.content();
44 const bool isFirstTextPart = (node->topLevel()->textContent() == node);
45 44
46 TextMessagePart::Ptr mp; 45 TextMessagePart::Ptr mp;
47 if (isFirstTextPart) { 46 if (KMime::isAttachment(node)) {
48 mp = TextMessagePart::Ptr(new TextMessagePart(part.objectTreeParser(), node)); 47 mp = AttachmentMessagePart::Ptr(new AttachmentMessagePart(part.objectTreeParser(), node));
49 } else { 48 } else {
50 mp = TextMessagePart::Ptr(new AttachmentMessagePart(part.objectTreeParser(), node)); 49 mp = TextMessagePart::Ptr(new TextMessagePart(part.objectTreeParser(), node));
51 } 50 }
52 51
53 return mp; 52 return mp;
diff --git a/framework/src/domain/mime/partmodel.cpp b/framework/src/domain/mime/partmodel.cpp
index ea3e90e1..6ee3d46f 100644
--- a/framework/src/domain/mime/partmodel.cpp
+++ b/framework/src/domain/mime/partmodel.cpp
@@ -35,6 +35,8 @@ public:
35 void createTree(); 35 void createTree();
36 PartModel *q; 36 PartModel *q;
37 QVector<MimeTreeParser::MessagePartPtr> mParts; 37 QVector<MimeTreeParser::MessagePartPtr> mParts;
38 QHash<MimeTreeParser::MessagePart*, QVector<MimeTreeParser::MessagePartPtr>> mEncapsulatedParts;
39 QHash<MimeTreeParser::MessagePart*, MimeTreeParser::MessagePart*> mParents;
38 std::shared_ptr<MimeTreeParser::ObjectTreeParser> mParser; 40 std::shared_ptr<MimeTreeParser::ObjectTreeParser> mParser;
39}; 41};
40 42
@@ -44,6 +46,14 @@ PartModelPrivate::PartModelPrivate(PartModel *q_ptr, const std::shared_ptr<MimeT
44{ 46{
45 mParts = mParser->collectContentParts(); 47 mParts = mParser->collectContentParts();
46 qWarning() << "Collected content parts: " << mParts.size(); 48 qWarning() << "Collected content parts: " << mParts.size();
49 for (auto p : mParts) {
50 if (auto e = p.dynamicCast<MimeTreeParser::EncapsulatedRfc822MessagePart>()) {
51 mEncapsulatedParts[e.data()] = mParser->collectContentParts(e);
52 for (auto subPart : mEncapsulatedParts[e.data()]) {
53 mParents[subPart.data()] = e.data();
54 }
55 }
56 }
47} 57}
48 58
49PartModelPrivate::~PartModelPrivate() 59PartModelPrivate::~PartModelPrivate()
@@ -71,6 +81,8 @@ QHash<int, QByteArray> PartModel::roleNames() const
71 roles[EncryptionErrorType] = "errorType"; 81 roles[EncryptionErrorType] = "errorType";
72 roles[EncryptionErrorString] = "errorString"; 82 roles[EncryptionErrorString] = "errorString";
73 roles[IsErrorRole] = "error"; 83 roles[IsErrorRole] = "error";
84 roles[SenderRole] = "sender";
85 roles[DateRole] = "date";
74 return roles; 86 return roles;
75} 87}
76 88
@@ -79,6 +91,15 @@ QModelIndex PartModel::index(int row, int column, const QModelIndex &parent) con
79 if (row < 0 || column != 0) { 91 if (row < 0 || column != 0) {
80 return QModelIndex(); 92 return QModelIndex();
81 } 93 }
94 if (parent.isValid()) {
95 if (auto e = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart*>(static_cast<MimeTreeParser::MessagePart*>(parent.internalPointer()))) {
96 const auto parts = d->mEncapsulatedParts[e];
97 if (row < parts.size()) {
98 return createIndex(row, column, parts.at(row).data());
99 }
100 }
101 return QModelIndex();
102 }
82 if (row < d->mParts.size()) { 103 if (row < d->mParts.size()) {
83 return createIndex(row, column, d->mParts.at(row).data()); 104 return createIndex(row, column, d->mParts.at(row).data());
84 } 105 }
@@ -88,26 +109,35 @@ QModelIndex PartModel::index(int row, int column, const QModelIndex &parent) con
88QVariant PartModel::data(const QModelIndex &index, int role) const 109QVariant PartModel::data(const QModelIndex &index, int role) const
89{ 110{
90 if (!index.isValid()) { 111 if (!index.isValid()) {
91 switch (role) {
92 case Qt::DisplayRole:
93 return QString("root");
94 case IsEmbededRole:
95 return false;
96 }
97 return QVariant(); 112 return QVariant();
98 } 113 }
99 114
100 if (index.internalPointer()) { 115 if (index.internalPointer()) {
101 const auto messagePart = static_cast<MimeTreeParser::MessagePart*>(index.internalPointer()); 116 const auto messagePart = static_cast<MimeTreeParser::MessagePart*>(index.internalPointer());
102 qWarning() << "Found message part " << messagePart->metaObject()->className() << messagePart->partMetaData()->status << messagePart->error(); 117 // qWarning() << "Found message part " << messagePart->metaObject()->className() << messagePart->partMetaData()->status << messagePart->error();
103 Q_ASSERT(messagePart); 118 Q_ASSERT(messagePart);
104 switch(role) { 119 switch(role) {
105 case Qt::DisplayRole: 120 case Qt::DisplayRole:
106 return QStringLiteral("Content%1"); 121 return QStringLiteral("Content%1");
122 case SenderRole: {
123 if (auto e = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart*>(messagePart)) {
124 return e->from();
125 }
126 return {};
127 }
128 case DateRole: {
129 if (auto e = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart*>(messagePart)) {
130 return e->date();
131 }
132 return {};
133 }
107 case TypeRole: { 134 case TypeRole: {
108 if (messagePart->error()) { 135 if (messagePart->error()) {
109 return "error"; 136 return "error";
110 } 137 }
138 if (dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart*>(messagePart)) {
139 return "encapsulated";
140 }
111 //For simple html we don't need a browser 141 //For simple html we don't need a browser
112 auto complexHtml = [&] { 142 auto complexHtml = [&] {
113 if (messagePart->isHtml()) { 143 if (messagePart->isHtml()) {
@@ -165,11 +195,39 @@ QVariant PartModel::data(const QModelIndex &index, int role) const
165 195
166QModelIndex PartModel::parent(const QModelIndex &index) const 196QModelIndex PartModel::parent(const QModelIndex &index) const
167{ 197{
168 return QModelIndex(); 198 if (index.isValid()) {
199 if (auto e = static_cast<MimeTreeParser::MessagePart*>(index.internalPointer())) {
200 for (const auto &p : d->mParts) {
201 if (p.data() == e) {
202 return QModelIndex();
203 }
204 }
205 const auto parentPart = d->mParents[e];
206 Q_ASSERT(parentPart);
207 int row = 0;
208 const auto parts = d->mEncapsulatedParts[parentPart];
209 for (const auto &p : parts) {
210 if (p.data() == e) {
211 break;
212 }
213 row++;
214 }
215 return createIndex(row, 0, parentPart);
216 }
217 return {};
218 }
219 return {};
169} 220}
170 221
171int PartModel::rowCount(const QModelIndex &parent) const 222int PartModel::rowCount(const QModelIndex &parent) const
172{ 223{
224 if (parent.isValid()) {
225 if (auto e = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart*>(static_cast<MimeTreeParser::MessagePart*>(parent.internalPointer()))) {
226 const auto parts = d->mEncapsulatedParts[e];
227 return parts.size();
228 }
229 return 0;
230 }
173 return d->mParts.count(); 231 return d->mParts.count();
174} 232}
175 233
diff --git a/framework/src/domain/mime/partmodel.h b/framework/src/domain/mime/partmodel.h
index efbd154b..a0e14b61 100644
--- a/framework/src/domain/mime/partmodel.h
+++ b/framework/src/domain/mime/partmodel.h
@@ -47,7 +47,9 @@ public:
47 IsErrorRole, 47 IsErrorRole,
48 SecurityLevelRole, 48 SecurityLevelRole,
49 EncryptionErrorType, 49 EncryptionErrorType,
50 EncryptionErrorString 50 EncryptionErrorString,
51 SenderRole,
52 DateRole
51 }; 53 };
52 54
53 QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE; 55 QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;