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 QModelIndex extraTopLeft = mParent->index( topLeft.row(), mParent->columnCount( parent ) - 1 - 3 , parent );
00179 QModelIndex extraBottomRight = mParent->index( bottomRight.row(), mParent->columnCount( parent ) -1, parent );
00180 mParent->disconnect( mParent, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ),
00181 mParent, SLOT( proxyDataChanged( const QModelIndex&, const QModelIndex& ) ) );
00182 emit mParent->dataChanged( extraTopLeft, extraBottomRight );
00183
00184
00185
00186
00187 while ( parent.isValid() )
00188 {
00189 emit mParent->dataChanged( parent.sibling( parent.row(), mParent->columnCount( parent ) - 1 - 3 ),
00190 parent.sibling( parent.row(), mParent->columnCount( parent ) - 1 ) );
00191 parent = parent.parent();
00192 }
00193 mParent->connect( mParent, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ),
00194 SLOT( proxyDataChanged( const QModelIndex&, const QModelIndex& ) ) );
00195 }
00196 }
00197
00198 void StatisticsProxyModel::Private::sourceLayoutAboutToBeChanged()
00199 {
00200 QModelIndexList persistent = mParent->persistentIndexList();
00201 const int columnCount = mParent->sourceModel()->columnCount();
00202 foreach( const QModelIndex &idx, persistent ) {
00203 if ( idx.column() >= columnCount ) {
00204 m_nonPersistent.push_back( idx );
00205 m_persistent.push_back( idx );
00206 const QModelIndex firstColumn = idx.sibling( 0, idx.column() );
00207 m_nonPersistentFirstColumn.push_back( firstColumn );
00208 m_persistentFirstColumn.push_back( firstColumn );
00209 }
00210 }
00211 }
00212
00213 void StatisticsProxyModel::Private::sourceLayoutChanged()
00214 {
00215 QModelIndexList oldList;
00216 QModelIndexList newList;
00217
00218 const int columnCount = mParent->sourceModel()->columnCount();
00219
00220 for( int i = 0; i < m_persistent.size(); ++i ) {
00221 const QModelIndex persistentIdx = m_persistent.at( i );
00222 const QModelIndex nonPersistentIdx = m_nonPersistent.at( i );
00223 if ( m_persistentFirstColumn.at( i ) != m_nonPersistentFirstColumn.at( i ) && persistentIdx.column() >= columnCount ) {
00224 oldList.append( nonPersistentIdx );
00225 newList.append( persistentIdx );
00226 }
00227 }
00228 mParent->changePersistentIndexList( oldList, newList );
00229 }
00230
00231 void StatisticsProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
00232 {
00233
00234
00235 disconnect(this, SIGNAL(layoutChanged()), this, SLOT(sourceLayoutChanged()));
00236 connect(this, SIGNAL(layoutChanged()), SLOT(sourceLayoutChanged()));
00237 QSortFilterProxyModel::setSourceModel(sourceModel);
00238
00239
00240 disconnect(this, SIGNAL(layoutAboutToBeChanged()), this, SLOT(sourceLayoutAboutToBeChanged()));
00241 connect(this, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()));
00242 }
00243
00244 void StatisticsProxyModel::connectNotify(const char *signal)
00245 {
00246 static bool ignore = false;
00247 if (ignore || QLatin1String(signal) == SIGNAL(layoutAboutToBeChanged()))
00248 return QSortFilterProxyModel::connectNotify(signal);
00249 ignore = true;
00250 disconnect(this, SIGNAL(layoutAboutToBeChanged()), this, SLOT(sourceLayoutAboutToBeChanged()));
00251 connect(this, SIGNAL(layoutAboutToBeChanged()), SLOT(sourceLayoutAboutToBeChanged()));
00252 ignore = false;
00253 QSortFilterProxyModel::connectNotify(signal);
00254 }
00255
00256
00257 StatisticsProxyModel::StatisticsProxyModel( QObject *parent )
00258 : QSortFilterProxyModel( parent ),
00259 d( new Private( this ) )
00260 {
00261 connect( this, SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ),
00262 SLOT( proxyDataChanged( const QModelIndex&, const QModelIndex& ) ) );
00263 }
00264
00265 StatisticsProxyModel::~StatisticsProxyModel()
00266 {
00267 delete d;
00268 }
00269
00270 void StatisticsProxyModel::setToolTipEnabled( bool enable )
00271 {
00272 d->mToolTipEnabled = enable;
00273 }
00274
00275 bool StatisticsProxyModel::isToolTipEnabled() const
00276 {
00277 return d->mToolTipEnabled;
00278 }
00279
00280 void StatisticsProxyModel::setExtraColumnsEnabled( bool enable )
00281 {
00282 d->mExtraColumnsEnabled = enable;
00283 }
00284
00285 bool StatisticsProxyModel::isExtraColumnsEnabled() const
00286 {
00287 return d->mExtraColumnsEnabled;
00288 }
00289
00290 QModelIndex Akonadi::StatisticsProxyModel::index( int row, int column, const QModelIndex & parent ) const
00291 {
00292 if (!hasIndex(row, column, parent))
00293 return QModelIndex();
00294
00295
00296 int sourceColumn = column;
00297
00298 if ( column>=d->sourceColumnCount( parent ) ) {
00299 sourceColumn = 0;
00300 }
00301
00302 QModelIndex i = QSortFilterProxyModel::index( row, sourceColumn, parent );
00303 return createIndex( i.row(), column, i.internalPointer() );
00304 }
00305
00306 QVariant StatisticsProxyModel::data( const QModelIndex & index, int role) const
00307 {
00308 if (!sourceModel())
00309 return QVariant();
00310 if ( role == Qt::DisplayRole && index.column()>=d->sourceColumnCount( index.parent() ) ) {
00311 const QModelIndex sourceIndex = mapToSource( index.sibling( index.row(), 0 ) );
00312 Collection collection = sourceModel()->data( sourceIndex, EntityTreeModel::CollectionRole ).value<Collection>();
00313
00314 if ( collection.isValid() && collection.statistics().count()>=0 ) {
00315 if ( index.column() == d->sourceColumnCount( QModelIndex() )+2 ) {
00316 return KIO::convertSize( (KIO::filesize_t)( collection.statistics().size() ) );
00317 } else if ( index.column() == d->sourceColumnCount( QModelIndex() )+1 ) {
00318 return collection.statistics().count();
00319 } else if ( index.column() == d->sourceColumnCount( QModelIndex() ) ) {
00320 if ( collection.statistics().unreadCount() > 0 ) {
00321 return collection.statistics().unreadCount();
00322 } else {
00323 return QString();
00324 }
00325 } else {
00326 kWarning() << "We shouldn't get there for a column which is not total, unread or size.";
00327 return QVariant();
00328 }
00329 }
00330
00331 } else if ( role == Qt::TextAlignmentRole && index.column()>=d->sourceColumnCount( index.parent() ) ) {
00332 return Qt::AlignRight;
00333
00334 } else if ( role == Qt::ToolTipRole && d->mToolTipEnabled ) {
00335 const QModelIndex sourceIndex = mapToSource( index.sibling( index.row(), 0 ) );
00336 Collection collection
00337 = sourceModel()->data( sourceIndex,
00338 EntityTreeModel::CollectionRole ).value<Collection>();
00339
00340 if ( collection.isValid() && collection.statistics().count()>0 ) {
00341 return d->toolTipForCollection( index, collection );
00342 }
00343
00344 } else if ( role == Qt::DecorationRole && index.column() == 0 ) {
00345 const QModelIndex sourceIndex = mapToSource( index.sibling( index.row(), 0 ) );
00346 Collection collection = sourceModel()->data( sourceIndex, EntityTreeModel::CollectionRole ).value<Collection>();
00347
00348 if ( collection.isValid() )
00349 return KIcon( CollectionUtils::displayIconName( collection ) );
00350 else
00351 return QVariant();
00352 }
00353
00354 return QAbstractProxyModel::data( index, role );
00355 }
00356
00357 QVariant StatisticsProxyModel::headerData( int section, Qt::Orientation orientation, int role) const
00358 {
00359 if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) {
00360 if ( section == d->sourceColumnCount( QModelIndex() ) + 2 ) {
00361 return i18nc( "collection size", "Size" );
00362 } else if ( section == d->sourceColumnCount( QModelIndex() ) + 1 ) {
00363 return i18nc( "number of entities in the collection", "Total" );
00364 } else if ( section == d->sourceColumnCount( QModelIndex() ) ) {
00365 return i18nc( "number of unread entities in the collection", "Unread" );
00366 }
00367 }
00368
00369 return QSortFilterProxyModel::headerData( section, orientation, role );
00370 }
00371
00372 Qt::ItemFlags StatisticsProxyModel::flags( const QModelIndex & index ) const
00373 {
00374 if ( index.column()>=d->sourceColumnCount( index.parent() ) ) {
00375 return QSortFilterProxyModel::flags( index.sibling( index.row(), 0 ) )
00376 & ( Qt::ItemIsSelectable | Qt::ItemIsDragEnabled
00377 | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled );
00378 }
00379
00380 return QSortFilterProxyModel::flags( index );
00381 }
00382
00383 int StatisticsProxyModel::columnCount( const QModelIndex & parent ) const
00384 {
00385 if ( sourceModel()==0 ) {
00386 return 0;
00387 } else {
00388 return d->sourceColumnCount( parent )
00389 + ( d->mExtraColumnsEnabled ? 3 : 0 );
00390 }
00391 }
00392
00393 QModelIndexList StatisticsProxyModel::match( const QModelIndex& start, int role, const QVariant& value,
00394 int hits, Qt::MatchFlags flags ) const
00395 {
00396 if ( role < Qt::UserRole )
00397 return QSortFilterProxyModel::match( start, role, value, hits, flags );
00398
00399 QModelIndexList list;
00400 QModelIndex proxyIndex;
00401 foreach ( const QModelIndex &idx, sourceModel()->match( mapToSource( start ), role, value, hits, flags ) ) {
00402 proxyIndex = mapFromSource( idx );
00403 if ( proxyIndex.isValid() )
00404 list << proxyIndex;
00405 }
00406
00407 return list;
00408 }
00409
00410
00411 #include "statisticsproxymodel.moc"
00412