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