Messagelib

mimetreemodel.cpp
1/*
2 SPDX-FileCopyrightText: 2007, 2008 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "mimetreemodel.h"
8#include "messageviewer_debug.h"
9
10#include <MimeTreeParser/NodeHelper>
11#include <MimeTreeParser/Util>
12
13#include <KMime/Content>
14#include <KMime/Message>
15
16#include <KIO/Global>
17#include <KLocalizedString>
18
19#include <QIcon>
20#include <QMimeData>
21#include <QMimeDatabase>
22#include <QTemporaryDir>
23#include <QUrl>
24
25using namespace MessageViewer;
26
27class Q_DECL_HIDDEN MimeTreeModel::MimeTreeModelPrivate
28{
29public:
30 MimeTreeModelPrivate() = default;
31
32 ~MimeTreeModelPrivate()
33 {
34 qDeleteAll(tempDirs);
35 }
36
37 void clearTempDir()
38 {
39 qDeleteAll(tempDirs);
40 tempDirs.clear();
41 }
42
43 QString descriptionForContent(KMime::Content *content)
44 {
45 auto const message = dynamic_cast<KMime::Message *>(content);
46 if (message && message->subject(false)) {
47 return message->subject(false)->asUnicodeString();
48 }
50 if (!name.isEmpty()) {
51 return name;
52 }
53 if (auto ct = content->contentDescription(false)) {
54 const QString desc = ct->asUnicodeString();
55 if (!desc.isEmpty()) {
56 return desc;
57 }
58 }
59 return i18n("body part");
60 }
61
62 QString mimeTypeForContent(KMime::Content *content)
63 {
64 if (auto ct = content->contentType(false)) {
65 return QString::fromLatin1(ct->mimeType());
66 }
67 return {};
68 }
69
70 QString typeForContent(KMime::Content *content)
71 {
72 if (auto ct = content->contentType(false)) {
73 const QString contentMimeType = QString::fromLatin1(ct->mimeType());
74 const auto mimeType = m_mimeDb.mimeTypeForName(contentMimeType);
75 if (!mimeType.isValid()) {
76 return contentMimeType;
77 }
78 return mimeType.comment();
79 } else {
80 return {};
81 }
82 }
83
84 QString sizeOfContent(KMime::Content *content)
85 {
86 if (content->body().isEmpty()) {
87 return {};
88 }
89 return KIO::convertSize(content->body().size());
90 }
91
92 QIcon iconForContent(KMime::Content *content)
93 {
94 if (auto ct = content->contentType(false)) {
95 auto iconName = MimeTreeParser::Util::iconNameForMimetype(QLatin1StringView(ct->mimeType()));
96
97 auto mimeType = m_mimeDb.mimeTypeForName(QString::fromLatin1(ct->mimeType()));
98 if (!mimeType.isValid() || mimeType.name() == QLatin1StringView("application/octet-stream")) {
99 const QString name = descriptionForContent(content);
100 mimeType = MimeTreeParser::Util::mimetype(name);
101 }
102
103 if (mimeType.isValid() && mimeType.name().startsWith(QLatin1StringView("multipart/"))) {
104 return QIcon::fromTheme(QStringLiteral("folder"));
105 } else if (!iconName.isEmpty() && iconName != QLatin1StringView("unknown")) {
106 return QIcon::fromTheme(iconName);
107 } else if (mimeType.isValid() && !mimeType.iconName().isEmpty()) {
108 return QIcon::fromTheme(mimeType.iconName());
109 }
110
111 return {};
112 } else {
113 return {};
114 }
115 }
116
117 QList<QTemporaryDir *> tempDirs;
118 KMime::Content *root = nullptr;
119 QMimeDatabase m_mimeDb;
120};
121
122MimeTreeModel::MimeTreeModel(QObject *parent)
123 : QAbstractItemModel(parent)
124 , d(new MimeTreeModelPrivate)
125{
126}
127
128MimeTreeModel::~MimeTreeModel() = default;
129
130void MimeTreeModel::setRoot(KMime::Content *root)
131{
132 if (d->root != root) {
134 d->clearTempDir();
135 d->root = root;
137 }
138}
139
140KMime::Content *MimeTreeModel::root()
141{
142 return d->root;
143}
144
145QModelIndex MimeTreeModel::index(int row, int column, const QModelIndex &parent) const
146{
147 if (!parent.isValid()) {
148 if (row != 0) {
149 return {};
150 }
151 return createIndex(row, column, d->root);
152 }
153
154 auto parentContent = static_cast<KMime::Content *>(parent.internalPointer());
155 if (!parentContent || parentContent->contents().count() <= row || row < 0) {
156 return {};
157 }
158 KMime::Content *content = parentContent->contents().at(row);
159 return createIndex(row, column, content);
160}
161
163{
164 if (!index.isValid()) {
165 return {};
166 }
167 auto currentContent = static_cast<KMime::Content *>(index.internalPointer());
168 if (!currentContent) {
169 return {};
170 }
171
172 KMime::ContentIndex currentIndex = d->root->indexForContent(currentContent);
173 if (!currentIndex.isValid()) {
174 return {};
175 }
176 currentIndex.up();
177 KMime::Content *parentContent = d->root->content(currentIndex);
178 int row = 0;
179 if (currentIndex.isValid()) {
180 row = currentIndex.up() - 1; // 1 based -> 0 based
181 }
182
183 return createIndex(row, 0, parentContent);
184}
185
186int MimeTreeModel::rowCount(const QModelIndex &parent) const
187{
188 if (!d->root) {
189 return 0;
190 }
191 if (!parent.isValid()) {
192 return 1;
193 }
194 auto parentContent = static_cast<KMime::Content *>(parent.internalPointer());
195 if (parentContent) {
196 return parentContent->contents().count();
197 }
198 return 0;
199}
200
201int MimeTreeModel::columnCount(const QModelIndex &parent) const
202{
204 return 3;
205}
206
207QVariant MimeTreeModel::data(const QModelIndex &index, int role) const
208{
209 auto content = static_cast<KMime::Content *>(index.internalPointer());
210 if (!content) {
211 return {};
212 }
213 if (role == Qt::ToolTipRole) {
214 // TODO
215 // return d->root->indexForContent( content ).toString();
216 return {};
217 }
218 if (role == Qt::DisplayRole) {
219 switch (index.column()) {
220 case 0:
221 return d->descriptionForContent(content);
222 case 1:
223 return d->typeForContent(content);
224 case 2:
225 return d->sizeOfContent(content);
226 }
227 }
228 if (role == Qt::DecorationRole && index.column() == 0) {
229 return d->iconForContent(content);
230 }
231 if (role == ContentIndexRole) {
232 return QVariant::fromValue(d->root->indexForContent(content));
233 }
234 if (role == ContentRole) {
235 return QVariant::fromValue(content);
236 }
237 if (role == MimeTypeRole) {
238 return d->mimeTypeForContent(content);
239 }
240 if (role == MainBodyPartRole) {
241 auto topLevelMsg = dynamic_cast<KMime::Message *>(d->root);
242 if (!topLevelMsg) {
243 return false;
244 }
245 return topLevelMsg->mainBodyPart() == content;
246 }
247 if (role == AlternativeBodyPartRole) {
248 auto topLevelMsg = dynamic_cast<KMime::Message *>(d->root);
249 if (!topLevelMsg) {
250 return false;
251 }
252 return topLevelMsg->mainBodyPart(content->contentType()->mimeType()) == content;
253 }
254 return {};
255}
256
257QVariant MimeTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
258{
259 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
260 switch (section) {
261 case 0:
262 return i18n("Description");
263 case 1:
264 return i18n("Type");
265 case 2:
266 return i18n("Size");
267 }
268 }
269 return QAbstractItemModel::headerData(section, orientation, role);
270}
271
272QMimeData *MimeTreeModel::mimeData(const QModelIndexList &indexes) const
273{
274 QList<QUrl> urls;
275 for (const QModelIndex &index : indexes) {
276 // create dnd for one item not for all three columns.
277 if (index.column() != 0) {
278 continue;
279 }
280 auto content = static_cast<KMime::Content *>(index.internalPointer());
281 if (!content) {
282 continue;
283 }
284 const QByteArray data = content->decodedContent();
285 if (data.isEmpty()) {
286 continue;
287 }
288
289 auto tempDir = new QTemporaryDir; // Will remove the directory on destruction.
290 d->tempDirs.append(tempDir);
291 const QString fileName = tempDir->path() + QLatin1Char('/') + d->descriptionForContent(content);
292 QFile f(fileName);
293 if (!f.open(QIODevice::WriteOnly)) {
294 qCWarning(MESSAGEVIEWER_LOG) << "Cannot write attachment:" << f.errorString();
295 continue;
296 }
297 if (f.write(data) != data.length()) {
298 qCWarning(MESSAGEVIEWER_LOG) << "Failed to write all data to file!";
299 continue;
300 }
301 f.close();
302
303 const QUrl url = QUrl::fromLocalFile(fileName);
304 qCDebug(MESSAGEVIEWER_LOG) << " temporary file " << url;
305 urls.append(url);
306 }
307
308 auto mimeData = new QMimeData;
309 mimeData->setUrls(urls);
310 return mimeData;
311}
312
313Qt::ItemFlags MimeTreeModel::flags(const QModelIndex &index) const
314{
317}
318
319QStringList MimeTreeModel::mimeTypes() const
320{
321 const QStringList types = {QStringLiteral("text/uri-list")};
322 return types;
323}
324
325#include "moc_mimetreemodel.cpp"
bool isValid() const
unsigned int up()
Headers::ContentDescription * contentDescription(bool create=true)
Headers::ContentType * contentType(bool create=true)
QByteArray decodedContent()
QByteArray body() const
QByteArray mimeType() const
A model representing the mime part tree of a message.
static QString fileName(const KMime::Content *node)
Returns a usable filename for a node, that can be the filename from the content disposition header,...
QString i18n(const char *text, const TYPE &arg...)
KCALUTILS_EXPORT QString mimeType()
KIOCORE_EXPORT QString convertSize(KIO::filesize_t size)
QString name(StandardShortcut id)
QModelIndex createIndex(int row, int column, const void *ptr) const const
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const const
bool isEmpty() const const
qsizetype length() const const
qsizetype size() const const
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
void setUrls(const QList< QUrl > &urls)
int column() const const
void * internalPointer() const const
bool isValid() const const
QObject * parent() const const
T qobject_cast(QObject *object)
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
ToolTipRole
typedef ItemFlags
Orientation
QUrl fromLocalFile(const QString &localFile)
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:12:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.