1 /* Copyright (C) 2014 - 2015 Stephan Platz <trojita@paalsteek.de>
2 Copyright (C) 2006 - 2016 Jan Kundrát <jkt@kde.org>
3
4 This file is part of the Trojita Qt IMAP e-mail client,
5 http://trojita.flaska.net/
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of
10 the License or (at your option) version 3 or any later version
11 accepted by the membership of KDE e.V. (or its successor approved
12 by the membership of KDE e.V.), which shall act as a proxy
13 defined in Section 14 of version 3 of the license.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "Cryptography/MessageModel.h"
25 #include "Cryptography/MessagePart.h"
26 #include "Cryptography/PartReplacer.h"
27 #include "Common/MetaTypes.h"
28 #include "Imap/Model/ItemRoles.h"
29 #include "Imap/Model/MailboxTree.h"
30
31 namespace Cryptography {
32
MessageModel(QObject * parent,const QModelIndex & message)33 MessageModel::MessageModel(QObject *parent, const QModelIndex &message)
34 : QAbstractItemModel(parent)
35 , m_message(message)
36 , m_rootPart(std::move(new TopLevelMessage(m_message, this)))
37 {
38 Q_ASSERT(m_message.isValid());
39 connect(m_message.model(), &QAbstractItemModel::dataChanged, this, &MessageModel::mapDataChanged);
40 }
41
mapDataChanged(const QModelIndex & topLeft,const QModelIndex & bottomRight)42 void MessageModel::mapDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
43 {
44 if (!m_message.isValid()) {
45 return;
46 }
47
48 Q_ASSERT(m_message.model());
49 Q_ASSERT(topLeft.parent() == bottomRight.parent());
50
51 QModelIndex root = index(0,0);
52 if (!root.isValid())
53 return;
54
55 auto topLeftIt = m_map.constFind(topLeft);
56 auto bottomRightIt = m_map.constFind(bottomRight);
57 if (topLeftIt != m_map.constEnd() && bottomRightIt != m_map.constEnd()) {
58 emit dataChanged(
59 createIndex(topLeft.row(), topLeft.column(), *topLeftIt),
60 createIndex(bottomRight.row(), bottomRight.column(), *bottomRightIt)
61 );
62 }
63 }
64
index(int row,int column,const QModelIndex & parent) const65 QModelIndex MessageModel::index(int row, int column, const QModelIndex &parent) const
66 {
67 Q_ASSERT(!parent.isValid() || parent.model() == this);
68 auto parentPart = translatePtr(parent);
69 Q_ASSERT(parentPart);
70 auto childPtr = parentPart->child(const_cast<MessageModel *>(this), row, column);
71 if (!childPtr) {
72 return QModelIndex();
73 } else {
74 return createIndex(row, column, childPtr);
75 }
76 }
77
parent(const QModelIndex & child) const78 QModelIndex MessageModel::parent(const QModelIndex &child) const
79 {
80 if (!child.isValid())
81 return QModelIndex();
82 Q_ASSERT(child.model() == this);
83 auto childPart = translatePtr(child);
84 Q_ASSERT(childPart);
85 auto parentPart = childPart->parent();
86 return (parentPart && parentPart != m_rootPart.get()) ? createIndex(parentPart->row(), 0, parentPart) : QModelIndex();
87 }
88
data(const QModelIndex & index,int role) const89 QVariant MessageModel::data(const QModelIndex &index, int role) const
90 {
91 if (!index.isValid()) {
92 return QVariant();
93 }
94
95 Q_ASSERT(index.model() == this);
96
97 return translatePtr(index)->data(role);
98 }
99
message() const100 QModelIndex MessageModel::message() const
101 {
102 return m_message;
103 }
104
insertSubtree(const QModelIndex & parent,MessagePart::Ptr tree)105 void MessageModel::insertSubtree(const QModelIndex &parent, MessagePart::Ptr tree)
106 {
107 std::vector<MessagePart::Ptr> parts;
108 parts.emplace_back(std::move(tree));
109 insertSubtree(parent, std::move(parts));
110 }
111
insertSubtree(const QModelIndex & parent,std::vector<Cryptography::MessagePart::Ptr> && parts)112 void MessageModel::insertSubtree(const QModelIndex &parent, std::vector<Cryptography::MessagePart::Ptr> &&parts)
113 {
114 Q_ASSERT(!parent.isValid() || parent.model() == this);
115 auto *part = translatePtr(parent);
116 Q_ASSERT(part);
117
118 #ifdef MIME_TREE_DEBUG
119 qDebug() << "Whole tree:\n" << *m_rootPart;
120 qDebug() << "Inserting at:\n" << (void*)(part);
121 qDebug() << "New items:\n" << *(tree.get());
122 #endif
123
124 beginInsertRows(parent, 0, parts.size());
125
126 Q_ASSERT(part->m_children.empty());
127 for (size_t i = 0; i < parts.size(); ++i) {
128 parts[i]->m_parent = part;
129 part->m_children[i] = std::move(parts[i]);
130 }
131
132 bool needsDataChanged = false;
133 if (LocalMessagePart *localPart = dynamic_cast<LocalMessagePart*>(part)) {
134 localPart->m_localState = MessagePart::FetchingState::DONE;
135 needsDataChanged = true;
136 }
137
138 endInsertRows();
139
140 if (needsDataChanged) {
141 emit dataChanged(parent, parent);
142 }
143 }
144
replaceMeWithSubtree(const QModelIndex & parent,MessagePart * partToReplace,MessagePart::Ptr tree)145 void MessageModel::replaceMeWithSubtree(const QModelIndex &parent, MessagePart *partToReplace, MessagePart::Ptr tree)
146 {
147 Q_ASSERT(!parent.isValid() || parent.model() == this);
148 auto *part = translatePtr(parent);
149 Q_ASSERT(part);
150
151 #ifdef MIME_TREE_DEBUG
152 qDebug() << "Whole tree:\n" << *m_rootPart;
153 qDebug() << "Replacing:\n" << (void*)(partToReplace);
154 qDebug() << "New items:\n" << *(tree.get());
155 #endif
156
157 Q_ASSERT(partToReplace);
158 Q_ASSERT(tree->row() == partToReplace->row());
159
160 auto it = part->m_children.find(partToReplace->row());
161 Q_ASSERT(it != part->m_children.end());
162 Q_ASSERT(it->second->row() == partToReplace->row());
163 Q_ASSERT(partToReplace->row() == tree->row());
164
165 emit layoutAboutToBeChanged(QList<QPersistentModelIndex>() << parent);
166
167 auto oldIndexes = persistentIndexList();
168 auto newIndexes = oldIndexes;
169 for (int i = 0; i < oldIndexes.size(); ++i) {
170 const auto &index = oldIndexes[i];
171 if (index.parent() == parent && index.column() == 0 && index.row() == tree->row()) {
172 newIndexes[i] = createIndex(tree->row(), 0, tree.get());
173 }
174 }
175 changePersistentIndexList(oldIndexes, newIndexes);
176
177 tree->m_parent = part;
178 MessagePart::Ptr partGoingAway = std::move(it->second);
179 it->second = std::move(tree);
180 emit layoutChanged(QList<QPersistentModelIndex>() << parent);
181
182 emit dataChanged(parent, parent);
183
184 #ifdef MIME_TREE_DEBUG
185 qDebug() << "After replacement:\n" << *m_rootPart;
186 #endif
187 }
188
189 /** @short Get the underlying MessagePart * for the passed index */
translatePtr(const QModelIndex & part) const190 MessagePart *MessageModel::translatePtr(const QModelIndex &part) const
191 {
192 if (!part.isValid())
193 return m_rootPart.get();
194 return static_cast<MessagePart *>(part.internalPointer());
195 }
196
rowCount(const QModelIndex & parent) const197 int MessageModel::rowCount(const QModelIndex &parent) const
198 {
199 return translatePtr(parent)->rowCount(const_cast<MessageModel *>(this));
200 }
201
columnCount(const QModelIndex & parent) const202 int MessageModel::columnCount(const QModelIndex &parent) const
203 {
204 return translatePtr(parent)->columnCount(const_cast<MessageModel *>(this));
205 }
206
registerPartHandler(std::shared_ptr<PartReplacer> module)207 void MessageModel::registerPartHandler(std::shared_ptr<PartReplacer> module)
208 {
209 m_partHandlers.push_back(module);
210 }
211
212 #ifdef MIME_TREE_DEBUG
debugDump()213 void MessageModel::debugDump()
214 {
215 qDebug() << "MIME tree:\n" << *m_rootPart;
216 }
217 #endif
218
219 }
220