22 #include <QItemSelection>
24 #include <akonadi/entitytreemodel.h>
25 #include <akonadi/collectionutils_p.h>
27 #include <akonadi/collectionquotaattribute.h>
28 #include <akonadi/collectionstatistics.h>
29 #include <akonadi/entitydisplayattribute.h>
32 #include <kiconloader.h>
34 #include <kio/global.h>
36 #include <QApplication>
39 using namespace Akonadi;
45 class StatisticsProxyModel::Private
49 : mParent( parent ), mToolTipEnabled( false ), mExtraColumnsEnabled( true )
53 void getCountRecursive(
const QModelIndex &index, qint64 &totalSize )
const
55 Collection collection = qvariant_cast<Collection>( index.data( EntityTreeModel::CollectionRole ) );
58 if ( collection.isValid() ) {
59 CollectionStatistics statistics = collection.statistics();
60 totalSize += qMax( 0LL, statistics.size() );
61 if ( index.model()->hasChildren( index ) ) {
62 const int rowCount = index.model()->rowCount( index );
63 for (
int row = 0; row < rowCount; row++ ) {
64 static const int column = 0;
65 getCountRecursive( index.model()->index( row, column, index ), totalSize );
71 int sourceColumnCount()
const
73 return mParent->sourceModel()->columnCount();
76 QModelIndex sourceIndexAtFirstColumn(
const QModelIndex& proxyIndex)
const;
78 QString toolTipForCollection(
const QModelIndex &index,
const Collection &collection )
80 QString bckColor = QApplication::palette().color( QPalette::ToolTipBase ).name();
81 QString txtColor = QApplication::palette().color( QPalette::ToolTipText ).name();
83 QString tip = QString::fromLatin1(
84 "<table width=\"100%\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\">\n"
86 const QString textDirection = ( QApplication::layoutDirection() == Qt::LeftToRight ) ? QLatin1String(
"left" ) : QLatin1String(
"right" );
87 tip += QString::fromLatin1(
89 " <td bgcolor=\"%1\" colspan=\"2\" align=\"%4\" valign=\"middle\">\n"
90 " <div style=\"color: %2; font-weight: bold;\">\n"
95 ).arg( txtColor ).arg( bckColor ).arg( index.data( Qt::DisplayRole ).toString() ).arg( textDirection );
98 tip += QString::fromLatin1(
100 " <td align=\"%1\" valign=\"top\">\n"
101 ).arg( textDirection );
104 tipInfo += QString::fromLatin1(
105 " <strong>%1</strong>: %2<br>\n"
106 " <strong>%3</strong>: %4<br><br>\n"
107 ).arg( i18n(
"Total Messages" ) ).arg( collection.statistics().count() )
108 .arg( i18n(
"Unread Messages" ) ).arg( collection.statistics().unreadCount() );
110 if ( collection.hasAttribute<CollectionQuotaAttribute>() ) {
111 CollectionQuotaAttribute *quota = collection.attribute<CollectionQuotaAttribute>();
112 if ( quota->currentValue() > -1 && quota->maximumValue() > 0 ) {
113 qreal percentage = ( 100.0 * quota->currentValue() ) / quota->maximumValue();
115 if ( qAbs( percentage ) >= 0.01 ) {
116 QString percentStr = QString::number( percentage,
'f', 2 );
117 tipInfo += QString::fromLatin1(
118 " <strong>%1</strong>: %2%<br>\n"
119 ).arg( i18n(
"Quota" ) ).arg( percentStr );
124 qint64 currentFolderSize( collection.statistics().size() );
125 tipInfo += QString::fromLatin1(
126 " <strong>%1</strong>: %2<br>\n"
127 ).arg( i18n(
"Storage Size" ) ).arg( KIO::convertSize( (KIO::filesize_t)( currentFolderSize ) ) );
130 qint64 totalSize = 0;
131 getCountRecursive( index, totalSize );
132 totalSize -= currentFolderSize;
133 if (totalSize > 0 ) {
134 tipInfo += QString::fromLatin1(
135 "<strong>%1</strong>: %2<br>"
136 ).arg( i18n(
"Subfolder Storage Size") ).arg( KIO::convertSize( (KIO::filesize_t)( totalSize ) ) );
139 QString iconName = CollectionUtils::defaultIconName( collection );
140 if ( collection.hasAttribute<EntityDisplayAttribute>() &&
141 !collection.attribute<EntityDisplayAttribute>()->iconName().isEmpty() ) {
142 if ( !collection.attribute<EntityDisplayAttribute>()->activeIconName().isEmpty() && collection.statistics().unreadCount()> 0) {
143 iconName = collection.attribute<EntityDisplayAttribute>()->activeIconName();
146 iconName = collection.attribute<EntityDisplayAttribute>()->iconName();
150 int iconSizes[] = { 32, 22 };
151 int icon_size_found = 32;
155 for (
int i = 0; i < 2; ++i ) {
156 iconPath = KIconLoader::global()->iconPath( iconName, -iconSizes[ i ],
true );
157 if ( !iconPath.isEmpty() ) {
158 icon_size_found = iconSizes[ i ];
163 if ( iconPath.isEmpty() ) {
164 iconPath = KIconLoader::global()->iconPath( QLatin1String(
"folder" ), -32,
false );
167 QString tipIcon = QString::fromLatin1(
168 " <table border=\"0\"><tr><td width=\"32\" height=\"32\" align=\"center\" valign=\"middle\">\n"
169 " <img src=\"%1\" width=\"%2\" height=\"32\">\n"
170 " </td></tr></table>\n"
172 ).arg( iconPath ).arg( icon_size_found ) ;
174 if ( QApplication::layoutDirection() == Qt::LeftToRight )
176 tip += tipInfo + QString::fromLatin1(
"</td><td align=\"%3\" valign=\"top\">" ).arg( textDirection ) + tipIcon;
180 tip += tipIcon + QString::fromLatin1(
"</td><td align=\"%3\" valign=\"top\">" ).arg( textDirection ) + tipInfo;
184 tip += QString::fromLatin1(
192 void proxyDataChanged(
const QModelIndex &topLeft,
const QModelIndex &bottomRight);
194 void sourceLayoutAboutToBeChanged();
195 void sourceLayoutChanged();
197 QVector<QModelIndex> m_proxyIndexes;
198 QVector<QPersistentModelIndex> m_persistentSourceFirstColumn;
202 bool mToolTipEnabled;
203 bool mExtraColumnsEnabled;
206 void StatisticsProxyModel::Private::proxyDataChanged(
const QModelIndex& topLeft,
const QModelIndex& bottomRight)
208 if ( mExtraColumnsEnabled )
212 QModelIndex parent = topLeft.
parent();
213 int parentColumnCount = mParent->columnCount( parent );
214 QModelIndex extraTopLeft = mParent->index( topLeft.row(), parentColumnCount - 1 - 3 , parent );
215 QModelIndex extraBottomRight = mParent->index( bottomRight.row(), parentColumnCount -1, parent );
216 mParent->disconnect( mParent, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
217 mParent, SLOT(proxyDataChanged(QModelIndex,QModelIndex)) );
218 emit mParent->dataChanged( extraTopLeft, extraBottomRight );
223 while ( parent.isValid() )
225 emit mParent->dataChanged( parent.sibling( parent.row(), parentColumnCount - 1 - 3 ),
226 parent.sibling( parent.row(), parentColumnCount - 1 ) );
227 parent = parent.parent();
228 parentColumnCount = mParent->columnCount( parent );
230 mParent->connect( mParent, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
231 SLOT(proxyDataChanged(QModelIndex,QModelIndex)) );
235 void StatisticsProxyModel::Private::sourceLayoutAboutToBeChanged()
240 QModelIndexList persistent = mParent->persistentIndexList();
241 const int columnCount = mParent->sourceModel()->columnCount();
242 foreach(
const QModelIndex &proxyPersistentIndex, persistent ) {
243 if ( proxyPersistentIndex.column() >= columnCount ) {
244 m_proxyIndexes << proxyPersistentIndex;
245 m_persistentSourceFirstColumn << QPersistentModelIndex( sourceIndexAtFirstColumn( proxyPersistentIndex ) );
250 void StatisticsProxyModel::Private::sourceLayoutChanged()
252 QModelIndexList oldList;
253 QModelIndexList newList;
255 for(
int i = 0; i < m_proxyIndexes.size(); ++i ) {
256 const QModelIndex oldProxyIndex = m_proxyIndexes.at( i );
257 const QModelIndex proxyIndexFirstCol = mParent->mapFromSource( m_persistentSourceFirstColumn.at( i ) );
258 const QModelIndex newProxyIndex = proxyIndexFirstCol.sibling( proxyIndexFirstCol.row(), oldProxyIndex.column() );
259 if ( newProxyIndex != oldProxyIndex ) {
260 oldList.append( oldProxyIndex );
261 newList.append( newProxyIndex );
264 mParent->changePersistentIndexList( oldList, newList );
265 m_persistentSourceFirstColumn.clear();
266 m_proxyIndexes.clear();
269 void StatisticsProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
273 disconnect(
this, SIGNAL(layoutChanged()),
this, SLOT(sourceLayoutChanged()));
274 connect(
this, SIGNAL(layoutChanged()), SLOT(sourceLayoutChanged()));
275 KIdentityProxyModel::setSourceModel(sourceModel);
278 disconnect(
this, SIGNAL(layoutAboutToBeChanged()),
this, SLOT(sourceLayoutAboutToBeChanged()));
279 connect(
this, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()));
282 void StatisticsProxyModel::connectNotify(
const char *signal)
284 static bool ignore =
false;
285 if (ignore || QLatin1String(signal) == SIGNAL(layoutAboutToBeChanged()))
286 return KIdentityProxyModel::connectNotify(signal);
288 disconnect(
this, SIGNAL(layoutAboutToBeChanged()),
this, SLOT(sourceLayoutAboutToBeChanged()));
289 connect(
this, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()));
291 KIdentityProxyModel::connectNotify(signal);
295 StatisticsProxyModel::StatisticsProxyModel(
QObject *parent )
297 d( new Private( this ) )
299 connect(
this, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
300 SLOT(proxyDataChanged(QModelIndex,QModelIndex)) );
310 d->mToolTipEnabled = enable;
315 return d->mToolTipEnabled;
320 d->mExtraColumnsEnabled = enable;
325 return d->mExtraColumnsEnabled;
330 if (!hasIndex(row, column, parent))
331 return QModelIndex();
334 int sourceColumn = column;
335 if ( column >= d->sourceColumnCount() ) {
339 QModelIndex i = KIdentityProxyModel::index( row, sourceColumn, parent );
340 return createIndex( i.row(), column, i.internalPointer() );
343 struct SourceModelIndex
345 SourceModelIndex(
int _r,
int _c,
void *_p, QAbstractItemModel *_m)
346 : r(_r), c(_c), p(_p), m(_m) {}
348 operator QModelIndex() {
return reinterpret_cast<QModelIndex&
>(*this); }
352 const QAbstractItemModel *m;
355 QModelIndex StatisticsProxyModel::Private::sourceIndexAtFirstColumn(
const QModelIndex& proxyIndex)
const
358 return SourceModelIndex(proxyIndex.row(), 0, proxyIndex.internalPointer(), mParent->sourceModel());
364 return QModelIndex();
366 Q_ASSERT(child.isValid() ? child.model() ==
this :
true);
367 if (child.column() >= d->sourceColumnCount()) {
372 const QModelIndex sourceIndex = d->sourceIndexAtFirstColumn(child);
373 const QModelIndex sourceParent = sourceIndex.parent();
377 return KIdentityProxyModel::parent(child);
386 const int sourceColumnCount = d->sourceColumnCount();
388 if ( role == Qt::DisplayRole && index.column() >= sourceColumnCount ) {
389 const QModelIndex sourceIndex = d->sourceIndexAtFirstColumn( index );
390 Collection collection = sourceModel()->data( sourceIndex, EntityTreeModel::CollectionRole ).value<Collection>();
392 if ( collection.isValid() && collection.statistics().count()>=0 ) {
393 if ( index.column() == sourceColumnCount + 2 ) {
394 return KIO::convertSize( (KIO::filesize_t)( collection.statistics().size() ) );
395 }
else if ( index.column() == sourceColumnCount + 1 ) {
396 return collection.statistics().count();
397 }
else if ( index.column() == sourceColumnCount ) {
398 if ( collection.statistics().unreadCount() > 0 ) {
399 return collection.statistics().unreadCount();
404 kWarning() <<
"We shouldn't get there for a column which is not total, unread or size.";
409 }
else if ( role == Qt::TextAlignmentRole && index.column() >= sourceColumnCount ) {
410 return Qt::AlignRight;
412 }
else if ( role == Qt::ToolTipRole && d->mToolTipEnabled ) {
413 const QModelIndex sourceIndex = d->sourceIndexAtFirstColumn( index );
414 Collection collection
415 = sourceModel()->data( sourceIndex,
416 EntityTreeModel::CollectionRole ).value<Collection>();
418 if ( collection.isValid() ) {
419 const QModelIndex sourceIndex = d->sourceIndexAtFirstColumn( index );
420 return d->toolTipForCollection( sourceIndex, collection );
423 }
else if ( role == Qt::DecorationRole && index.column() == 0 ) {
424 const QModelIndex sourceIndex =
mapToSource( index );
425 Collection collection = sourceModel()->data( sourceIndex, EntityTreeModel::CollectionRole ).value<Collection>();
426 if ( collection.isValid() )
427 return KIcon( CollectionUtils::displayIconName( collection ) );
432 if ( index.column() >= sourceColumnCount )
435 return QAbstractProxyModel::data( index, role );
440 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
441 if ( section == d->sourceColumnCount() + 2 ) {
442 return i18nc(
"collection size",
"Size" );
443 }
else if ( section == d->sourceColumnCount() + 1 ) {
444 return i18nc(
"number of entities in the collection",
"Total" );
445 }
else if ( section == d->sourceColumnCount() ) {
446 return i18nc(
"number of unread entities in the collection",
"Unread" );
450 if ( orientation == Qt::Horizontal && section >= d->sourceColumnCount() ) {
454 return KIdentityProxyModel::headerData( section, orientation, role );
459 if ( index.column() >= d->sourceColumnCount() ) {
460 return KIdentityProxyModel::flags( index.sibling( index.row(), 0 ) )
461 & ( Qt::ItemIsSelectable | Qt::ItemIsDragEnabled
462 | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled );
465 return KIdentityProxyModel::flags( index );
470 if ( sourceModel()==0 ) {
473 return d->sourceColumnCount()
474 + ( d->mExtraColumnsEnabled ? 3 : 0 );
479 int hits, Qt::MatchFlags flags )
const
481 if ( role < Qt::UserRole )
482 return KIdentityProxyModel::match( start, role, value, hits, flags );
484 QModelIndexList list;
485 QModelIndex proxyIndex;
486 foreach (
const QModelIndex &idx, sourceModel()->
match(
mapToSource( start ), role, value, hits, flags ) ) {
488 if ( proxyIndex.isValid() )
497 if (!sourceIndex.isValid())
498 return QModelIndex();
499 Q_ASSERT(sourceIndex.model() == sourceModel());
500 Q_ASSERT(sourceIndex.column() < d->sourceColumnCount());
501 return KIdentityProxyModel::mapFromSource(sourceIndex);
506 if (!index.isValid())
507 return QModelIndex();
508 Q_ASSERT(index.model() ==
this);
509 if (index.column() >= d->sourceColumnCount() ) {
510 return QModelIndex();
512 return KIdentityProxyModel::mapToSource(index);
518 return QModelIndex();
523 QItemSelection sourceSelection;
526 return sourceSelection;
530 const int sourceColumnCount = d->sourceColumnCount();
531 QItemSelection::const_iterator it = selection.constBegin();
532 const QItemSelection::const_iterator end = selection.constEnd();
533 for ( ; it != end; ++it) {
534 Q_ASSERT(it->model() ==
this);
535 QModelIndex topLeft = it->topLeft();
536 Q_ASSERT(topLeft.isValid());
537 Q_ASSERT(topLeft.model() ==
this);
538 topLeft = topLeft.sibling(topLeft.row(), 0);
539 QModelIndex bottomRight = it->bottomRight();
540 Q_ASSERT(bottomRight.isValid());
541 Q_ASSERT(bottomRight.model() ==
this);
542 if (bottomRight.column() >= sourceColumnCount)
543 bottomRight = bottomRight.sibling(bottomRight.row(), sourceColumnCount-1);
546 QItemSelection newSelection; newSelection << range;
547 sourceSelection.merge(newSelection, QItemSelectionModel::Select);
550 return sourceSelection;
553 #include "statisticsproxymodel.moc"
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
bool isExtraColumnsEnabled() const
Return true if we display extra statistics columns, otherwise false.
void setToolTipEnabled(bool enable)
A proxy model that exposes collection statistics through extra columns.
virtual int columnCount(const QModelIndex &parent=QModelIndex()) const
bool isToolTipEnabled() const
Return true if we display tooltips, otherwise false.
virtual QModelIndexList match(const QModelIndex &start, int role, const QVariant &value, int hits=1, Qt::MatchFlags flags=Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const
virtual QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
void setExtraColumnsEnabled(bool enable)
QModelIndex mapToSource(const QModelIndex &sourceIndex) const
virtual QModelIndex parent(const QModelIndex &child) const
QItemSelection mapSelectionToSource(const QItemSelection &selection) const
virtual ~StatisticsProxyModel()
Destroys the statistics proxy model.
QModelIndex buddy(const QModelIndex &index) const
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
virtual Qt::ItemFlags flags(const QModelIndex &index) const