collectionstatisticsdelegate.cpp
00001 /* 00002 Copyright (c) 2008 Thomas McGuire <thomas.mcguire@gmx.net> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 00018 */ 00019 00020 #include "collectionstatisticsdelegate.h" 00021 #include "collectionstatisticsmodel.h" 00022 00023 #include <kcolorscheme.h> 00024 #include <kdebug.h> 00025 00026 #include <QtGui/QPainter> 00027 #include <QtGui/QStyle> 00028 #include <QtGui/QStyleOption> 00029 #include <QtGui/QStyleOptionViewItemV4> 00030 #include <QtGui/QAbstractItemView> 00031 #include <QtGui/QTreeView> 00032 00033 #include "entitytreemodel.h" 00034 #include "collectionstatistics.h" 00035 #include "collection.h" 00036 #include "progressspinnerdelegate_p.h" 00037 00038 using namespace Akonadi; 00039 00040 namespace Akonadi { 00041 00042 enum CountType 00043 { 00044 UnreadCount, 00045 TotalCount 00046 }; 00047 00048 class CollectionStatisticsDelegatePrivate 00049 { 00050 public: 00051 QAbstractItemView *parent; 00052 bool drawUnreadAfterFolder; 00053 DelegateAnimator *animator; 00054 QColor mSelectedUnreadColor; 00055 QColor mDeselectedUnreadColor; 00056 00057 CollectionStatisticsDelegatePrivate( QAbstractItemView *treeView ) 00058 : parent( treeView ), 00059 drawUnreadAfterFolder( false ), 00060 animator( 0 ) 00061 { 00062 mSelectedUnreadColor = KColorScheme( QPalette::Active, KColorScheme::Selection ) 00063 .foreground( KColorScheme::LinkText ).color(); 00064 mDeselectedUnreadColor = KColorScheme( QPalette::Active, KColorScheme::View ) 00065 .foreground( KColorScheme::LinkText ).color(); 00066 } 00067 00068 void getCountRecursive( const QModelIndex &index, qint64 &totalCount, qint64 &unreadCount ) const 00069 { 00070 Collection collection = qvariant_cast<Collection>( index.data( EntityTreeModel::CollectionRole ) ); 00071 // Do not assert on invalid collections, since a collection may be deleted 00072 // in the meantime and deleted collections are invalid. 00073 if ( collection.isValid() ) { 00074 CollectionStatistics statistics = collection.statistics(); 00075 totalCount += qMax( 0LL, statistics.count() ); 00076 unreadCount += qMax( 0LL, statistics.unreadCount() ); 00077 00078 if ( index.model()->hasChildren( index ) ) { 00079 const int rowCount = index.model()->rowCount( index ); 00080 for ( int row = 0; row < rowCount; row++ ) { 00081 static const int column = 0; 00082 getCountRecursive( index.model()->index( row, column, index ), totalCount, unreadCount ); 00083 } 00084 } 00085 } 00086 } 00087 }; 00088 00089 } 00090 00091 CollectionStatisticsDelegate::CollectionStatisticsDelegate( QAbstractItemView *parent ) 00092 : QStyledItemDelegate( parent ), 00093 d_ptr( new CollectionStatisticsDelegatePrivate( parent ) ) 00094 { 00095 00096 } 00097 00098 CollectionStatisticsDelegate::CollectionStatisticsDelegate( QTreeView *parent ) 00099 : QStyledItemDelegate( parent ), 00100 d_ptr( new CollectionStatisticsDelegatePrivate( parent ) ) 00101 { 00102 00103 } 00104 00105 CollectionStatisticsDelegate::~CollectionStatisticsDelegate() 00106 { 00107 delete d_ptr; 00108 } 00109 00110 void CollectionStatisticsDelegate::setUnreadCountShown( bool enable ) 00111 { 00112 Q_D( CollectionStatisticsDelegate ); 00113 d->drawUnreadAfterFolder = enable; 00114 } 00115 00116 bool CollectionStatisticsDelegate::unreadCountShown() const 00117 { 00118 Q_D( const CollectionStatisticsDelegate ); 00119 return d->drawUnreadAfterFolder; 00120 } 00121 00122 void CollectionStatisticsDelegate::setProgressAnimationEnabled( bool enable ) 00123 { 00124 Q_D( CollectionStatisticsDelegate ); 00125 if ( enable == ( d->animator != 0 ) ) 00126 return; 00127 if ( enable ) { 00128 Q_ASSERT( !d->animator ); 00129 Akonadi::DelegateAnimator *animator = new Akonadi::DelegateAnimator( d->parent ); 00130 d->animator = animator; 00131 } else { 00132 delete d->animator; 00133 d->animator = 0; 00134 } 00135 } 00136 00137 bool CollectionStatisticsDelegate::progressAnimationEnabled() const 00138 { 00139 Q_D( const CollectionStatisticsDelegate ); 00140 return d->animator != 0; 00141 } 00142 00143 void CollectionStatisticsDelegate::initStyleOption( QStyleOptionViewItem *option, 00144 const QModelIndex &index ) const 00145 { 00146 Q_D( const CollectionStatisticsDelegate ); 00147 00148 QStyleOptionViewItemV4 *noTextOption = 00149 qstyleoption_cast<QStyleOptionViewItemV4 *>( option ); 00150 QStyledItemDelegate::initStyleOption( noTextOption, index ); 00151 if ( option->decorationPosition != QStyleOptionViewItem::Top ) { 00152 noTextOption->text.clear(); 00153 } 00154 00155 if ( d->animator ) { 00156 00157 const Akonadi::Collection collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 00158 00159 if (!collection.isValid()) 00160 { 00161 d->animator->pop(index); 00162 return; 00163 } 00164 00165 if (index.data(Akonadi::EntityTreeModel::FetchStateRole).toInt() != Akonadi::EntityTreeModel::FetchingState) 00166 { 00167 d->animator->pop(index); 00168 return; 00169 } 00170 00171 d->animator->push(index); 00172 00173 if (QStyleOptionViewItemV4 *v4 = qstyleoption_cast<QStyleOptionViewItemV4 *>(option)) { 00174 v4->icon = d->animator->sequenceFrame(index); 00175 } 00176 } 00177 } 00178 00179 class PainterStateSaver 00180 { 00181 public: 00182 PainterStateSaver( QPainter *painter ) 00183 { 00184 mPainter = painter; 00185 mPainter->save(); 00186 } 00187 00188 ~PainterStateSaver() 00189 { 00190 mPainter->restore(); 00191 } 00192 00193 private: 00194 QPainter *mPainter; 00195 }; 00196 00197 void CollectionStatisticsDelegate::paint( QPainter *painter, 00198 const QStyleOptionViewItem &option, 00199 const QModelIndex &index ) const 00200 { 00201 Q_D( const CollectionStatisticsDelegate ); 00202 PainterStateSaver stateSaver( painter ); 00203 00204 const QColor textColor = index.data( Qt::ForegroundRole ).value<QColor>(); 00205 // First, paint the basic, but without the text. We remove the text 00206 // in initStyleOption(), which gets called by QStyledItemDelegate::paint(). 00207 QStyledItemDelegate::paint( painter, option, index ); 00208 00209 // No, we retrieve the correct style option by calling intiStyleOption from 00210 // the superclass. 00211 QStyleOptionViewItemV4 option4 = option; 00212 QStyledItemDelegate::initStyleOption( &option4, index ); 00213 QString text = option4.text; 00214 00215 // Now calculate the rectangle for the text 00216 QStyle *s = d->parent->style(); 00217 const QWidget *widget = option4.widget; 00218 const QRect textRect = s->subElementRect( QStyle::SE_ItemViewItemText, &option4, widget ); 00219 const QRect iconRect = s->subElementRect( QStyle::SE_ItemViewItemDecoration, &option4, widget ); 00220 00221 // When checking if the item is expanded, we need to check that for the first 00222 // column, as Qt only recogises the index as expanded for the first column 00223 QModelIndex firstColumn = index.model()->index( index.row(), 0, index.parent() ); 00224 QTreeView* treeView = qobject_cast<QTreeView*>( d->parent ); 00225 bool expanded = treeView && treeView->isExpanded( firstColumn ); 00226 00227 if ( option.state & QStyle::State_Selected ) { 00228 painter->setPen( textColor.isValid() ? textColor : option.palette.highlightedText().color() ); 00229 } 00230 00231 Collection collection = index.sibling( index.row(), 0 ).data( EntityTreeModel::CollectionRole ).value<Collection>(); 00232 00233 Q_ASSERT(collection.isValid()); 00234 00235 CollectionStatistics statistics = collection.statistics(); 00236 00237 qint64 unreadCount = qMax( 0LL, statistics.unreadCount() ); 00238 qint64 totalRecursiveCount = 0; 00239 qint64 unreadRecursiveCount = 0; 00240 d->getCountRecursive( index.sibling( index.row(), 0 ), totalRecursiveCount, unreadRecursiveCount ); 00241 00242 // Draw the unread count after the folder name (in parenthesis) 00243 if ( d->drawUnreadAfterFolder && index.column() == 0 ) { 00244 // Construct the string which will appear after the foldername (with the 00245 // unread count) 00246 QString unread; 00247 // qDebug() << expanded << unreadCount << unreadRecursiveCount; 00248 if ( expanded && unreadCount > 0 ) 00249 unread = QString::fromLatin1( " (%1)" ).arg( unreadCount ); 00250 else if ( !expanded ) { 00251 if ( unreadCount != unreadRecursiveCount ) 00252 unread = QString::fromLatin1( " (%1 + %2)" ).arg( unreadCount ).arg( unreadRecursiveCount - unreadCount ); 00253 else if ( unreadCount > 0 ) 00254 unread = QString::fromLatin1( " (%1)" ).arg( unreadCount ); 00255 } 00256 00257 PainterStateSaver stateSaver( painter ); 00258 00259 if ( !unread.isEmpty() ) { 00260 QFont font = painter->font(); 00261 font.setBold( true ); 00262 painter->setFont( font ); 00263 } 00264 00265 const QColor unreadColor = (option.state & QStyle::State_Selected) ? d->mSelectedUnreadColor : d->mDeselectedUnreadColor; 00266 00267 if ( option.decorationPosition == QStyleOptionViewItem::Left 00268 || option.decorationPosition == QStyleOptionViewItem::Right ) { 00269 // Squeeze the folder text if it is to big and calculate the rectangles 00270 // where the folder text and the unread count will be drawn to 00271 QString folderName = text; 00272 QFontMetrics fm( painter->fontMetrics() ); 00273 int unreadWidth = fm.width( unread ); 00274 int folderWidth( fm.width( folderName ) ); 00275 if ( folderWidth + unreadWidth > textRect.width() ) { 00276 folderName = fm.elidedText( folderName, Qt::ElideRight, 00277 textRect.width() - unreadWidth ); 00278 folderWidth = fm.width( folderName ); 00279 } 00280 QRect folderRect = textRect; 00281 QRect unreadRect = textRect; 00282 folderRect.setRight( textRect.left() + folderWidth ); 00283 unreadRect.setLeft( folderRect.right() ); 00284 if ( textColor.isValid() ) 00285 painter->setPen( textColor ); 00286 00287 // Draw folder name and unread count 00288 painter->drawText( folderRect, Qt::AlignLeft | Qt::AlignVCenter, folderName ); 00289 painter->setPen( unreadColor ); 00290 painter->drawText( unreadRect, Qt::AlignLeft | Qt::AlignVCenter, unread ); 00291 } else if ( option.decorationPosition == QStyleOptionViewItem::Top ) { 00292 if ( unreadCount > 0 ) { 00293 // draw over the icon 00294 painter->setPen( unreadColor ); 00295 painter->drawText( iconRect, Qt::AlignCenter, QString::number( unreadCount ) ); 00296 } 00297 } 00298 return; 00299 } 00300 00301 // For the unread/total column, paint the summed up count if the item 00302 // is collapsed 00303 if ( ( index.column() == 1 || index.column() == 2 ) ) { 00304 00305 QFont savedFont = painter->font(); 00306 QString sumText; 00307 if ( index.column() == 1 && ( ( !expanded && unreadRecursiveCount > 0 ) || ( expanded && unreadCount > 0 ) ) ) { 00308 QFont font = painter->font(); 00309 font.setBold( true ); 00310 painter->setFont( font ); 00311 sumText = QString::number( expanded ? unreadCount : unreadRecursiveCount ); 00312 } else { 00313 00314 qint64 totalCount = statistics.count(); 00315 if (index.column() == 2 && ( ( !expanded && totalRecursiveCount > 0 ) || ( expanded && totalCount > 0 ) ) ) { 00316 sumText = QString::number( expanded ? totalCount : totalRecursiveCount ); 00317 } 00318 } 00319 00320 painter->drawText( textRect, Qt::AlignRight | Qt::AlignVCenter, sumText ); 00321 painter->setFont( savedFont ); 00322 return; 00323 } 00324 00325 if ( textColor.isValid() ) 00326 painter->setPen( textColor ); 00327 painter->drawText( textRect, option4.displayAlignment | Qt::AlignVCenter, text ); 00328 } 00329 00330 #include "collectionstatisticsdelegate.moc"