Commit 800c78c2 authored by Thomas McGuire's avatar Thomas McGuire
Browse files

Add a new class MessageCollectionDelegate, which can paint the sum

of the unread/total count behind the foldername and which can show
the unread/total count of all children.

Adopt the collectionmodel and the messagecollectionmodel for that.

svn path=/trunk/KDE/kdepimlibs/; revision=789125
parent ee0281cb
......@@ -103,6 +103,7 @@ set( akonadikde_LIB_SRC
itemsync.cpp
itemview.cpp
job.cpp
messagecollectiondelegate.cpp
messagecollectionmodel.cpp
monitor.cpp
monitor_p.cpp
......@@ -195,6 +196,7 @@ install( FILES
itemsync.h
itemview.h
job.h
messagecollectiondelegate.h
messagecollectionmodel.h
monitor.h
profilemanager.h
......
......@@ -26,12 +26,11 @@
#include "session.h"
#include <kdebug.h>
#include <kiconloader.h>
#include <klocale.h>
#include <kurl.h>
#include <kiconloader.h>
#include <QtCore/QMimeData>
#include <QtCore/QTimer>
#include <QtGui/QPixmap>
using namespace Akonadi;
......@@ -41,17 +40,15 @@ CollectionModel::CollectionModel( QObject * parent ) :
d_ptr( new CollectionModelPrivate( this ) )
{
Q_D( CollectionModel );
d->session = new Session( QByteArray("CollectionModel-") + QByteArray::number( qrand() ), this );
QTimer::singleShot( 0, this, SLOT(init()) );
// monitor collection changes
d->monitor = new Monitor();
d->monitor->monitorCollection( Collection::root() );
d->monitor->fetchCollection( true );
d->init();
}
// ### Hack to get the kmail resource folder icons
KIconLoader::global()->addAppDir( QLatin1String( "kmail" ) );
KIconLoader::global()->addAppDir( QLatin1String( "kdepim" ) );
CollectionModel::CollectionModel( CollectionModelPrivate *d,
QObject *parent )
: QAbstractItemModel( parent ),
d_ptr( d )
{
d->init();
}
CollectionModel::~CollectionModel()
......
......@@ -95,12 +95,12 @@ class AKONADI_EXPORT CollectionModel : public QAbstractItemModel
/**
Reimplemented from QAbstractItemModel.
*/
bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole );
virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole );
/**
Reimplemented from QAbstractItemModel.
*/
Qt::ItemFlags flags( const QModelIndex &index ) const;
virtual Qt::ItemFlags flags( const QModelIndex &index ) const;
/**
Reimplemented from QAbstractItemModel.
......@@ -148,6 +148,8 @@ class AKONADI_EXPORT CollectionModel : public QAbstractItemModel
Akonadi::CollectionModelPrivate *d_ptr;
explicit CollectionModel( CollectionModelPrivate *d, QObject *parent = 0 );
private:
/**
Helper function to generate a model index for a given collection reference.
......@@ -177,7 +179,7 @@ class AKONADI_EXPORT CollectionModel : public QAbstractItemModel
private:
Q_DECLARE_PRIVATE( CollectionModel )
Q_PRIVATE_SLOT( d_func(), void init() )
Q_PRIVATE_SLOT( d_func(), void startFirstListJob() )
Q_PRIVATE_SLOT( d_func(), void collectionRemoved( const Akonadi::Collection& ) )
Q_PRIVATE_SLOT( d_func(), void collectionChanged( const Akonadi::Collection& ) )
Q_PRIVATE_SLOT( d_func(), void updateDone( KJob* ) )
......
......@@ -29,8 +29,10 @@
#include <kdebug.h>
#include <kjob.h>
#include <kiconloader.h>
#include <QtCore/QModelIndex>
#include <QtCore/QTimer>
using namespace Akonadi;
......@@ -185,12 +187,17 @@ void CollectionModelPrivate::init()
{
Q_Q( CollectionModel );
// start a list job
CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive, session );
job->includeUnsubscribed( unsubscribed );
q->connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)),
q, SLOT(collectionsChanged(Akonadi::Collection::List)) );
q->connect( job, SIGNAL(result(KJob*)), q, SLOT(listDone(KJob*)) );
session = new Session( QByteArray("CollectionModel-") + QByteArray::number( qrand() ), q );
QTimer::singleShot( 0, q, SLOT(startFirstListJob()) );
// monitor collection changes
monitor = new Monitor();
monitor->monitorCollection( Collection::root() );
monitor->fetchCollection( true );
// ### Hack to get the kmail resource folder icons
KIconLoader::global()->addAppDir( QLatin1String( "kmail" ) );
KIconLoader::global()->addAppDir( QLatin1String( "kdepim" ) );
// monitor collection changes
q->connect( monitor, SIGNAL(collectionChanged(const Akonadi::Collection&)),
......@@ -202,3 +209,15 @@ void CollectionModelPrivate::init()
q->connect( monitor, SIGNAL(collectionStatusChanged(Akonadi::Collection::Id,Akonadi::CollectionStatus)),
q, SLOT(collectionStatusChanged(Akonadi::Collection::Id,Akonadi::CollectionStatus)) );
}
void CollectionModelPrivate::startFirstListJob()
{
Q_Q( CollectionModel );
// start a list job
CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive, session );
job->includeUnsubscribed( unsubscribed );
q->connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)),
q, SLOT(collectionsChanged(Akonadi::Collection::List)) );
q->connect( job, SIGNAL(result(KJob*)), q, SLOT(listDone(KJob*)) );
}
......@@ -47,6 +47,8 @@ class CollectionModelPrivate
{
}
virtual ~CollectionModelPrivate() {}
CollectionModel *q_ptr;
QHash<Collection::Id, Collection> collections;
QHash<Collection::Id, QList<Collection::Id> > childCollections;
......@@ -57,6 +59,7 @@ class CollectionModelPrivate
bool unsubscribed;
void init();
void startFirstListJob();
void collectionRemoved( const Akonadi::Collection& );
void collectionChanged( const Akonadi::Collection& );
void updateDone( KJob* );
......
/*
Copyright (c) 2008 Thomas McGuire <thomas.mcguire@gmx.net>
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 "messagecollectiondelegate.h"
#include "messagecollectionmodel.h"
#include <kcolorscheme.h>
#include <kdebug.h>
#include <QPainter>
#include <QTreeView>
using namespace Akonadi;
namespace Akonadi {
class MessageCollectionDelegatePrivate
{
public:
QModelIndex index;
QTreeView *parent;
bool drawUnreadAfterFolder;
QColor unreadColor;
MessageCollectionDelegatePrivate( QTreeView *treeView )
: parent( treeView ), drawUnreadAfterFolder( false )
{
unreadColor = KColorScheme( QPalette::Active ).
foreground( KColorScheme::LinkText ).color();
}
};
}
MessageCollectionDelegate::MessageCollectionDelegate( QTreeView *parent )
: QItemDelegate( parent ),
d_ptr( new MessageCollectionDelegatePrivate( parent ) )
{
}
MessageCollectionDelegate::~MessageCollectionDelegate()
{
delete d_ptr;
d_ptr = 0;
}
void MessageCollectionDelegate::setUnreadColor( const QColor &color )
{
Q_D( MessageCollectionDelegate );
d->unreadColor = color;
}
void MessageCollectionDelegate::toggleUnreadAfterFolderName( bool enable )
{
Q_D( MessageCollectionDelegate );
d->drawUnreadAfterFolder = enable;
}
void MessageCollectionDelegate::paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
d_ptr->index = index;
QItemDelegate::paint( painter, option, index );
}
void MessageCollectionDelegate::drawDisplay( QPainter *painter,
const QStyleOptionViewItem &option,
const QRect &rect,
const QString &text) const
{
const Q_D( MessageCollectionDelegate );
// When checking if the item is expanded, we need to check that for the first
// column, as Qt only recogises the index as expanded for the first column
QModelIndex firstColumn = d->index.model()->index( d->index.row(), 0,
d->index.parent() );
bool expanded = d->parent->isExpanded( firstColumn );
// Draw the unread count after the folder name (in parenthesis)
if ( d->drawUnreadAfterFolder && d->index.column() == 0 ) {
QVariant unreadCount = d->index.model()->data( d->index,
MessageCollectionModel::MessageCollectionUnreadRole );
QVariant unreadRecursiveCount = d->index.model()->data( d->index,
MessageCollectionModel::MessageCollectionUnreadRecursiveRole );
Q_ASSERT( unreadCount.type() == QVariant::LongLong );
Q_ASSERT( unreadRecursiveCount.type() == QVariant::LongLong );
// Construct the string which will appear after the foldername (with the
// unread count)
QString unread;
QString unreadCountInChilds = QString::number( unreadRecursiveCount.toLongLong() -
unreadCount.toLongLong() );
if ( expanded && unreadCount.toLongLong() > 0 )
unread = QString( QLatin1String(" (%1)") ).arg( unreadCount.toLongLong() );
else if ( !expanded ) {
if ( unreadCount.toLongLong() != unreadRecursiveCount.toLongLong() )
unread = QString( QLatin1String(" (%1 + %2)") ).arg( unreadCount.toString(),
unreadCountInChilds );
else if ( unreadCount.toLongLong() > 0 )
unread = QString( QLatin1String(" (%1)") ).arg( unreadCount.toString() );
}
painter->save();
if ( !unread.isEmpty() ) {
QFont font = painter->font();
font.setBold( true );
painter->setFont( font );
}
// 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() );
int unreadWidth = fm.width( unread );
if ( fm.width( folderName ) + unreadWidth > rect.width() ) {
folderName = fm.elidedText( folderName, Qt::ElideRight,
rect.width() - unreadWidth );
}
int folderWidth = fm.width( folderName );
QRect folderRect = rect;
QRect unreadRect = rect;
folderRect.setRight( rect.left() + folderWidth );
unreadRect.setLeft( folderRect.right() );
// Draw folder name and unread count
painter->drawText( folderRect, Qt::AlignLeft, folderName );
painter->setPen( d->unreadColor );
painter->drawText( unreadRect, Qt::AlignLeft, unread );
painter->restore();
return;
}
// For the unread/total column, paint the summed up count if the item
// is collapsed
if ( ( d->index.column() == 1 || d->index.column() == 2 ) ) {
painter->save();
int role;
if ( d->index.column() == 1 ) {
if ( !expanded )
role = MessageCollectionModel::MessageCollectionUnreadRecursiveRole;
else
role = MessageCollectionModel::MessageCollectionUnreadRole;
}
else if ( d->index.column() == 2 ) {
if ( !expanded )
role = MessageCollectionModel::MessageCollectionTotalRecursiveRole;
else
role = MessageCollectionModel::MessageCollectionTotalRole;
}
QVariant sum = d->index.model()->data( d->index, role );
Q_ASSERT( sum.type() == QVariant::LongLong );
QStyleOptionViewItem opt = option;
if ( d->index.column() == 1 && sum.toLongLong() > 0 ) {
opt.font.setBold( true );
}
QString sumText;
if ( sum.toLongLong() <= 0 )
sumText = text;
else
sumText = sum.toString();
QItemDelegate::drawDisplay( painter, opt, rect, sumText );
painter->restore();
return;
}
QItemDelegate::drawDisplay( painter, option, rect, text );
}
/*
Copyright (c) 2008 Thomas McGuire <thomas.mcguire@gmx.net>
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.
*/
#ifndef AKONADI_MESSAGECOLLECTIONDELEGATE_H
#define AKONADI_MESSAGECOLLECTIONDELEGATE_H
#include "akonadi_export.h"
#include <QtGui/QItemDelegate>
class QTreeView;
namespace Akonadi {
class MessageCollectionDelegatePrivate;
/**
* The MessageCollectionDelegate draws the unread and total count of the
* underlying MessageCollectionModel in a special way:
*
* <li>Collections with unread items will have the foldername and the unread
* column marked in bold.
* <li>If a folder is collapsed, the unread and the total column will contain
* the total sum of all child folders
* <li>It has the possibility to draw the unread count directly after the
* foldername, see toggleUnreadAfterFolderName().
*/
class AKONADI_EXPORT MessageCollectionDelegate : public QItemDelegate
{
Q_OBJECT
public:
/**
* Constructs an MessageCollectionDelegate with the given parent.
* You still need to call setItemDelegate() yourself.
*
* @param parent the parent tree view, which will also take ownership
*/
MessageCollectionDelegate( QTreeView *parent );
~MessageCollectionDelegate();
/**
* Sets the color in which the unread count is drawn behind the folder name.
* The default is KColorScheme::LinkText.
*
* @param color the color for the unread count
*/
void setUnreadColor( const QColor &color );
public Q_SLOTS:
/**
* Enable or disable the drawing of the unread count behind the folder
* name.
* You probably want to enable this when the unread count is hidden only.
* This is disabled by default.
*
* @param enable if true, the unread count is drawn behind the folder name,
* if false, the folder name will be drawn normally.
*/
void toggleUnreadAfterFolderName( bool enable );
protected:
/**
* Reimplemented to draw the unread count after the folder name
*/
virtual void drawDisplay( QPainter *painter, const QStyleOptionViewItem &option,
const QRect &rect, const QString &text) const;
/**
* Reimplemented to store the index for use in drawDisplay()
*/
virtual void paint( QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index ) const;
MessageCollectionDelegatePrivate *d_ptr;
private:
Q_DECLARE_PRIVATE( MessageCollectionDelegate )
};
}
#endif
......@@ -20,6 +20,7 @@
#include "messagecollectionmodel.h"
#include "collection.h"
#include "collectionmodel_p.h"
#include <kdebug.h>
#include <klocale.h>
......@@ -28,22 +29,58 @@
using namespace Akonadi;
namespace Akonadi {
class MessageCollectionModelPrivate : public CollectionModelPrivate
{
public:
enum CountType { Total, Unread };
Q_DECLARE_PUBLIC( MessageCollectionModel )
MessageCollectionModelPrivate( MessageCollectionModel *parent )
: CollectionModelPrivate( parent )
{}
qint64 countRecursive( Collection::Id collection, CountType type ) const;
};
}
qint64 MessageCollectionModelPrivate::countRecursive( Collection::Id collection,
CountType type ) const
{
qint64 result;
switch ( type ) {
case Unread: result = collections.value( collection ).status().unreadCount();
break;
case Total: result = collections.value( collection ).status().count();
break;
default: Q_ASSERT( false );
break;
}
QList<Collection::Id> children = childCollections.value( collection );
foreach( Collection::Id currentCollection, children ) {
result += countRecursive( currentCollection, type );
}
return result;
}
MessageCollectionModel::MessageCollectionModel( QObject * parent ) :
CollectionModel( parent ),
d( 0 )
CollectionModel( new MessageCollectionModelPrivate( this ), parent )
{
fetchCollectionStatus( true );
}
int MessageCollectionModel::columnCount( const QModelIndex & parent ) const
{
if (parent.isValid() && parent.column() != 0)
if ( parent.isValid() && parent.column() != 0 )
return 0;
return 3;
}
QVariant MessageCollectionModel::data( const QModelIndex & index, int role ) const
{
const Q_D( MessageCollectionModel );
if ( !index.isValid() )
return QVariant();
......@@ -52,11 +89,29 @@ QVariant MessageCollectionModel::data( const QModelIndex & index, int role ) con
return QVariant();
CollectionStatus status = col.status();
if ( role == Qt::DisplayRole && ( index.column() == 1 || index.column() == 2 ) ) {
int value = -1;
qint64 total = status.count();
qint64 unread = status.unreadCount();
qint64 totalRecursive = d->countRecursive( col.id(),
MessageCollectionModelPrivate::Total );
qint64 unreadRecursive = d->countRecursive( col.id(),
MessageCollectionModelPrivate::Unread );
if ( role == MessageCollectionTotalRole )
return total;
else if ( role == MessageCollectionUnreadRole )
return unread;
else if ( role == MessageCollectionUnreadRecursiveRole )
return unreadRecursive;
else if ( role == MessageCollectionTotalRecursiveRole )
return totalRecursive;
if ( role == Qt::DisplayRole &&
( index.column() == 1 || index.column() == 2 ) ) {
qint64 value = -1;
switch ( index.column() ) {
case 1: value = status.unreadCount(); break;
case 2: value = status.count(); break;
case 1 : value = unread; break;
case 2 : value = total; break;
}
if ( value < 0 )
return QString();
......@@ -69,13 +124,6 @@ QVariant MessageCollectionModel::data( const QModelIndex & index, int role ) con
if ( role == Qt::TextAlignmentRole && ( index.column() == 1 || index.column() == 2 ) )
return Qt::AlignRight;
// ### that's wrong, we'll need a custom delegate anyway
if ( role == Qt::FontRole && status.unreadCount() > 0 ) {
QFont f;
f.setBold( true );
return f;
}
return CollectionModel::data( index, role );
}
......
......@@ -25,6 +25,8 @@
namespace Akonadi {
class MessageCollectionModelPrivate;
/**
Extended model for message collections.
Supports columns for message unread/total counts.
......@@ -34,6 +36,27 @@ class AKONADI_EXPORT MessageCollectionModel : public CollectionModel
Q_OBJECT
public:
/**
* Custom roles for the message collection model
*/
enum MessageCollectionRole {
/// Get the number of unread items in this collection
MessageCollectionUnreadRole = CollectionViewUserRole,
/// Get the number of items in this collection
MessageCollectionTotalRole,
/// Get the number of unread items in this collection and its children
MessageCollectionUnreadRecursiveRole,
/// Get the number of items in this collection and its children
MessageCollectionTotalRecursiveRole,
MessageCollectionUserRole = Qt::UserRole + 64
};
/**
Create a new message collection model.
@param parent The parent object.
......@@ -56,8 +79,7 @@ class AKONADI_EXPORT MessageCollectionModel : public CollectionModel
virtual QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
private:
class Private;
Private* const d;
Q_DECLARE_PRIVATE( MessageCollectionModel )
};
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment