kde-playground/kdepimlibs/akonadi/collectionstatisticsdelegate.cpp
2015-04-14 21:49:29 +00:00

364 lines
13 KiB
C++

/*
Copyright (c) 2008 Thomas McGuire <thomas.mcguire@gmx.net>
Copyright (c) 2012 Laurent Montel <montel@kde.org>
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include "collectionstatisticsdelegate.h"
#include "collectionstatisticsmodel.h"
#include <kcolorscheme.h>
#include <kdebug.h>
#include <kio/global.h>
#include <QPainter>
#include <QStyle>
#include <QStyleOption>
#include <QStyleOptionViewItemV4>
#include <QAbstractItemView>
#include <QTreeView>
#include "entitytreemodel.h"
#include "collectionstatistics.h"
#include "collection.h"
#include "progressspinnerdelegate_p.h"
using namespace Akonadi;
namespace Akonadi {
enum CountType {
UnreadCount,
TotalCount
};
class CollectionStatisticsDelegatePrivate
{
public:
QAbstractItemView *parent;
bool drawUnreadAfterFolder;
DelegateAnimator *animator;
QColor mSelectedUnreadColor;
QColor mDeselectedUnreadColor;
CollectionStatisticsDelegatePrivate(QAbstractItemView *treeView)
: parent(treeView)
, drawUnreadAfterFolder(false)
, animator(0)
{
updateColor();
}
void getCountRecursive(const QModelIndex &index, qint64 &totalCount, qint64 &unreadCount, qint64 &totalSize) const
{
Collection collection = qvariant_cast<Collection>(index.data(EntityTreeModel::CollectionRole));
// Do not assert on invalid collections, since a collection may be deleted
// in the meantime and deleted collections are invalid.
if (collection.isValid()) {
CollectionStatistics statistics = collection.statistics();
totalCount += qMax(0LL, statistics.count());
unreadCount += qMax(0LL, statistics.unreadCount());
totalSize += qMax(0LL, statistics.size());
if (index.model()->hasChildren(index)) {
const int rowCount = index.model()->rowCount(index);
for (int row = 0; row < rowCount; row++) {
static const int column = 0;
getCountRecursive(index.model()->index(row, column, index), totalCount, unreadCount, totalSize);
}
}
}
}
void updateColor()
{
mSelectedUnreadColor = KColorScheme(QPalette::Active, KColorScheme::Selection)
.foreground(KColorScheme::LinkText).color();
mDeselectedUnreadColor = KColorScheme(QPalette::Active, KColorScheme::View)
.foreground(KColorScheme::LinkText).color();
}
};
}
CollectionStatisticsDelegate::CollectionStatisticsDelegate(QAbstractItemView *parent)
: QStyledItemDelegate(parent)
, d_ptr(new CollectionStatisticsDelegatePrivate(parent))
{
}
CollectionStatisticsDelegate::CollectionStatisticsDelegate(QTreeView *parent)
: QStyledItemDelegate(parent)
, d_ptr(new CollectionStatisticsDelegatePrivate(parent))
{
}
CollectionStatisticsDelegate::~CollectionStatisticsDelegate()
{
delete d_ptr;
}
void CollectionStatisticsDelegate::setUnreadCountShown(bool enable)
{
Q_D(CollectionStatisticsDelegate);
d->drawUnreadAfterFolder = enable;
}
bool CollectionStatisticsDelegate::unreadCountShown() const
{
Q_D(const CollectionStatisticsDelegate);
return d->drawUnreadAfterFolder;
}
void CollectionStatisticsDelegate::setProgressAnimationEnabled(bool enable)
{
Q_D(CollectionStatisticsDelegate);
if (enable == (d->animator != 0)) {
return;
}
if (enable) {
Q_ASSERT(!d->animator);
Akonadi::DelegateAnimator *animator = new Akonadi::DelegateAnimator(d->parent);
d->animator = animator;
} else {
delete d->animator;
d->animator = 0;
}
}
bool CollectionStatisticsDelegate::progressAnimationEnabled() const
{
Q_D(const CollectionStatisticsDelegate);
return d->animator != 0;
}
void CollectionStatisticsDelegate::initStyleOption(QStyleOptionViewItem *option,
const QModelIndex &index) const
{
Q_D(const CollectionStatisticsDelegate);
QStyleOptionViewItemV4 *noTextOption =
qstyleoption_cast<QStyleOptionViewItemV4 *>(option);
QStyledItemDelegate::initStyleOption(noTextOption, index);
if (option->decorationPosition != QStyleOptionViewItem::Top) {
noTextOption->text.clear();
}
if (d->animator) {
const QVariant fetchState = index.data(Akonadi::EntityTreeModel::FetchStateRole);
if (!fetchState.isValid() || fetchState.toInt() != Akonadi::EntityTreeModel::FetchingState) {
d->animator->pop(index);
return;
}
d->animator->push(index);
if (QStyleOptionViewItemV4 *v4 = qstyleoption_cast<QStyleOptionViewItemV4 *>(option)) {
v4->icon = d->animator->sequenceFrame(index);
}
}
}
class PainterStateSaver
{
public:
PainterStateSaver(QPainter *painter)
{
mPainter = painter;
mPainter->save();
}
~PainterStateSaver()
{
mPainter->restore();
}
private:
QPainter *mPainter;
};
void CollectionStatisticsDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
Q_D(const CollectionStatisticsDelegate);
PainterStateSaver stateSaver(painter);
const QColor textColor = index.data(Qt::ForegroundRole).value<QColor>();
// First, paint the basic, but without the text. We remove the text
// in initStyleOption(), which gets called by QStyledItemDelegate::paint().
QStyledItemDelegate::paint(painter, option, index);
// No, we retrieve the correct style option by calling intiStyleOption from
// the superclass.
QStyleOptionViewItemV4 option4 = option;
QStyledItemDelegate::initStyleOption(&option4, index);
QString text = option4.text;
// Now calculate the rectangle for the text
QStyle *s = d->parent->style();
const QWidget *widget = option4.widget;
const QRect textRect = s->subElementRect(QStyle::SE_ItemViewItemText, &option4, widget);
// When checking if the item is expanded, we need to check that for the first
// column, as Qt only recognises the index as expanded for the first column
const QModelIndex firstColumn = index.sibling(index.row(), 0);
QTreeView *treeView = qobject_cast<QTreeView *>(d->parent);
bool expanded = treeView && treeView->isExpanded(firstColumn);
if (option.state & QStyle::State_Selected) {
painter->setPen(textColor.isValid() ? textColor : option.palette.highlightedText().color());
}
Collection collection = firstColumn.data(EntityTreeModel::CollectionRole).value<Collection>();
if (!collection.isValid()) {
kError() << "Invalid collection: " << collection;
}
Q_ASSERT(collection.isValid()); // TODO: I seem to hit this when removing a duplicated "Personal Contacts" or "Personal Calendar"
CollectionStatistics statistics = collection.statistics();
qint64 unreadCount = qMax(0LL, statistics.unreadCount());
qint64 totalRecursiveCount = 0;
qint64 unreadRecursiveCount = 0;
qint64 totalSize = 0;
bool needRecursiveCounts = false;
bool needTotalSize = false;
if (d->drawUnreadAfterFolder && index.column() == 0) {
needRecursiveCounts = true;
} else if ((index.column() == 1 || index.column() == 2)) {
needRecursiveCounts = true;
} else if (index.column() == 3 && !expanded) {
needTotalSize = true;
}
if (needRecursiveCounts || needTotalSize) {
d->getCountRecursive(firstColumn, totalRecursiveCount, unreadRecursiveCount, totalSize);
}
// Draw the unread count after the folder name (in parenthesis)
if (d->drawUnreadAfterFolder && index.column() == 0) {
// Construct the string which will appear after the foldername (with the
// unread count)
QString unread;
// qDebug() << expanded << unreadCount << unreadRecursiveCount;
if (expanded && unreadCount > 0) {
unread = QString::fromLatin1(" (%1)").arg(unreadCount);
} else if (!expanded) {
if (unreadCount != unreadRecursiveCount) {
unread = QString::fromLatin1(" (%1 + %2)").arg(unreadCount).arg(unreadRecursiveCount - unreadCount);
} else if (unreadCount > 0) {
unread = QString::fromLatin1(" (%1)").arg(unreadCount);
}
}
PainterStateSaver stateSaver(painter);
if (!unread.isEmpty()) {
QFont font = painter->font();
font.setBold(true);
painter->setFont(font);
}
const QColor unreadColor = (option.state & QStyle::State_Selected) ? d->mSelectedUnreadColor : d->mDeselectedUnreadColor;
const QRect iconRect = s->subElementRect(QStyle::SE_ItemViewItemDecoration, &option4, widget);
if (option.decorationPosition == QStyleOptionViewItem::Left ||
option.decorationPosition == QStyleOptionViewItem::Right) {
// Squeeze the folder text if it is to big and calculate the rectangles
// where the folder text and the unread count will be drawn to
QString folderName = text;
QFontMetrics fm(painter->fontMetrics());
const int unreadWidth = fm.width(unread);
int folderWidth(fm.width(folderName));
const bool enoughPlaceForText = (option.rect.width() > (folderWidth + unreadWidth + iconRect.width()));
if (!enoughPlaceForText && (folderWidth + unreadWidth > textRect.width())) {
folderName = fm.elidedText(folderName, Qt::ElideRight,
option.rect.width() - unreadWidth - iconRect.width());
folderWidth = fm.width(folderName);
}
QRect folderRect = textRect;
QRect unreadRect = textRect;
folderRect.setRight(textRect.left() + folderWidth);
unreadRect = QRect(folderRect.right(), folderRect.top(), unreadRect.width(), unreadRect.height());
if (textColor.isValid()) {
painter->setPen(textColor);
}
// Draw folder name and unread count
painter->drawText(folderRect, Qt::AlignLeft | Qt::AlignVCenter, folderName);
painter->setPen(unreadColor);
painter->drawText(unreadRect, Qt::AlignLeft | Qt::AlignVCenter, unread);
} else if (option.decorationPosition == QStyleOptionViewItem::Top) {
if (unreadCount > 0) {
// draw over the icon
painter->setPen(unreadColor);
painter->drawText(iconRect, Qt::AlignCenter, QString::number(unreadCount));
}
}
return;
}
// For the unread/total column, paint the summed up count if the item
// is collapsed
if ((index.column() == 1 || index.column() == 2)) {
QFont savedFont = painter->font();
QString sumText;
if (index.column() == 1 && ((!expanded && unreadRecursiveCount > 0) || (expanded && unreadCount > 0))) {
QFont font = painter->font();
font.setBold(true);
painter->setFont(font);
sumText = QString::number(expanded ? unreadCount : unreadRecursiveCount);
} else {
qint64 totalCount = statistics.count();
if (index.column() == 2 && ((!expanded && totalRecursiveCount > 0) || (expanded && totalCount > 0))) {
sumText = QString::number(expanded ? totalCount : totalRecursiveCount);
}
}
painter->drawText(textRect, Qt::AlignRight | Qt::AlignVCenter, sumText);
painter->setFont(savedFont);
return;
}
//total size
if (index.column() == 3 && !expanded) {
if (textColor.isValid()) {
painter->setPen(textColor);
}
painter->drawText(textRect, option4.displayAlignment | Qt::AlignVCenter, KIO::convertSize((KIO::filesize_t)totalSize));
return;
}
if (textColor.isValid()) {
painter->setPen(textColor);
}
painter->drawText(textRect, option4.displayAlignment | Qt::AlignVCenter, text);
}
void CollectionStatisticsDelegate::updatePalette()
{
Q_D(CollectionStatisticsDelegate);
d->updateColor();
}