Messagelib

mimetreemodel.cpp
1 /*
2  SPDX-FileCopyrightText: 2007, 2008 Volker Krause <[email protected]>
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 
25 using namespace MessageViewer;
26 
27 class Q_DECL_HIDDEN MimeTreeModel::MimeTreeModelPrivate
28 {
29 public:
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(QLatin1String(ct->mimeType()));
96 
97  auto mimeType = m_mimeDb.mimeTypeForName(QString::fromLatin1(ct->mimeType()));
98  if (!mimeType.isValid() || mimeType.name() == QLatin1String("application/octet-stream")) {
99  const QString name = descriptionForContent(content);
100  mimeType = MimeTreeParser::Util::mimetype(name);
101  }
102 
103  if (mimeType.isValid() && mimeType.name().startsWith(QLatin1String("multipart/"))) {
104  return QIcon::fromTheme(QStringLiteral("folder"));
105  } else if (!iconName.isEmpty() && iconName != QLatin1String("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  QVector<QTemporaryDir *> tempDirs;
118  KMime::Content *root = nullptr;
119  QMimeDatabase m_mimeDb;
120 };
121 
122 MimeTreeModel::MimeTreeModel(QObject *parent)
123  : QAbstractItemModel(parent)
124  , d(new MimeTreeModelPrivate)
125 {
126 }
127 
128 MimeTreeModel::~MimeTreeModel() = default;
129 
130 void MimeTreeModel::setRoot(KMime::Content *root)
131 {
132  if (d->root != root) {
133  beginResetModel();
134  d->clearTempDir();
135  d->root = root;
136  endResetModel();
137  }
138 }
139 
140 KMime::Content *MimeTreeModel::root()
141 {
142  return d->root;
143 }
144 
145 QModelIndex 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 
186 int 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 
201 int MimeTreeModel::columnCount(const QModelIndex &parent) const
202 {
203  Q_UNUSED(parent)
204  return 3;
205 }
206 
207 QVariant 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 
257 QVariant 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 
272 QMimeData *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 
313 Qt::ItemFlags MimeTreeModel::flags(const QModelIndex &index) const
314 {
315  Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
316  return Qt::ItemIsDragEnabled | defaultFlags;
317 }
318 
319 QStringList MimeTreeModel::mimeTypes() const
320 {
321  const QStringList types = {QStringLiteral("text/uri-list")};
322  return types;
323 }
void append(const T &value)
QByteArray body() const
ToolTipRole
static QString fileName(const KMime::Content *node)
Returns a usable filename for a node, that can be the filename from the content disposition header,...
KIOCORE_EXPORT QString convertSize(KIO::filesize_t size)
QVariant fromValue(const T &value)
void * internalPointer() const const
QByteArray mimeType() const
void setUrls(const QList< QUrl > &urls)
int column() const const
QIcon fromTheme(const QString &name)
bool isValid() const
KCALUTILS_EXPORT QString mimeType()
QStringList types(Mode mode=Writing)
QModelIndex createIndex(int row, int column, void *ptr) const const
typedef ItemFlags
QString i18n(const char *text, const TYPE &arg...)
Orientation
bool isEmpty() const const
QUrl fromLocalFile(const QString &localFile)
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
unsigned int up()
bool isValid() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QByteArray decodedContent()
A model representing the mime part tree of a message.
Definition: mimetreemodel.h:21
bool isEmpty() const const
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const const
QString fromLatin1(const char *str, int size)
QString name(StandardShortcut id)
int size() const const
int length() const const
Headers::ContentType * contentType(bool create=true)
Content * mainBodyPart(const QByteArray &type=QByteArray())
QObject * parent() const const
QString message
Headers::ContentDescription * contentDescription(bool create=true)
QVector< Content * > contents() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Fri Mar 24 2023 04:08:31 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.