mirror of
https://invent.kde.org/graphics/okular
synced 2024-08-27 03:30:20 +00:00
Add proxy model for grouping by author
svn path=/trunk/KDE/kdegraphics/okular/; revision=710548
This commit is contained in:
parent
df27f63497
commit
c70cb99bdc
|
@ -304,7 +304,7 @@ QVariant AnnotationModel::data( const QModelIndex &index, int role ) const
|
|||
switch ( role )
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
return item->annotation->author();
|
||||
return AnnotationGuiUtils::captionForAnnotation( item->annotation );
|
||||
break;
|
||||
case Qt::DecorationRole:
|
||||
return KIcon( "okular" );
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
***************************************************************************/
|
||||
|
||||
#include <QtCore/QList>
|
||||
#include <QtGui/QItemSelection>
|
||||
|
||||
#include <kicon.h>
|
||||
|
||||
#include "annotationmodel.h"
|
||||
|
||||
|
@ -217,4 +220,335 @@ void PageGroupProxyModel::groupByPage( bool value )
|
|||
rebuildIndexes();
|
||||
}
|
||||
|
||||
|
||||
class AuthorGroupItem
|
||||
{
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Page,
|
||||
Author,
|
||||
Annotation
|
||||
};
|
||||
|
||||
AuthorGroupItem( AuthorGroupItem *parent, Type type = Page, const QModelIndex &index = QModelIndex() )
|
||||
: mParent( parent ), mType( type ), mIndex( index )
|
||||
{
|
||||
}
|
||||
|
||||
~AuthorGroupItem()
|
||||
{
|
||||
qDeleteAll( mChilds );
|
||||
}
|
||||
|
||||
void appendChild( AuthorGroupItem *child ) { mChilds.append( child ); }
|
||||
AuthorGroupItem* parent() const { return mParent; }
|
||||
AuthorGroupItem* child( int row ) const { return mChilds.value( row ); }
|
||||
int childCount() const { return mChilds.count(); }
|
||||
|
||||
void dump( int level = 0 )
|
||||
{
|
||||
QString prefix;
|
||||
for ( int i = 0; i < level; ++i ) prefix += " ";
|
||||
|
||||
qDebug( "%s%s", qPrintable( prefix ), ( mType == Page ? "Page" : (mType == Author ? "Author" : "Annotation") ) );
|
||||
|
||||
for ( int i = 0; i < mChilds.count(); ++i )
|
||||
mChilds[ i ]->dump( level + 2 );
|
||||
}
|
||||
|
||||
const AuthorGroupItem* findIndex( const QModelIndex &index ) const
|
||||
{
|
||||
if ( index == mIndex )
|
||||
return this;
|
||||
|
||||
for ( int i = 0; i < mChilds.count(); ++i ) {
|
||||
const AuthorGroupItem *item = mChilds[ i ]->findIndex( index );
|
||||
if ( item )
|
||||
return item;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int row() const
|
||||
{
|
||||
return ( mParent ? mParent->mChilds.indexOf( const_cast<AuthorGroupItem*>( this ) ) : 0 );
|
||||
}
|
||||
|
||||
Type type() const { return mType; }
|
||||
QModelIndex index() const { return mIndex; }
|
||||
|
||||
void setAuthor( const QString &author ) { mAuthor = author; }
|
||||
QString author() const { return mAuthor; }
|
||||
|
||||
private:
|
||||
AuthorGroupItem *mParent;
|
||||
Type mType;
|
||||
QModelIndex mIndex;
|
||||
QList<AuthorGroupItem*> mChilds;
|
||||
QString mAuthor;
|
||||
};
|
||||
|
||||
class AuthorGroupProxyModel::Private
|
||||
{
|
||||
public:
|
||||
Private( AuthorGroupProxyModel *parent )
|
||||
: mParent( parent ), mRoot( 0 ),
|
||||
mGroupByAuthor( false )
|
||||
{
|
||||
}
|
||||
|
||||
AuthorGroupProxyModel *mParent;
|
||||
AuthorGroupItem *mRoot;
|
||||
bool mGroupByAuthor;
|
||||
};
|
||||
|
||||
AuthorGroupProxyModel::AuthorGroupProxyModel( QObject *parent )
|
||||
: QAbstractProxyModel( parent ),
|
||||
d( new Private( this ) )
|
||||
{
|
||||
}
|
||||
|
||||
AuthorGroupProxyModel::~AuthorGroupProxyModel()
|
||||
{
|
||||
}
|
||||
|
||||
int AuthorGroupProxyModel::columnCount( const QModelIndex& ) const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int AuthorGroupProxyModel::rowCount( const QModelIndex &parentIndex ) const
|
||||
{
|
||||
AuthorGroupItem *item = 0;
|
||||
if ( !parentIndex.isValid() )
|
||||
item = d->mRoot;
|
||||
else
|
||||
item = static_cast<AuthorGroupItem*>( parentIndex.internalPointer() );
|
||||
|
||||
return item->childCount();
|
||||
}
|
||||
|
||||
QModelIndex AuthorGroupProxyModel::index( int row, int column, const QModelIndex &parentIndex ) const
|
||||
{
|
||||
if ( !hasIndex( row, column, parentIndex ) )
|
||||
return QModelIndex();
|
||||
|
||||
AuthorGroupItem *parentItem = 0;
|
||||
if ( !parentIndex.isValid() )
|
||||
parentItem = d->mRoot;
|
||||
else
|
||||
parentItem = static_cast<AuthorGroupItem*>( parentIndex.internalPointer() );
|
||||
|
||||
AuthorGroupItem *child = parentItem->child( row );
|
||||
if ( child )
|
||||
return createIndex( row, column, child );
|
||||
else
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex AuthorGroupProxyModel::parent( const QModelIndex &index ) const
|
||||
{
|
||||
if ( !index.isValid() )
|
||||
return QModelIndex();
|
||||
|
||||
AuthorGroupItem *childItem = static_cast<AuthorGroupItem*>( index.internalPointer() );
|
||||
AuthorGroupItem *parentItem = childItem->parent();
|
||||
|
||||
if ( parentItem == d->mRoot )
|
||||
return QModelIndex();
|
||||
else
|
||||
return createIndex( parentItem->row(), 0, parentItem );
|
||||
}
|
||||
|
||||
QModelIndex AuthorGroupProxyModel::mapFromSource( const QModelIndex &sourceIndex ) const
|
||||
{
|
||||
const AuthorGroupItem *item = d->mRoot->findIndex( sourceIndex );
|
||||
if ( !item )
|
||||
return QModelIndex();
|
||||
|
||||
return createIndex( item->row(), 0, const_cast<AuthorGroupItem*>( item ) );
|
||||
}
|
||||
|
||||
QModelIndex AuthorGroupProxyModel::mapToSource( const QModelIndex &proxyIndex ) const
|
||||
{
|
||||
if ( !proxyIndex.isValid() )
|
||||
return QModelIndex();
|
||||
|
||||
AuthorGroupItem *item = static_cast<AuthorGroupItem*>( proxyIndex.internalPointer() );
|
||||
|
||||
return item->index();
|
||||
}
|
||||
|
||||
void AuthorGroupProxyModel::setSourceModel( QAbstractItemModel *model )
|
||||
{
|
||||
if ( sourceModel() ) {
|
||||
disconnect( sourceModel(), SIGNAL( layoutChanged() ), this, SLOT( rebuildIndexes() ) );
|
||||
disconnect( sourceModel(), SIGNAL( modelReset() ), this, SLOT( rebuildIndexes() ) );
|
||||
disconnect( sourceModel(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), this, SLOT( rebuildIndexes() ) );
|
||||
disconnect( sourceModel(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), this, SLOT( rebuildIndexes() ) );
|
||||
}
|
||||
|
||||
QAbstractProxyModel::setSourceModel( model );
|
||||
|
||||
connect( sourceModel(), SIGNAL( layoutChanged() ), this, SLOT( rebuildIndexes() ) );
|
||||
connect( sourceModel(), SIGNAL( modelReset() ), this, SLOT( rebuildIndexes() ) );
|
||||
connect( sourceModel(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ), this, SLOT( rebuildIndexes() ) );
|
||||
connect( sourceModel(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ), this, SLOT( rebuildIndexes() ) );
|
||||
|
||||
rebuildIndexes();
|
||||
}
|
||||
|
||||
static bool isAuthorItem( const QModelIndex &index )
|
||||
{
|
||||
AuthorGroupItem *item = static_cast<AuthorGroupItem*>( index.internalPointer() );
|
||||
return (item->type() == AuthorGroupItem::Author);
|
||||
}
|
||||
|
||||
QItemSelection AuthorGroupProxyModel::mapSelectionToSource( const QItemSelection &selection ) const
|
||||
{
|
||||
QModelIndexList proxyIndexes = selection.indexes();
|
||||
QItemSelection sourceSelection;
|
||||
for ( int i = 0; i < proxyIndexes.size(); ++i ) {
|
||||
if ( !isAuthorItem( proxyIndexes.at( i ) ) )
|
||||
sourceSelection << QItemSelectionRange( mapToSource( proxyIndexes.at( i ) ) );
|
||||
}
|
||||
|
||||
return sourceSelection;
|
||||
}
|
||||
|
||||
QItemSelection AuthorGroupProxyModel::mapSelectionFromSource( const QItemSelection &selection ) const
|
||||
{
|
||||
return QAbstractProxyModel::mapSelectionFromSource( selection );
|
||||
}
|
||||
|
||||
QVariant AuthorGroupProxyModel::data( const QModelIndex &proxyIndex, int role ) const
|
||||
{
|
||||
if ( isAuthorItem( proxyIndex ) ) {
|
||||
AuthorGroupItem *item = static_cast<AuthorGroupItem*>( proxyIndex.internalPointer() );
|
||||
if ( role == Qt::DisplayRole )
|
||||
return item->author();
|
||||
else if ( role == Qt::DecorationRole )
|
||||
return KIcon( item->author().isEmpty() ? "precense_away" : "personal" );
|
||||
else
|
||||
return QVariant();
|
||||
} else {
|
||||
return QAbstractProxyModel::data( proxyIndex, role );
|
||||
}
|
||||
}
|
||||
|
||||
QMap<int, QVariant> AuthorGroupProxyModel::itemData( const QModelIndex &index ) const
|
||||
{
|
||||
if ( isAuthorItem( index ) ) {
|
||||
return QMap<int, QVariant>();
|
||||
} else {
|
||||
return QAbstractProxyModel::itemData( index );
|
||||
}
|
||||
}
|
||||
|
||||
Qt::ItemFlags AuthorGroupProxyModel::flags( const QModelIndex &index ) const
|
||||
{
|
||||
if ( isAuthorItem( index ) ) {
|
||||
return Qt::ItemIsEnabled;
|
||||
} else {
|
||||
return QAbstractProxyModel::flags( index );
|
||||
}
|
||||
}
|
||||
|
||||
void AuthorGroupProxyModel::groupByAuthor( bool value )
|
||||
{
|
||||
if ( d->mGroupByAuthor == value )
|
||||
return;
|
||||
|
||||
d->mGroupByAuthor = value;
|
||||
|
||||
rebuildIndexes();
|
||||
}
|
||||
|
||||
void AuthorGroupProxyModel::rebuildIndexes()
|
||||
{
|
||||
delete d->mRoot;
|
||||
d->mRoot = new AuthorGroupItem( 0 );
|
||||
|
||||
if ( d->mGroupByAuthor ) {
|
||||
QMap<QString, AuthorGroupItem*> authorMap;
|
||||
|
||||
for ( int row = 0; row < sourceModel()->rowCount(); ++row ) {
|
||||
const QModelIndex idx = sourceModel()->index( row, 0 );
|
||||
const QString author = sourceModel()->data( idx, AnnotationModel::AuthorRole ).toString();
|
||||
if ( !author.isEmpty() ) {
|
||||
// We have the annotations as top-level, so introduce authors as new
|
||||
// top-levels and append the annotations
|
||||
if ( !authorMap.contains( author ) ) {
|
||||
AuthorGroupItem *item = new AuthorGroupItem( d->mRoot, AuthorGroupItem::Author );
|
||||
item->setAuthor( author );
|
||||
|
||||
// Add item to tree
|
||||
d->mRoot->appendChild( item );
|
||||
|
||||
// Insert to lookup list
|
||||
authorMap.insert( author, item );
|
||||
}
|
||||
|
||||
AuthorGroupItem *authorItem = authorMap.value( author );
|
||||
|
||||
AuthorGroupItem *item = new AuthorGroupItem( authorItem, AuthorGroupItem::Annotation, idx );
|
||||
authorItem->appendChild( item );
|
||||
} else {
|
||||
// We have the pages as top-level, so we use them as top-level, append the
|
||||
// authors for all annotations of the page, and then the annotations themself
|
||||
AuthorGroupItem *pageItem = new AuthorGroupItem( d->mRoot, AuthorGroupItem::Page, idx );
|
||||
d->mRoot->appendChild( pageItem );
|
||||
|
||||
// First collect all authors...
|
||||
QMap<QString, AuthorGroupItem*> pageAuthorMap;
|
||||
for ( int subRow = 0; subRow < sourceModel()->rowCount( idx ); ++subRow ) {
|
||||
const QModelIndex annIdx = sourceModel()->index( subRow, 0, idx );
|
||||
const QString author = sourceModel()->data( annIdx, AnnotationModel::AuthorRole ).toString();
|
||||
|
||||
if ( !pageAuthorMap.contains( author ) ) {
|
||||
AuthorGroupItem *item = new AuthorGroupItem( pageItem, AuthorGroupItem::Author );
|
||||
item->setAuthor( author );
|
||||
|
||||
// Add item to tree
|
||||
pageItem->appendChild( item );
|
||||
|
||||
// Insert to lookup list
|
||||
pageAuthorMap.insert( author, item );
|
||||
}
|
||||
|
||||
AuthorGroupItem *authorItem = pageAuthorMap.value( author );
|
||||
|
||||
AuthorGroupItem *item = new AuthorGroupItem( authorItem, AuthorGroupItem::Annotation, annIdx );
|
||||
authorItem->appendChild( item );
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for ( int row = 0; row < sourceModel()->rowCount(); ++row ) {
|
||||
const QModelIndex idx = sourceModel()->index( row, 0 );
|
||||
const QString author = sourceModel()->data( idx, AnnotationModel::AuthorRole ).toString();
|
||||
if ( !author.isEmpty() ) {
|
||||
// We have the annotations as top-level items
|
||||
AuthorGroupItem *item = new AuthorGroupItem( d->mRoot, AuthorGroupItem::Annotation, idx );
|
||||
d->mRoot->appendChild( item );
|
||||
} else {
|
||||
// We have the pages as top-level items
|
||||
AuthorGroupItem *pageItem = new AuthorGroupItem( d->mRoot, AuthorGroupItem::Page, idx );
|
||||
d->mRoot->appendChild( pageItem );
|
||||
|
||||
// Append all annotations as second-level
|
||||
for ( int subRow = 0; subRow < sourceModel()->rowCount( idx ); ++subRow ) {
|
||||
const QModelIndex subIdx = sourceModel()->index( subRow, 0, idx );
|
||||
AuthorGroupItem *item = new AuthorGroupItem( pageItem, AuthorGroupItem::Annotation, subIdx );
|
||||
pageItem->appendChild( item );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
#include "annotationproxymodels.moc"
|
||||
|
|
|
@ -61,7 +61,7 @@ class PageGroupProxyModel : public QAbstractProxyModel
|
|||
|
||||
public:
|
||||
/**
|
||||
* Creates a new page group proxy model.
|
||||
* Creates a new page group proxy model.
|
||||
*
|
||||
* @param parent The parent object.
|
||||
*/
|
||||
|
@ -93,4 +93,53 @@ class PageGroupProxyModel : public QAbstractProxyModel
|
|||
QList<QModelIndex> mIndexes;
|
||||
QList<QPair< QModelIndex, QList<QModelIndex> > > mTreeIndexes;
|
||||
};
|
||||
|
||||
/**
|
||||
* A proxy model which groups the annotations by author.
|
||||
*/
|
||||
class AuthorGroupProxyModel : public QAbstractProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a new author group proxy model.
|
||||
*
|
||||
* @param parent The parent object.
|
||||
*/
|
||||
AuthorGroupProxyModel( QObject *parent = 0 );
|
||||
~AuthorGroupProxyModel();
|
||||
|
||||
virtual int columnCount( const QModelIndex &parentIndex ) const;
|
||||
virtual int rowCount( const QModelIndex &parentIndex ) const;
|
||||
|
||||
virtual QModelIndex index( int row, int column, const QModelIndex &parentIndex = QModelIndex() ) const;
|
||||
virtual QModelIndex parent( const QModelIndex &index ) const;
|
||||
|
||||
virtual QModelIndex mapFromSource( const QModelIndex &sourceIndex ) const;
|
||||
virtual QModelIndex mapToSource( const QModelIndex &proxyIndex ) const;
|
||||
|
||||
virtual void setSourceModel( QAbstractItemModel *model );
|
||||
|
||||
virtual QItemSelection mapSelectionToSource(const QItemSelection &selection) const;
|
||||
virtual QItemSelection mapSelectionFromSource(const QItemSelection &selection) const;
|
||||
QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const;
|
||||
QMap<int, QVariant> itemData(const QModelIndex &index) const;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const;
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Sets whether the proxy model shall group
|
||||
* the annotations by author.
|
||||
*/
|
||||
void groupByAuthor( bool value );
|
||||
|
||||
private Q_SLOTS:
|
||||
void rebuildIndexes();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private* const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -103,11 +103,14 @@ Reviews::Reviews( QWidget * parent, Okular::Document * document )
|
|||
|
||||
m_filterProxy = new PageFilterProxyModel( m_view );
|
||||
m_groupProxy = new PageGroupProxyModel( m_view );
|
||||
m_authorProxy = new AuthorGroupProxyModel( m_view );
|
||||
|
||||
m_filterProxy->setSourceModel( m_model );
|
||||
m_groupProxy->setSourceModel( m_filterProxy );
|
||||
m_authorProxy->setSourceModel( m_groupProxy );
|
||||
|
||||
m_view->setModel( m_groupProxy );
|
||||
|
||||
m_view->setModel( m_authorProxy );
|
||||
|
||||
vLayout->addWidget( new KTreeViewSearchLine( this, m_view ) );
|
||||
vLayout->addWidget( m_view );
|
||||
|
@ -121,12 +124,11 @@ Reviews::Reviews( QWidget * parent, Okular::Document * document )
|
|||
connect( groupByPageAction, SIGNAL( toggled( bool ) ), this, SLOT( slotPageEnabled( bool ) ) );
|
||||
groupByPageAction->setChecked( Okular::Settings::groupByPage() );
|
||||
// - add Author button
|
||||
/*
|
||||
QAction * groupByAuthorAction = toolBar->addAction( KIcon( "user" ), i18n( "Group by Author" ) );
|
||||
groupByAuthorAction->setCheckable( true );
|
||||
connect( groupByAuthorAction, SIGNAL( toggled( bool ) ), this, SLOT( slotAuthorEnabled( bool ) ) );
|
||||
groupByAuthorAction->setChecked( Okular::Settings::groupByAuthor() );
|
||||
*/
|
||||
|
||||
// - add separator
|
||||
toolBar->addSeparator();
|
||||
// - add Current Page Only button
|
||||
|
@ -157,13 +159,17 @@ void Reviews::slotPageEnabled( bool on )
|
|||
// store toggle state in Settings and update the listview
|
||||
Okular::Settings::setGroupByPage( on );
|
||||
m_groupProxy->groupByPage( on );
|
||||
|
||||
m_view->expandAll();
|
||||
}
|
||||
|
||||
void Reviews::slotAuthorEnabled( bool on )
|
||||
{
|
||||
// store toggle state in Settings and update the listview
|
||||
Okular::Settings::setGroupByAuthor( on );
|
||||
//m_proxy->groupByAuthor( on );
|
||||
m_authorProxy->groupByAuthor( on );
|
||||
|
||||
m_view->expandAll();
|
||||
}
|
||||
|
||||
void Reviews::slotCurrentPageOnly( bool on )
|
||||
|
@ -171,13 +177,16 @@ void Reviews::slotCurrentPageOnly( bool on )
|
|||
// store toggle state in Settings and update the listview
|
||||
Okular::Settings::setCurrentPageOnly( on );
|
||||
m_filterProxy->groupByCurrentPage( on );
|
||||
|
||||
m_view->expandAll();
|
||||
}
|
||||
//END GUI Slots
|
||||
|
||||
|
||||
void Reviews::activated( const QModelIndex &index )
|
||||
{
|
||||
const QModelIndex filterIndex = m_groupProxy->mapToSource( index );
|
||||
const QModelIndex authorIndex = m_authorProxy->mapToSource( index );
|
||||
const QModelIndex filterIndex = m_groupProxy->mapToSource( authorIndex );
|
||||
const QModelIndex annotIndex = m_filterProxy->mapToSource( filterIndex );
|
||||
|
||||
Okular::Annotation *annotation = m_model->annotationForIndex( annotIndex );
|
||||
|
|
|
@ -23,6 +23,7 @@ class Document;
|
|||
}
|
||||
|
||||
class AnnotationModel;
|
||||
class AuthorGroupProxyModel;
|
||||
class PageFilterProxyModel;
|
||||
class PageGroupProxyModel;
|
||||
class TreeView;
|
||||
|
@ -59,6 +60,7 @@ class Reviews : public QWidget, public Okular::DocumentObserver
|
|||
// internal storage
|
||||
Okular::Document * m_document;
|
||||
AnnotationModel * m_model;
|
||||
AuthorGroupProxyModel * m_authorProxy;
|
||||
PageFilterProxyModel * m_filterProxy;
|
||||
PageGroupProxyModel * m_groupProxy;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue