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 void getCountRecursive( const QModelIndex &index, qint64 &totalCount, qint64 &unreadCount ) const 00069 { 00070 Collection collection = qvariant_cast<Collection>( index.data( EntityTreeModel::CollectionRole ) ); 00071 Q_ASSERT( collection.isValid() ); 00072 CollectionStatistics statistics = collection.statistics(); 00073 totalCount += qMax( 0LL, statistics.count() ); 00074 unreadCount += qMax( 0LL, statistics.unreadCount() ); 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 getCountRecursive( index.model()->index( row, column, index ), totalCount, unreadCount ); 00083 } 00084 } 00085 } 00086 }; 00087 00088 } 00089 00090 CollectionStatisticsDelegate::CollectionStatisticsDelegate( QAbstractItemView *parent ) 00091 : QStyledItemDelegate( parent ), 00092 d_ptr( new CollectionStatisticsDelegatePrivate( parent ) ) 00093 { 00094 00095 } 00096 00097 CollectionStatisticsDelegate::CollectionStatisticsDelegate( QTreeView *parent ) 00098 : QStyledItemDelegate( parent ), 00099 d_ptr( new CollectionStatisticsDelegatePrivate( parent ) ) 00100 { 00101 00102 } 00103 00104 CollectionStatisticsDelegate::~CollectionStatisticsDelegate() 00105 { 00106 delete d_ptr; 00107 } 00108 00109 void CollectionStatisticsDelegate::setUnreadCountShown( bool enable ) 00110 { 00111 Q_D( CollectionStatisticsDelegate ); 00112 d->drawUnreadAfterFolder = enable; 00113 } 00114 00115 bool CollectionStatisticsDelegate::unreadCountShown() const 00116 { 00117 Q_D( const CollectionStatisticsDelegate ); 00118 return d->drawUnreadAfterFolder; 00119 } 00120 00121 void CollectionStatisticsDelegate::setProgressAnimationEnabled( bool enable ) 00122 { 00123 Q_D( CollectionStatisticsDelegate ); 00124 if ( enable == ( d->animator != 0 ) ) 00125 return; 00126 if ( enable ) { 00127 Q_ASSERT( !d->animator ); 00128 Akonadi::DelegateAnimator *animator = new Akonadi::DelegateAnimator( d->parent ); 00129 d->animator = animator; 00130 } else { 00131 delete d->animator; 00132 d->animator = 0; 00133 } 00134 } 00135 00136 bool CollectionStatisticsDelegate::progressAnimationEnabled() const 00137 { 00138 Q_D( const CollectionStatisticsDelegate ); 00139 return d->animator != 0; 00140 } 00141 00142 void CollectionStatisticsDelegate::initStyleOption( QStyleOptionViewItem *option, 00143 const QModelIndex &index ) const 00144 { 00145 Q_D( const CollectionStatisticsDelegate ); 00146 00147 QStyleOptionViewItemV4 *noTextOption = 00148 qstyleoption_cast<QStyleOptionViewItemV4 *>( option ); 00149 QStyledItemDelegate::initStyleOption( noTextOption, index ); 00150 if ( option->decorationPosition != QStyleOptionViewItem::Top ) { 00151 noTextOption->text.clear(); 00152 } 00153 00154 if ( d->animator ) { 00155 00156 const Akonadi::Collection collection = index.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 00157 00158 if (!collection.isValid()) 00159 { 00160 d->animator->pop(index); 00161 return; 00162 } 00163 00164 if (index.data(Akonadi::EntityTreeModel::FetchStateRole).toInt() != Akonadi::EntityTreeModel::FetchingState) 00165 { 00166 d->animator->pop(index); 00167 return; 00168 } 00169 00170 d->animator->push(index); 00171 00172 if (QStyleOptionViewItemV4 *v4 = qstyleoption_cast<QStyleOptionViewItemV4 *>(option)) { 00173 v4->icon = d->animator->sequenceFrame(index); 00174 } 00175 } 00176 } 00177 00178 class PainterStateSaver 00179 { 00180 public: 00181 PainterStateSaver( QPainter *painter ) 00182 { 00183 mPainter = painter; 00184 mPainter->save(); 00185 } 00186 00187 ~PainterStateSaver() 00188 { 00189 mPainter->restore(); 00190 } 00191 00192 private: 00193 QPainter *mPainter; 00194 }; 00195 00196 void CollectionStatisticsDelegate::paint( QPainter *painter, 00197 const QStyleOptionViewItem &option, 00198 const QModelIndex &index ) const 00199 { 00200 Q_D( const CollectionStatisticsDelegate ); 00201 PainterStateSaver stateSaver( painter ); 00202 00203 // First, paint the basic, but without the text. We remove the text 00204 // in initStyleOption(), which gets called by QStyledItemDelegate::paint(). 00205 QStyledItemDelegate::paint( painter, option, index ); 00206 00207 // No, we retrieve the correct style option by calling intiStyleOption from 00208 // the superclass. 00209 QStyleOptionViewItemV4 option4 = option; 00210 QStyledItemDelegate::initStyleOption( &option4, index ); 00211 QString text = option4.text; 00212 00213 // Now calculate the rectangle for the text 00214 QStyle *s = d->parent->style(); 00215 const QWidget *widget = option4.widget; 00216 const QRect textRect = s->subElementRect( QStyle::SE_ItemViewItemText, &option4, widget ); 00217 const QRect iconRect = s->subElementRect( QStyle::SE_ItemViewItemDecoration, &option4, widget ); 00218 00219 // When checking if the item is expanded, we need to check that for the first 00220 // column, as Qt only recogises the index as expanded for the first column 00221 QModelIndex firstColumn = index.model()->index( index.row(), 0, index.parent() ); 00222 QTreeView* treeView = qobject_cast<QTreeView*>( d->parent ); 00223 bool expanded = treeView && treeView->isExpanded( firstColumn ); 00224 00225 if ( option.state & QStyle::State_Selected ) { 00226 painter->setPen( option.palette.highlightedText().color() ); 00227 } 00228 00229 Collection collection = index.sibling( index.row(), 0 ).data( EntityTreeModel::CollectionRole ).value<Collection>(); 00230 00231 Q_ASSERT(collection.isValid()); 00232 00233 CollectionStatistics statistics = collection.statistics(); 00234 00235 qint64 unreadCount = qMax( 0LL, statistics.unreadCount() ); 00236 qint64 totalRecursiveCount = 0; 00237 qint64 unreadRecursiveCount = 0; 00238 d->getCountRecursive( index.sibling( index.row(), 0 ), totalRecursiveCount, unreadRecursiveCount ); 00239 00240 // Draw the unread count after the folder name (in parenthesis) 00241 if ( d->drawUnreadAfterFolder && index.column() == 0 ) { 00242 // Construct the string which will appear after the foldername (with the 00243 // unread count) 00244 QString unread; 00245 // qDebug() << expanded << unreadCount << unreadRecursiveCount; 00246 if ( expanded && unreadCount > 0 ) 00247 unread = QString( QLatin1String( " (%1)" ) ).arg( unreadCount ); 00248 else if ( !expanded ) { 00249 if ( unreadCount != unreadRecursiveCount ) 00250 unread = QString( QLatin1String( " (%1 + %2)" ) ).arg( unreadCount ).arg( unreadRecursiveCount - unreadCount ); 00251 else if ( unreadCount > 0 ) 00252 unread = QString( QLatin1String( " (%1)" ) ).arg( unreadCount ); 00253 } 00254 00255 PainterStateSaver stateSaver( painter ); 00256 00257 if ( !unread.isEmpty() ) { 00258 QFont font = painter->font(); 00259 font.setBold( true ); 00260 painter->setFont( font ); 00261 } 00262 00263 const QColor unreadColor = (option.state & QStyle::State_Selected) ? d->mSelectedUnreadColor : d->mDeselectedUnreadColor; 00264 00265 if ( option.decorationPosition == QStyleOptionViewItem::Left 00266 || option.decorationPosition == QStyleOptionViewItem::Right ) { 00267 // Squeeze the folder text if it is to big and calculate the rectangles 00268 // where the folder text and the unread count will be drawn to 00269 QString folderName = text; 00270 QFontMetrics fm( painter->fontMetrics() ); 00271 int unreadWidth = fm.width( unread ); 00272 if ( fm.width( folderName ) + unreadWidth > textRect.width() ) { 00273 folderName = fm.elidedText( folderName, Qt::ElideRight, 00274 textRect.width() - unreadWidth ); 00275 } 00276 int folderWidth = fm.width( folderName ); 00277 QRect folderRect = textRect; 00278 QRect unreadRect = textRect; 00279 folderRect.setRight( textRect.left() + folderWidth ); 00280 unreadRect.setLeft( folderRect.right() ); 00281 00282 // Draw folder name and unread count 00283 painter->drawText( folderRect, Qt::AlignLeft | Qt::AlignVCenter, folderName ); 00284 painter->setPen( unreadColor ); 00285 painter->drawText( unreadRect, Qt::AlignLeft | Qt::AlignVCenter, unread ); 00286 } else if ( option.decorationPosition == QStyleOptionViewItem::Top ) { 00287 // draw over the icon 00288 painter->setPen( unreadColor ); 00289 if ( unreadCount > 0 ) { 00290 painter->drawText( iconRect, Qt::AlignCenter, QString::number( unreadCount ) ); 00291 } 00292 } 00293 return; 00294 } 00295 00296 // For the unread/total column, paint the summed up count if the item 00297 // is collapsed 00298 if ( ( index.column() == 1 || index.column() == 2 ) ) { 00299 00300 PainterStateSaver stateSaver( painter ); 00301 00302 QString sumText; 00303 if ( index.column() == 1 && ( ( !expanded && unreadRecursiveCount > 0 ) || ( expanded && unreadCount > 0 ) ) ) { 00304 QFont font = painter->font(); 00305 font.setBold( true ); 00306 painter->setFont( font ); 00307 sumText = QString::number( expanded ? unreadCount : unreadRecursiveCount ); 00308 } else { 00309 00310 qint64 totalCount = statistics.count(); 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 | Qt::AlignVCenter, sumText ); 00317 00318 return; 00319 } 00320 00321 painter->drawText( textRect, option4.displayAlignment, text ); 00322 } 00323 00324 #include "collectionstatisticsdelegate.moc"