00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "statisticsproxymodel.h"
00022
00023 #include "entitytreemodel.h"
00024 #include "collectionutils_p.h"
00025
00026 #include <akonadi/collectionquotaattribute.h>
00027 #include <akonadi/collectionstatistics.h>
00028 #include <akonadi/entitydisplayattribute.h>
00029
00030 #include <kdebug.h>
00031 #include <kiconloader.h>
00032 #include <klocale.h>
00033 #include <kio/global.h>
00034
00035 #include <QtGui/QApplication>
00036 #include <QtGui/QPalette>
00037 #include <KIcon>
00038 using namespace Akonadi;
00039
00043 class StatisticsProxyModel::Private
00044 {
00045 public:
00046 Private( StatisticsProxyModel *parent )
00047 : mParent( parent ), mToolTipEnabled( false ), mExtraColumnsEnabled( true )
00048 {
00049 }
00050
00051 int sourceColumnCount( const QModelIndex &parent )
00052 {
00053 return mParent->sourceModel()->columnCount( mParent->mapToSource( parent ) );
00054 }
00055
00056 QString toolTipForCollection( const QModelIndex &index, const Collection &collection )
00057 {
00058 QString bckColor = QApplication::palette().color( QPalette::ToolTipBase ).name();
00059 QString txtColor = QApplication::palette().color( QPalette::ToolTipText ).name();
00060
00061 QString tip = QString::fromLatin1(
00062 "<table width=\"100%\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\">\n"
00063 );
00064 const QString textDirection = ( QApplication::layoutDirection() == Qt::LeftToRight ) ? QLatin1String( "left" ) : QLatin1String( "right" );
00065 tip += QString::fromLatin1(
00066 " <tr>\n"
00067 " <td bgcolor=\"%1\" colspan=\"2\" align=\"%4\" valign=\"middle\">\n"
00068 " <div style=\"color: %2; font-weight: bold;\">\n"
00069 " %3\n"
00070 " </div>\n"
00071 " </td>\n"
00072 " </tr>\n"
00073 ).arg( txtColor ).arg( bckColor ).arg( index.data( Qt::DisplayRole ).toString() ).arg( textDirection );
00074
00075
00076 tip += QString::fromLatin1(
00077 " <tr>\n"
00078 " <td align=\"%1\" valign=\"top\">\n"
00079 ).arg( textDirection );
00080
00081 QString tipInfo;
00082 tipInfo += QString::fromLatin1(
00083 " <strong>%1</strong>: %2<br>\n"
00084 " <strong>%3</strong>: %4<br><br>\n"
00085 ).arg( i18n( "Total Messages" ) ).arg( collection.statistics().count() )
00086 .arg( i18n( "Unread Messages" ) ).arg( collection.statistics().unreadCount() );
00087
00088 if ( collection.hasAttribute<CollectionQuotaAttribute>() ) {
00089 CollectionQuotaAttribute *quota = collection.attribute<CollectionQuotaAttribute>();
00090 if ( quota->currentValue() > -1 && quota->maximumValue() > 0 ) {
00091 qreal percentage = ( 100.0 * quota->currentValue() ) / quota->maximumValue();
00092
00093 if ( qAbs( percentage ) >= 0.01 ) {
00094 QString percentStr = QString::number( percentage, 'f', 2 );
00095 tipInfo += QString::fromLatin1(
00096 " <strong>%1</strong>: %2%<br>\n"
00097 ).arg( i18n( "Quota" ) ).arg( percentStr );
00098 }
00099 }
00100 }
00101
00102 tipInfo += QString::fromLatin1(
00103 " <strong>%1</strong>: %2<br>\n"
00104 ).arg( i18n( "Storage Size" ) ).arg( KIO::convertSize( (KIO::filesize_t)( collection.statistics().size() ) ) );
00105
00106
00107 QString iconName = CollectionUtils::defaultIconName( collection );
00108 if ( collection.hasAttribute<EntityDisplayAttribute>() &&
00109 !collection.attribute<EntityDisplayAttribute>()->iconName().isEmpty() ) {
00110 iconName = collection.attribute<EntityDisplayAttribute>()->iconName();
00111 }
00112
00113 int iconSizes[] = { 32, 22 };
00114 int icon_size_found = 32;
00115
00116 QString iconPath;
00117
00118 for ( int i = 0; i < 2; i++ ) {
00119 iconPath = KIconLoader::global()->iconPath( iconName, -iconSizes[ i ], true );
00120 if ( !iconPath.isEmpty() ) {
00121 icon_size_found = iconSizes[ i ];
00122 break;
00123 }
00124 }
00125
00126 if ( iconPath.isEmpty() ) {
00127 iconPath = KIconLoader::global()->iconPath( QLatin1String( "folder" ), -32, false );
00128 }
00129
00130 QString tipIcon = QString::fromLatin1(
00131 " <table border=\"0\"><tr><td width=\"32\" height=\"32\" align=\"center\" valign=\"middle\">\n"
00132 " <img src=\"%1\" width=\"%2\" height=\"32\">\n"
00133 " </td></tr></table>\n"
00134 " </td>\n"
00135 ).arg( iconPath ).arg( icon_size_found ) ;
00136
00137 if ( QApplication::layoutDirection() == Qt::LeftToRight )
00138 {
00139 tip += tipInfo + QString::fromLatin1( "</td><td align=\"%3\" valign=\"top\">" ).arg( textDirection ) + tipIcon;
00140 }
00141 else
00142 {
00143 tip += tipIcon + QString::fromLatin1( "</td><td align=\"%3\" valign=\"top\">" ).arg( textDirection ) + tipInfo;
00144 }
00145
00146
00147 tip += QString::fromLatin1(
00148 " </tr>" \
00149 "</table>"
00150 );
00151
00152 return tip;
00153 }
00154
00155 void proxyDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
00156
00157 void sourceLayoutAboutToBeChanged();
00158 void sourceLayoutChanged();
00159
00160 QVector<QModelIndex> m_nonPersistent;
00161 QVector<QModelIndex> m_nonPersistentFirstColumn;
00162 QVector<QPersistentModelIndex> m_persistent;
00163 QVector<QPersistentModelIndex> m_persistentFirstColumn;
00164
00165 StatisticsProxyModel *mParent;
00166
00167 bool mToolTipEnabled;
00168 bool mExtraColumnsEnabled;
00169 };
00170
00171 void StatisticsProxyModel::Private::proxyDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
00172 {
00173 if ( mExtraColumnsEnabled )
00174 {
00175
00176
00177 QModelIndex parent = topLeft.parent();
00178 int parentColumnCount = mParent->columnCount( parent );
00179 QModelIndex extraTopLeft = mParent->index( topLeft.row(), parentColumnCount - 1 - 3 , parent );
00180 QModelIndex extraBottomRight = mParent->index( bottomRight.row(), parentColumnCount -1, parent );
00181 mParent->disconnect( mParent, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ),
00182 mParent, SLOT( proxyDataChanged( const QModelIndex&, const QModelIndex& ) ) );
00183 emit mParent->dataChanged( extraTopLeft, extraBottomRight );
00184
00185
00186
00187
00188 while ( parent.isValid() )
00189 {
00190 emit mParent->dataChanged( parent.sibling( parent.row(), parentColumnCount - 1 - 3 ),
00191 parent.sibling( parent.row(), parentColumnCount - 1 ) );
00192 parent = parent.parent();
00193 parentColumnCount = mParent->columnCount( parent );
00194 }
00195 mParent->connect( mParent, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ),
00196 SLOT( proxyDataChanged( const QModelIndex&, const QModelIndex& ) ) );
00197 }
00198 }
00199
00200 void StatisticsProxyModel::Private::sourceLayoutAboutToBeChanged()
00201 {
00202 QModelIndexList persistent = mParent->persistentIndexList();
00203 const int columnCount = mParent->sourceModel()->columnCount();
00204 foreach( const QModelIndex &idx, persistent ) {
00205 if ( idx.column() >= columnCount ) {
00206 m_nonPersistent.push_back( idx );
00207 m_persistent.push_back( idx );
00208 const QModelIndex firstColumn = idx.sibling( 0, idx.column() );
00209 m_nonPersistentFirstColumn.push_back( firstColumn );
00210 m_persistentFirstColumn.push_back( firstColumn );
00211 }
00212 }
00213 }
00214
00215 void StatisticsProxyModel::Private::sourceLayoutChanged()
00216 {
00217 QModelIndexList oldList;
00218 QModelIndexList newList;
00219
00220 const int columnCount = mParent->sourceModel()->columnCount();
00221
00222 for( int i = 0; i < m_persistent.size(); ++i ) {
00223 const QModelIndex persistentIdx = m_persistent.at( i );
00224 const QModelIndex nonPersistentIdx = m_nonPersistent.at( i );
00225 if ( m_persistentFirstColumn.at( i ) != m_nonPersistentFirstColumn.at( i ) && persistentIdx.column() >= columnCount ) {
00226 oldList.append( nonPersistentIdx );
00227 newList.append( persistentIdx );
00228 }
00229 }
00230 mParent->changePersistentIndexList( oldList, newList );
00231 }
00232
00233 void StatisticsProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
00234 {
00235
00236
00237 disconnect(this, SIGNAL(layoutChanged()), this, SLOT(sourceLayoutChanged()));
00238 connect(this, SIGNAL(layoutChanged()), SLOT(sourceLayoutChanged()));
00239 QSortFilterProxyModel::setSourceModel(sourceModel);
00240
00241
00242 disconnect(this, SIGNAL(layoutAboutToBeChanged()), this, SLOT(sourceLayoutAboutToBeChanged()));
00243 connect(this, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()));
00244 }
00245
00246 void StatisticsProxyModel::connectNotify(const char *signal)
00247 {
00248 static bool ignore = false;
00249 if (ignore || QLatin1String(signal) == SIGNAL(layoutAboutToBeChanged()))
00250 return QSortFilterProxyModel::connectNotify(signal);
00251 ignore = true;
00252 disconnect(this, SIGNAL(layoutAboutToBeChanged()), this, SLOT(sourceLayoutAboutToBeChanged()));
00253 connect(this, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()));
00254 ignore = false;
00255 QSortFilterProxyModel::connectNotify(signal);
00256 }
00257
00258
00259 StatisticsProxyModel::StatisticsProxyModel( QObject *parent )
00260 : QSortFilterProxyModel( parent ),
00261 d( new Private( this ) )
00262 {
00263 connect( this, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ),
00264 SLOT( proxyDataChanged( const QModelIndex&, const QModelIndex& ) ) );
00265 }
00266
00267 StatisticsProxyModel::~StatisticsProxyModel()
00268 {
00269 delete d;
00270 }
00271
00272 void StatisticsProxyModel::setToolTipEnabled( bool enable )
00273 {
00274 d->mToolTipEnabled = enable;
00275 }
00276
00277 bool StatisticsProxyModel::isToolTipEnabled() const
00278 {
00279 return d->mToolTipEnabled;
00280 }
00281
00282 void StatisticsProxyModel::setExtraColumnsEnabled( bool enable )
00283 {
00284 d->mExtraColumnsEnabled = enable;
00285 }
00286
00287 bool StatisticsProxyModel::isExtraColumnsEnabled() const
00288 {
00289 return d->mExtraColumnsEnabled;
00290 }
00291
00292 QModelIndex Akonadi::StatisticsProxyModel::index( int row, int column, const QModelIndex & parent ) const
00293 {
00294 if (!hasIndex(row, column, parent))
00295 return QModelIndex();
00296
00297
00298 int sourceColumn = column;
00299
00300 if ( column>=d->sourceColumnCount( parent ) ) {
00301 sourceColumn = 0;
00302 }
00303
00304 QModelIndex i = QSortFilterProxyModel::index( row, sourceColumn, parent );
00305 return createIndex( i.row(), column, i.internalPointer() );
00306 }
00307
00308 QVariant StatisticsProxyModel::data( const QModelIndex & index, int role) const
00309 {
00310 if (!sourceModel())
00311 return QVariant();
00312 if ( role == Qt::DisplayRole && index.column()>=d->sourceColumnCount( index.parent() ) ) {
00313 const QModelIndex sourceIndex = mapToSource( index.sibling( index.row(), 0 ) );
00314 Collection collection = sourceModel()->data( sourceIndex, EntityTreeModel::CollectionRole ).value<Collection>();
00315
00316 if ( collection.isValid() && collection.statistics().count()>=0 ) {
00317 if ( index.column() == d->sourceColumnCount( QModelIndex() )+2 ) {
00318 return KIO::convertSize( (KIO::filesize_t)( collection.statistics().size() ) );
00319 } else if ( index.column() == d->sourceColumnCount( QModelIndex() )+1 ) {
00320 return collection.statistics().count();
00321 } else if ( index.column() == d->sourceColumnCount( QModelIndex() ) ) {
00322 if ( collection.statistics().unreadCount() > 0 ) {
00323 return collection.statistics().unreadCount();
00324 } else {
00325 return QString();
00326 }
00327 } else {
00328 kWarning() << "We shouldn't get there for a column which is not total, unread or size.";
00329 return QVariant();
00330 }
00331 }
00332
00333 } else if ( role == Qt::TextAlignmentRole && index.column()>=d->sourceColumnCount( index.parent() ) ) {
00334 return Qt::AlignRight;
00335
00336 } else if ( role == Qt::ToolTipRole && d->mToolTipEnabled ) {
00337 const QModelIndex sourceIndex = mapToSource( index.sibling( index.row(), 0 ) );
00338 Collection collection
00339 = sourceModel()->data( sourceIndex,
00340 EntityTreeModel::CollectionRole ).value<Collection>();
00341
00342 if ( collection.isValid() && collection.statistics().count()>0 ) {
00343 return d->toolTipForCollection( index, collection );
00344 }
00345
00346 } else if ( role == Qt::DecorationRole && index.column() == 0 ) {
00347 const QModelIndex sourceIndex = mapToSource( index.sibling( index.row(), 0 ) );
00348 Collection collection = sourceModel()->data( sourceIndex, EntityTreeModel::CollectionRole ).value<Collection>();
00349
00350 if ( collection.isValid() )
00351 return KIcon( CollectionUtils::displayIconName( collection ) );
00352 else
00353 return QVariant();
00354 }
00355
00356 return QAbstractProxyModel::data( index, role );
00357 }
00358
00359 QVariant StatisticsProxyModel::headerData( int section, Qt::Orientation orientation, int role) const
00360 {
00361 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
00362 if ( section == d->sourceColumnCount( QModelIndex() ) + 2 ) {
00363 return i18nc( "collection size", "Size" );
00364 } else if ( section == d->sourceColumnCount( QModelIndex() ) + 1 ) {
00365 return i18nc( "number of entities in the collection", "Total" );
00366 } else if ( section == d->sourceColumnCount( QModelIndex() ) ) {
00367 return i18nc( "number of unread entities in the collection", "Unread" );
00368 }
00369 }
00370
00371 return QSortFilterProxyModel::headerData( section, orientation, role );
00372 }
00373
00374 Qt::ItemFlags StatisticsProxyModel::flags( const QModelIndex & index ) const
00375 {
00376 if ( index.column()>=d->sourceColumnCount( index.parent() ) ) {
00377 return QSortFilterProxyModel::flags( index.sibling( index.row(), 0 ) )
00378 & ( Qt::ItemIsSelectable | Qt::ItemIsDragEnabled
00379 | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled );
00380 }
00381
00382 return QSortFilterProxyModel::flags( index );
00383 }
00384
00385 int StatisticsProxyModel::columnCount( const QModelIndex & parent ) const
00386 {
00387 if ( sourceModel()==0 ) {
00388 return 0;
00389 } else {
00390 return d->sourceColumnCount( parent )
00391 + ( d->mExtraColumnsEnabled ? 3 : 0 );
00392 }
00393 }
00394
00395 QModelIndexList StatisticsProxyModel::match( const QModelIndex& start, int role, const QVariant& value,
00396 int hits, Qt::MatchFlags flags ) const
00397 {
00398 if ( role < Qt::UserRole )
00399 return QSortFilterProxyModel::match( start, role, value, hits, flags );
00400
00401 QModelIndexList list;
00402 QModelIndex proxyIndex;
00403 foreach ( const QModelIndex &idx, sourceModel()->match( mapToSource( start ), role, value, hits, flags ) ) {
00404 proxyIndex = mapFromSource( idx );
00405 if ( proxyIndex.isValid() )
00406 list << proxyIndex;
00407 }
00408
00409 return list;
00410 }
00411
00412
00413 #include "statisticsproxymodel.moc"
00414