akonadi
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 template<CountType countType> 00069 qint64 getCountRecursive( const QModelIndex &index ) const 00070 { 00071 Collection collection = qvariant_cast<Collection>( index.data( EntityTreeModel::CollectionRole ) ); 00072 Q_ASSERT( collection.isValid() ); 00073 CollectionStatistics statistics = collection.statistics(); 00074 qint64 count = qMax( 0LL, countType == UnreadCount ? statistics.unreadCount() : statistics.count() ); 00075 00076 if ( index.model()->hasChildren( index ) ) 00077 { 00078 const int rowCount = index.model()->rowCount( index ); 00079 for ( int row = 0; row < rowCount; row++ ) 00080 { 00081 static const int column = 0; 00082 count += getCountRecursive<countType>( index.model()->index( row, column, index ) ); 00083 } 00084 } 00085 return count; 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 // First, paint the basic, but without the text. We remove the text 00205 // in initStyleOption(), which gets called by QStyledItemDelegate::paint(). 00206 QStyledItemDelegate::paint( painter, option, index ); 00207 00208 // No, we retrieve the correct style option by calling intiStyleOption from 00209 // the superclass. 00210 QStyleOptionViewItemV4 option4 = option; 00211 QStyledItemDelegate::initStyleOption( &option4, index ); 00212 QString text = option4.text; 00213 00214 // Now calculate the rectangle for the text 00215 QStyle *s = d->parent->style(); 00216 const QWidget *widget = option4.widget; 00217 const QRect textRect = s->subElementRect( QStyle::SE_ItemViewItemText, &option4, widget ); 00218 const QRect iconRect = s->subElementRect( QStyle::SE_ItemViewItemDecoration, &option4, widget ); 00219 00220 // When checking if the item is expanded, we need to check that for the first 00221 // column, as Qt only recogises the index as expanded for the first column 00222 QModelIndex firstColumn = index.model()->index( index.row(), 0, index.parent() ); 00223 QTreeView* treeView = qobject_cast<QTreeView*>( d->parent ); 00224 bool expanded = treeView && treeView->isExpanded( firstColumn ); 00225 00226 if ( option.state & QStyle::State_Selected ) { 00227 painter->setPen( option.palette.highlightedText().color() ); 00228 } 00229 00230 Collection collection = index.sibling( index.row(), 0 ).data( EntityTreeModel::CollectionRole ).value<Collection>(); 00231 00232 Q_ASSERT(collection.isValid()); 00233 00234 CollectionStatistics statistics = collection.statistics(); 00235 00236 qint64 unreadCount = qMax( 0LL, statistics.unreadCount() ); 00237 qint64 unreadRecursiveCount = d->getCountRecursive<UnreadCount>( index.sibling( index.row(), 0 ) ); 00238 00239 // Draw the unread count after the folder name (in parenthesis) 00240 if ( d->drawUnreadAfterFolder && index.column() == 0 ) { 00241 // Construct the string which will appear after the foldername (with the 00242 // unread count) 00243 QString unread; 00244 // qDebug() << expanded << unreadCount << unreadRecursiveCount; 00245 if ( expanded && unreadCount > 0 ) 00246 unread = QString( QLatin1String( " (%1)" ) ).arg( unreadCount ); 00247 else if ( !expanded ) { 00248 if ( unreadCount != unreadRecursiveCount ) 00249 unread = QString( QLatin1String( " (%1 + %2)" ) ).arg( unreadCount ).arg( unreadRecursiveCount - unreadCount ); 00250 else if ( unreadCount > 0 ) 00251 unread = QString( QLatin1String( " (%1)" ) ).arg( unreadCount ); 00252 } 00253 00254 PainterStateSaver stateSaver( painter ); 00255 00256 if ( !unread.isEmpty() ) { 00257 QFont font = painter->font(); 00258 font.setBold( true ); 00259 painter->setFont( font ); 00260 } 00261 00262 const QColor unreadColor = (option.state & QStyle::State_Selected) ? d->mSelectedUnreadColor : d->mDeselectedUnreadColor; 00263 00264 if ( option.decorationPosition == QStyleOptionViewItem::Left 00265 || option.decorationPosition == QStyleOptionViewItem::Right ) { 00266 // Squeeze the folder text if it is to big and calculate the rectangles 00267 // where the folder text and the unread count will be drawn to 00268 QString folderName = text; 00269 QFontMetrics fm( painter->fontMetrics() ); 00270 int unreadWidth = fm.width( unread ); 00271 if ( fm.width( folderName ) + unreadWidth > textRect.width() ) { 00272 folderName = fm.elidedText( folderName, Qt::ElideRight, 00273 textRect.width() - unreadWidth ); 00274 } 00275 int folderWidth = fm.width( folderName ); 00276 QRect folderRect = textRect; 00277 QRect unreadRect = textRect; 00278 folderRect.setRight( textRect.left() + folderWidth ); 00279 unreadRect.setLeft( folderRect.right() ); 00280 00281 // Draw folder name and unread count 00282 painter->drawText( folderRect, Qt::AlignLeft, folderName ); 00283 painter->setPen( unreadColor ); 00284 painter->drawText( unreadRect, Qt::AlignLeft, unread ); 00285 } else if ( option.decorationPosition == QStyleOptionViewItem::Top ) { 00286 // draw over the icon 00287 painter->setPen( unreadColor ); 00288 if ( unreadCount > 0 ) { 00289 painter->drawText( iconRect, Qt::AlignCenter, QString::number( unreadCount ) ); 00290 } 00291 } 00292 return; 00293 } 00294 00295 // For the unread/total column, paint the summed up count if the item 00296 // is collapsed 00297 if ( ( index.column() == 1 || index.column() == 2 ) ) { 00298 00299 PainterStateSaver stateSaver( painter ); 00300 00301 QString sumText; 00302 if ( index.column() == 1 && ( ( !expanded && unreadRecursiveCount > 0 ) || ( expanded && unreadCount > 0 ) ) ) { 00303 QFont font = painter->font(); 00304 font.setBold( true ); 00305 painter->setFont( font ); 00306 sumText = QString::number( expanded ? unreadCount : unreadRecursiveCount ); 00307 } else { 00308 00309 qint64 totalCount = statistics.count(); 00310 qint64 totalRecursiveCount = d->getCountRecursive<TotalCount>( index.sibling( index.row(), 0 ) ); 00311 if (index.column() == 2 && ( ( !expanded && totalRecursiveCount > 0 ) || ( expanded && totalCount > 0 ) ) ) { 00312 sumText = QString::number( expanded ? totalCount : totalRecursiveCount ); 00313 } 00314 } 00315 00316 painter->drawText( textRect, Qt::AlignRight, sumText ); 00317 00318 return; 00319 } 00320 00321 painter->drawText( textRect, option4.displayAlignment, text ); 00322 } 00323 00324 #include "collectionstatisticsdelegate.moc"