• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

akonadi

statisticsproxymodel.cpp
00001 /*
00002     Copyright (c) 2009 Kevin Ottens <ervin@kde.org>
00003 
00004 
00005     This library is free software; you can redistribute it and/or modify it
00006     under the terms of the GNU Library General Public License as published by
00007     the Free Software Foundation; either version 2 of the License, or (at your
00008     option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful, but WITHOUT
00011     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00012     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00013     License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to the
00017     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00018     02110-1301, USA.
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     // Ugly hack.
00176     // The proper solution is a KExtraColumnsProxyModel, but this will do for now.
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     // We get this signal when the statistics of a row changes.
00186     // However, we need to emit data changed for the statistics of all ancestor rows too
00187     // so that recursive totals can be updated.
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   // Order is important here. sourceLayoutChanged must be called *before* any downstreams react
00236   // to the layoutChanged so that it can have the QPersistentModelIndexes uptodate in time.
00237   disconnect(this, SIGNAL(layoutChanged()), this, SLOT(sourceLayoutChanged()));
00238   connect(this, SIGNAL(layoutChanged()), SLOT(sourceLayoutChanged()));
00239   QSortFilterProxyModel::setSourceModel(sourceModel);
00240   // This one should come *after* any downstream handlers of layoutAboutToBeChanged.
00241   // The connectNotify stuff below ensures that it remains the last one.
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 // Allowed flags
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 

akonadi

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

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal