• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi/kmime

  • sources
  • kde-4.14
  • kdepimlibs
  • akonadi
  • kmime
messagethreaderproxymodel.cpp
1 /*
2  Copyright (c) 2007 Bruno Virlet <bruno.virlet@gmail.com>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "messagethreaderproxymodel.h"
21 #include "messagethreadingattribute.h"
22 #include "messagemodel.h"
23 
24 #include <akonadi/attributefactory.h>
25 #include <akonadi/itemfetchjob.h>
26 #include <akonadi/itemfetchscope.h>
27 
28 #include <QtCore/QDebug>
29 #include <QtCore/QString>
30 #include <QtCore/QStringList>
31 #include <QtCore/QHash>
32 #include <QtCore/QTime>
33 #include <QtCore/QModelIndex>
34 
35 using namespace Akonadi;
36 
37 class MessageThreaderProxyModel::Private
38 {
39 public:
40  Private(MessageThreaderProxyModel *parent)
41  : mParent(parent)
42  {
43  }
44 
45  MessageModel *sourceMessageModel()
46  {
47  return dynamic_cast<MessageModel *>(mParent->sourceModel());
48  }
49 
50  /*
51  * Reset everything
52  */
53  void slotCollectionChanged()
54  {
55  childrenMap.clear();
56  indexMap.clear();
57  parentMap.clear();
58  realPerfectParentsMap.clear();
59  realUnperfectParentsMap.clear();
60  realSubjectParentsMap.clear();
61 
62  realPerfectChildrenMap.clear();
63  realUnperfectChildrenMap.clear();
64  realSubjectChildrenMap.clear();
65 
66  mParent->reset();
67  }
68 
69  /*
70  * Function called when the signal rowsInserted was triggered in the
71  * source model.
72  */
73  void slotInsertRows(const QModelIndex &sourceIndex, int begin, int end)
74  {
75  Q_UNUSED(sourceIndex); // parent source index is always invalid (flat source model)
76  QTime time;
77  time.start();
78 
79  for (int i = begin; i <= end; i++) {
80  // Retrieve the item from the source model
81  Item item = sourceMessageModel()->itemForIndex(sourceMessageModel()->index(i, 0));
82  Entity::Id id = item.id();
83  // Get his best potential parent using the mail threader parts
84  readParentsFromParts(item);
85  Entity::Id parentId = parentForItem(item.id());
86 
87  /*
88  * Fill in the tree maps
89  */
90  int row = childrenMap[parentId].count();
91  mParent->beginInsertRows(indexMap[parentId], row, row);
92  childrenMap[parentId] << item.id();
93  parentMap[id] = parentId;
94  QModelIndex index = mParent->createIndex(childrenMap[parentId].count() - 1, 0, id);
95  mParent->endInsertRows();
96 
97  /*
98  * Look for potential children into real children map
99  */
100  QList<Entity::Id> potentialChildren = realPerfectChildrenMap[id]
101  << realUnperfectChildrenMap[id]
102  << realSubjectChildrenMap[id];
103  foreach (Entity::Id potentialChildId, potentialChildren) {
104  // This item can be a child of our item if:
105  // - it's not the item itself (could we do that check when building the 'real' maps ?)
106  // - his parent is set
107  // - and this parent is not already our item
108  if (potentialChildId != id &&
109  parentMap.constFind(potentialChildId) != parentMap.constEnd() &&
110  parentMap[potentialChildId] != id &&
111  parentMap[potentialChildId]) {
112  // Check that the current parent of this item is not better than ours
113  QList<Entity::Id> realParentsList = realPerfectParentsMap[potentialChildId]
114  << realUnperfectParentsMap[potentialChildId]
115  << realSubjectParentsMap[potentialChildId];
116  int currentParentPos = realParentsList.indexOf(parentMap[potentialChildId]);
117  // currentParentPos = 0 is probably the more common case so we may avoid an indexOf.
118  if (currentParentPos == 0 || (currentParentPos != -1 && realParentsList.indexOf(id) > currentParentPos)) {
119  // (currentParentPos can be -1 if parent is root)
120  continue;
121  }
122 
123  // Remove the children from the old location
124  int childRow = childrenMap[parentMap[potentialChildId]].indexOf(potentialChildId);
125  mParent->beginRemoveRows(indexMap[parentMap[potentialChildId]], childRow, childRow);
126  mParent->endRemoveRows();
127  childrenMap[parentMap[potentialChildId]].removeAt(childRow);
128 
129  // Change the tree info
130  mParent->beginInsertRows(index, childrenMap[id].count(), childrenMap[id].count());
131  parentMap[potentialChildId] = id;
132  childrenMap[id] << potentialChildId;
133 
134  // Recreate index because row change
135  mParent->createIndex(childrenMap[id].count() - 1, 0, potentialChildId);
136  mParent->endInsertRows();
137  }
138  }
139  }
140 
141  qDebug() << time.elapsed() << "ms for" << end - begin + 1 << "items";
142  }
143 
144  /*
145  * Function called when the signal rowsAboutToBeRemoved is sent by the source model
146  * (source model indexes are *still* valid)
147  */
148  void slotRemoveRows(const QModelIndex &sourceIndex, int begin, int end)
149  {
150  Q_UNUSED(sourceIndex);
151  for (int i = begin; i <= end; i++) {
152  Item item = sourceMessageModel()->itemForIndex(sourceMessageModel()->index(i, 0));
153  Entity::Id id = item.id();
154  Entity::Id parentId = parentMap[id];
155  int row = childrenMap[parentId].indexOf(id);
156 
157  // Reparent the children to the closest parent
158  foreach (Entity::Id childId, childrenMap[id]) {
159  int childRow = childrenMap[id].indexOf(childId);
160  mParent->beginRemoveRows(indexMap[id], childRow, childRow);
161  childrenMap[id].removeAll(childId); // There is only one ...
162  mParent->endRemoveRows();
163 
164  mParent->beginInsertRows(indexMap[parentId], childrenMap[parentId].count(),
165  childrenMap[parentId].count());
166  parentMap[childId] = parentId;
167  childrenMap[parentId] << childId;
168  mParent->endInsertRows();
169 
170  mParent->createIndex(childrenMap[parentId].count() - 1, 0, childId); // Is it necessary to recreate the index ?
171  }
172 
173  mParent->beginRemoveRows(indexMap[parentId], row, row);
174  childrenMap[parentId].removeAll(id); // Remove this id from the children of parentId
175  parentMap.remove(id);
176  indexMap.remove(id);
177  mParent->endRemoveRows();
178 // mParent->beginRemoveColumns( indexMap[parentId], 0, sourceMessageModel()->columnCount() - 1 );
179 // mParent->endRemoveColumns();
180  }
181  }
182 
183  /*
184  * This item has his parents stored in his threading parts.
185  * Read them and store them in the 'real' maps.
186  *
187  * We store both relationships :
188  * - child -> parents ( real*ParentsMap )
189  * - parent -> children ( real*ChildrenMap )
190  */
191  void readParentsFromParts(const Item &item)
192  {
193  MessageThreadingAttribute *attr = item.attribute<MessageThreadingAttribute>();
194  if (attr) {
195  QList<Entity::Id> realPerfectParentsList = attr->perfectParents();
196  QList<Entity::Id> realUnperfectParentsList = attr->unperfectParents();
197  QList<Entity::Id> realSubjectParentsList = attr->subjectParents();
198 
199  realPerfectParentsMap[item.id()] = realPerfectParentsList;
200  realUnperfectParentsMap[item.id()] = realUnperfectParentsList;
201  realSubjectParentsMap[item.id()] = realSubjectParentsList;
202 
203  // Fill in the children maps
204  foreach (Entity::Id parentId, realPerfectParentsList) {
205  realPerfectChildrenMap[parentId] << item.id();
206  }
207  foreach (Entity::Id parentId, realUnperfectParentsList) {
208  realUnperfectChildrenMap[parentId] << item.id();
209  }
210  foreach (Entity::Id parentId, realSubjectParentsList) {
211  realSubjectChildrenMap[parentId] << item.id();
212  }
213  }
214  }
215 
216  /*
217  * Find the first parent in the parents maps which is actually in the current collection
218  * @param id the item id
219  * @returns the parent id
220  */
221  Entity::Id parentForItem(Entity::Id id)
222  {
223 
224  QList<Entity::Id> parentsIds;
225  parentsIds << realPerfectParentsMap[id] << realUnperfectParentsMap[id] << realSubjectParentsMap[id];
226 
227  foreach (Entity::Id parentId, parentsIds) {
228  // Check that the parent is in the collection
229  // This is time consuming but ... required.
230  if (sourceMessageModel()->indexForItem(Item(parentId), 0).isValid()) {
231  return parentId;
232  }
233  }
234 
235  // TODO Check somewhere for 'parent loops' : in the parts, an item child of his child ...
236  return -1;
237  }
238 
239  // -1 is an invalid id which means 'root'
240  Entity::Id idForIndex(const QModelIndex &index)
241  {
242  return index.isValid() ? index.internalId() : -1;
243  }
244 
245  MessageThreaderProxyModel *mParent;
246 
247  /*
248  * These maps store the current tree structure, as presented in the view.
249  * It tries to be as close as possible from the real structure, given that not every parents
250  * are present in the collection
251  */
252  QHash<Entity::Id, QList<Entity::Id> > childrenMap;
253  QHash<Entity::Id, Entity::Id> parentMap;
254  QHash<Entity::Id, QModelIndex> indexMap;
255 
256  /*
257  * These maps store the real parents, as read from the item parts
258  * In the best case, the list should contain only one element ( = unique parent )
259  * If there isn't only one, the algorithm will pick up the first one in the current collection
260  */
261  QHash<Entity::Id, QList<Entity::Id> > realPerfectParentsMap;
262  QHash<Entity::Id, QList<Entity::Id> > realUnperfectParentsMap;
263  QHash<Entity::Id, QList<Entity::Id> > realSubjectParentsMap;
264 
265  QHash<Entity::Id, QList<Entity::Id> > realPerfectChildrenMap;
266  QHash<Entity::Id, QList<Entity::Id> > realUnperfectChildrenMap;
267  QHash<Entity::Id, QList<Entity::Id> > realSubjectChildrenMap;
268 };
269 
270 MessageThreaderProxyModel::MessageThreaderProxyModel(QObject *parent)
271  : QAbstractProxyModel(parent)
272  , d(new Private(this))
273 {
274  AttributeFactory::registerAttribute<MessageThreadingAttribute>();
275 }
276 
277 MessageThreaderProxyModel::~MessageThreaderProxyModel()
278 {
279  delete d;
280 }
281 
282 QModelIndex MessageThreaderProxyModel::index(int row, int column, const QModelIndex &parent) const
283 {
284  Entity::Id parentId = d->idForIndex(parent);
285 
286  if (row < 0
287  || column < 0
288  || row >= d->childrenMap[parentId].count()
289  || column >= columnCount(parent)) {
290  return QModelIndex();
291  }
292 
293  Entity::Id id = d->childrenMap[parentId].at(row);
294 
295  return createIndex(row, column, id);
296 }
297 
298 QModelIndex MessageThreaderProxyModel::parent(const QModelIndex &index) const
299 {
300  if (!index.isValid()) {
301  return QModelIndex();
302  }
303 
304  Entity::Id parentId = d->parentMap[index.internalId()];
305 
306  if (parentId == -1) {
307  return QModelIndex();
308  }
309 
310 // int parentParentId = d->parentMap[parentId];
311  //int row = d->childrenMap[parentParentId].indexOf( parentId );
312  return d->indexMap[d->parentMap[index.internalId()]];
313  //return createIndex( row, 0, parentId );
314 }
315 
316 QModelIndex MessageThreaderProxyModel::mapToSource(const QModelIndex &index) const
317 {
318  // This function is slow because it relies on rowForItem in the ItemModel (linear time)
319  return d->sourceMessageModel()->indexForItem(Item(index.internalId()), index.column());
320 }
321 
322 QModelIndex MessageThreaderProxyModel::mapFromSource(const QModelIndex &index) const
323 {
324  Item item = d->sourceMessageModel()->itemForIndex(index);
325  Entity::Id id = item.id();
326  //return d->indexMap[id ]; // FIXME take column in account like mapToSource
327  return MessageThreaderProxyModel::index(d->indexMap[id].row(), index.column(), d->indexMap[id].parent());
328 }
329 
330 QModelIndex MessageThreaderProxyModel::createIndex(int row, int column, quint32 internalId) const
331 {
332  QModelIndex index = QAbstractProxyModel::createIndex(row, column, internalId);
333  if (column == 0) {
334  d->indexMap[internalId] = index; // Store the newly created index in the index map
335  }
336  return index;
337 }
338 
339 void MessageThreaderProxyModel::setSourceModel(QAbstractItemModel *model)
340 {
341  // TODO Assert model is a MessageModel
342  QAbstractProxyModel::setSourceModel(model);
343 
344  d->sourceMessageModel()->fetchScope().fetchAttribute<MessageThreadingAttribute>();
345 
346  // TODO disconnect old model
347  connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotInsertRows(QModelIndex,int,int)));
348  connect(sourceModel(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), SLOT(slotRemoveRows(QModelIndex,int,int)));
349  connect(d->sourceMessageModel(), SIGNAL(collectionChanged(Akonadi::Collection)), SLOT(slotCollectionChanged()));
350 }
351 
352 bool MessageThreaderProxyModel::hasChildren(const QModelIndex &index) const
353 {
354  return rowCount(index) > 0;
355 }
356 
357 int MessageThreaderProxyModel::columnCount(const QModelIndex &index) const
358 {
359  Q_UNUSED(index);
360  // We assume that the source model has the same number of columns for each rows
361  return sourceModel()->columnCount(QModelIndex());
362 }
363 
364 int MessageThreaderProxyModel::rowCount(const QModelIndex &index) const
365 {
366  Entity::Id id = d->idForIndex(index);
367  if (id == -1) {
368  return d->childrenMap[-1].count();
369  }
370 
371  if (index.column() == 0) { // QModelIndex() has children
372  return d->childrenMap[id].count();
373  }
374 
375  return 0;
376 }
377 
378 QStringList MessageThreaderProxyModel::mimeTypes() const
379 {
380  return d->sourceMessageModel()->mimeTypes();
381 }
382 
383 QMimeData *MessageThreaderProxyModel::mimeData(const QModelIndexList &indexes) const
384 {
385  QModelIndexList sourceIndexes;
386  for (int i = 0; i < indexes.count(); i++) {
387  sourceIndexes << mapToSource(indexes.at(i));
388  }
389 
390  return sourceModel()->mimeData(sourceIndexes);
391 }
392 
393 #include "moc_messagethreaderproxymodel.cpp"
QModelIndex
Akonadi::MessageThreaderProxyModel::setSourceModel
void setSourceModel(QAbstractItemModel *sourceMessageModel)
Set the source model.
Definition: messagethreaderproxymodel.cpp:339
Akonadi::MessageThreaderProxyModel::mimeTypes
QStringList mimeTypes() const
Reimplemented.
Definition: messagethreaderproxymodel.cpp:378
Akonadi::MessageThreaderProxyModel::createIndex
QModelIndex createIndex(int row, int column, quint32 internalId) const
Reimplemented.
Definition: messagethreaderproxymodel.cpp:330
Akonadi::MessageThreadingAttribute::subjectParents
QList< Item::Id > subjectParents() const
Returns the list of possible parent message ids based on analyzing the subject.
Definition: messagethreadingattribute.cpp:136
QAbstractProxyModel
Akonadi::MessageThreaderProxyModel::mapToSource
QModelIndex mapToSource(const QModelIndex &index) const
Reimplemented.
Definition: messagethreaderproxymodel.cpp:316
Akonadi::MessageThreaderProxyModel::rowCount
int rowCount(const QModelIndex &index) const
Reimplemented.
Definition: messagethreaderproxymodel.cpp:364
Akonadi::MessageThreaderProxyModel
Proxy to thread message using the Mailthreader agent.
Definition: messagethreaderproxymodel.h:38
QModelIndex::internalId
qint64 internalId() const
QAbstractItemModel::rowsAboutToBeRemoved
void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
Akonadi::MessageThreaderProxyModel::mapFromSource
QModelIndex mapFromSource(const QModelIndex &index) const
Reimplemented.
Definition: messagethreaderproxymodel.cpp:322
QTime
QMimeData
QList::indexOf
int indexOf(const T &value, int from) const
Akonadi::MessageThreadingAttribute
Message threading information.
Definition: messagethreadingattribute.h:34
Akonadi::MessageThreaderProxyModel::columnCount
int columnCount(const QModelIndex &index) const
Reimplemented.
Definition: messagethreaderproxymodel.cpp:357
QModelIndex::isValid
bool isValid() const
QTime::elapsed
int elapsed() const
QHash
QObject
Akonadi::MessageThreaderProxyModel::mimeData
QMimeData * mimeData(const QModelIndexList &indexes) const
Reimplemented.
Definition: messagethreaderproxymodel.cpp:383
Akonadi::MessageThreadingAttribute::perfectParents
QList< Item::Id > perfectParents() const
Returns the list of perfect parent message ids.
Definition: messagethreadingattribute.cpp:116
Akonadi::MessageThreadingAttribute::unperfectParents
QList< Item::Id > unperfectParents() const
Returns the list of non-perfect parent message ids.
Definition: messagethreadingattribute.cpp:126
Akonadi::MessageThreaderProxyModel::~MessageThreaderProxyModel
virtual ~MessageThreaderProxyModel()
Destroy the model.
Definition: messagethreaderproxymodel.cpp:277
QAbstractProxyModel::setSourceModel
virtual void setSourceModel(QAbstractItemModel *sourceModel)
QList
QAbstractItemModel::mimeData
virtual QMimeData * mimeData(const QModelIndexList &indexes) const
QStringList
QAbstractItemModel::createIndex
QModelIndex createIndex(int row, int column, void *ptr) const
QAbstractProxyModel::sourceModel
QAbstractItemModel * sourceModel() const
Akonadi::MessageModel
A flat self-updating message model.
Definition: messagemodel.h:38
QAbstractItemModel::columnCount
virtual int columnCount(const QModelIndex &parent) const =0
QModelIndex::column
int column() const
QAbstractItemModel
QTime::start
void start()
Akonadi::MessageThreaderProxyModel::MessageThreaderProxyModel
MessageThreaderProxyModel(QObject *parent=0)
Create a new MessageThreaderProxyModel.
Definition: messagethreaderproxymodel.cpp:270
QAbstractItemModel::rowsInserted
void rowsInserted(const QModelIndex &parent, int start, int end)
Akonadi::MessageThreaderProxyModel::hasChildren
bool hasChildren(const QModelIndex &index) const
Reimplemented.
Definition: messagethreaderproxymodel.cpp:352
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::parent
QObject * parent() const
Akonadi::MessageThreaderProxyModel::index
QModelIndex index(int row, int column, const QModelIndex &parent) const
Reimplemented.
Definition: messagethreaderproxymodel.cpp:282
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:24 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi/kmime

Skip menu "akonadi/kmime"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal