1 /***************************************************************************
2 * Copyright (C) 2007 by Tobias Koenig <tokoe@kde.org> *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 ***************************************************************************/
10 #include "annotationproxymodels.h"
12 #include <QtCore/QList>
13 #include <QtGui/QItemSelection>
17 #include "annotationmodel.h"
19 static quint32
mixIndex( int row
, int column
)
21 return ( row
<< 4 ) | column
;
24 PageFilterProxyModel::PageFilterProxyModel( QObject
*parent
)
25 : QSortFilterProxyModel( parent
),
26 mGroupByCurrentPage( false ),
29 setDynamicSortFilter( true );
32 void PageFilterProxyModel::groupByCurrentPage( bool value
)
34 if ( mGroupByCurrentPage
== value
)
37 mGroupByCurrentPage
= value
;
42 void PageFilterProxyModel::setCurrentPage( int page
)
44 if ( mCurrentPage
== page
)
49 // no need to invalidate when we're not showing the current page only
50 if ( !mGroupByCurrentPage
)
56 bool PageFilterProxyModel::filterAcceptsRow( int row
, const QModelIndex
&sourceParent
) const
58 if ( !mGroupByCurrentPage
)
61 const QModelIndex pageIndex
= sourceModel()->index( row
, 0, sourceParent
);
62 int page
= sourceModel()->data( pageIndex
, AnnotationModel::PageRole
).toInt();
64 return (page
== mCurrentPage
);
68 PageGroupProxyModel::PageGroupProxyModel( QObject
*parent
)
69 : QAbstractProxyModel( parent
),
74 int PageGroupProxyModel::columnCount( const QModelIndex
&parentIndex
) const
76 // For top-level and second level we have always only one column
78 if ( parentIndex
.isValid() ) {
79 if ( parentIndex
.parent().isValid() )
82 return 1; // second-level
85 return 1; // top-level
88 if ( !parentIndex
.isValid() ) // top-level
96 int PageGroupProxyModel::rowCount( const QModelIndex
&parentIndex
) const
99 if ( parentIndex
.isValid() ) {
100 if ( parentIndex
.parent().isValid() )
103 return mTreeIndexes
[ parentIndex
.row() ].second
.count(); // second-level
106 return mTreeIndexes
.count(); // top-level
109 if ( !parentIndex
.isValid() ) // top-level
110 return mIndexes
.count();
116 QModelIndex
PageGroupProxyModel::index( int row
, int column
, const QModelIndex
&parentIndex
) const
118 if ( row
< 0 || column
!= 0 )
119 return QModelIndex();
121 if ( mGroupByPage
) {
122 if ( parentIndex
.isValid() ) {
123 if ( parentIndex
.row() >= 0 && parentIndex
.row() < mTreeIndexes
.count()
124 && row
< mTreeIndexes
[ parentIndex
.row() ].second
.count() )
125 return createIndex( row
, column
, qint32( parentIndex
.row() + 1 ) );
127 return QModelIndex();
129 if ( row
< mTreeIndexes
.count() )
130 return createIndex( row
, column
, 0 );
132 return QModelIndex();
135 if ( row
< mIndexes
.count() )
136 return createIndex( row
, column
, mixIndex( parentIndex
.row(), parentIndex
.column() ) );
138 return QModelIndex();
142 QModelIndex
PageGroupProxyModel::parent( const QModelIndex
&idx
) const
144 if ( mGroupByPage
) {
145 if ( idx
.internalId() == 0 ) // top-level
146 return QModelIndex();
148 return index( idx
.internalId() - 1, idx
.column() );
150 // We have only top-level items
151 return QModelIndex();
155 QModelIndex
PageGroupProxyModel::mapFromSource( const QModelIndex
&sourceIndex
) const
157 if ( mGroupByPage
) {
158 if ( sourceIndex
.parent().isValid() ) {
159 return index( sourceIndex
.row(), sourceIndex
.column(), sourceIndex
.parent() );
161 return index( sourceIndex
.row(), sourceIndex
.column() );
164 for ( int i
= 0; i
< mIndexes
.count(); ++i
) {
165 if ( mIndexes
[ i
] == sourceIndex
)
166 return index( i
, 0 );
169 return QModelIndex();
173 QModelIndex
PageGroupProxyModel::mapToSource( const QModelIndex
&proxyIndex
) const
175 if ( !proxyIndex
.isValid() )
176 return QModelIndex();
178 if ( mGroupByPage
) {
179 if ( proxyIndex
.internalId() == 0 ) {
181 if ( proxyIndex
.row() >= mTreeIndexes
.count() || proxyIndex
.row() < 0 )
182 return QModelIndex();
184 return mTreeIndexes
[ proxyIndex
.row() ].first
;
186 if ( proxyIndex
.internalId() - 1 >= mTreeIndexes
.count() ||
187 proxyIndex
.row() >= mTreeIndexes
[ proxyIndex
.internalId() - 1 ].second
.count() )
188 return QModelIndex();
190 return mTreeIndexes
[ proxyIndex
.internalId() - 1 ].second
[ proxyIndex
.row() ];
193 if ( proxyIndex
.column() > 0 || proxyIndex
.row() >= mIndexes
.count() )
194 return QModelIndex();
196 return mIndexes
[ proxyIndex
.row() ];
201 void PageGroupProxyModel::setSourceModel( QAbstractItemModel
*model
)
203 if ( sourceModel() ) {
204 disconnect( sourceModel(), SIGNAL( layoutChanged() ), this, SLOT( rebuildIndexes() ) );
205 disconnect( sourceModel(), SIGNAL( modelReset() ), this, SLOT( rebuildIndexes() ) );
206 disconnect( sourceModel(), SIGNAL( rowsInserted( const QModelIndex
&, int, int ) ), this, SLOT( rebuildIndexes() ) );
207 disconnect( sourceModel(), SIGNAL( rowsRemoved( const QModelIndex
&, int, int ) ), this, SLOT( rebuildIndexes() ) );
210 QAbstractProxyModel::setSourceModel( model
);
212 connect( sourceModel(), SIGNAL( layoutChanged() ), this, SLOT( rebuildIndexes() ) );
213 connect( sourceModel(), SIGNAL( modelReset() ), this, SLOT( rebuildIndexes() ) );
214 connect( sourceModel(), SIGNAL( rowsInserted( const QModelIndex
&, int, int ) ), this, SLOT( rebuildIndexes() ) );
215 connect( sourceModel(), SIGNAL( rowsRemoved( const QModelIndex
&, int, int ) ), this, SLOT( rebuildIndexes() ) );
220 void PageGroupProxyModel::rebuildIndexes()
222 if ( mGroupByPage
) {
223 mTreeIndexes
.clear();
225 for ( int row
= 0; row
< sourceModel()->rowCount(); ++row
) {
226 const QModelIndex pageIndex
= sourceModel()->index( row
, 0 );
228 QList
<QModelIndex
> itemIndexes
;
229 for ( int subRow
= 0; subRow
< sourceModel()->rowCount( pageIndex
); ++subRow
) {
230 itemIndexes
.append( sourceModel()->index( subRow
, 0, pageIndex
) );
233 mTreeIndexes
.append( QPair
<QModelIndex
, QList
<QModelIndex
> >( pageIndex
, itemIndexes
) );
238 for ( int row
= 0; row
< sourceModel()->rowCount(); ++row
) {
239 const QModelIndex pageIndex
= sourceModel()->index( row
, 0 );
240 for ( int subRow
= 0; subRow
< sourceModel()->rowCount( pageIndex
); ++subRow
) {
241 mIndexes
.append( sourceModel()->index( subRow
, 0, pageIndex
) );
249 void PageGroupProxyModel::groupByPage( bool value
)
251 if ( mGroupByPage
== value
)
254 mGroupByPage
= value
;
260 class AuthorGroupItem
270 AuthorGroupItem( AuthorGroupItem
*parent
, Type type
= Page
, const QModelIndex
&index
= QModelIndex() )
271 : mParent( parent
), mType( type
), mIndex( index
)
277 qDeleteAll( mChilds
);
280 void appendChild( AuthorGroupItem
*child
) { mChilds
.append( child
); }
281 AuthorGroupItem
* parent() const { return mParent
; }
282 AuthorGroupItem
* child( int row
) const { return mChilds
.value( row
); }
283 int childCount() const { return mChilds
.count(); }
285 void dump( int level
= 0 )
288 for ( int i
= 0; i
< level
; ++i
) prefix
+= ' ';
290 qDebug( "%s%s", qPrintable( prefix
), ( mType
== Page
? "Page" : (mType
== Author
? "Author" : "Annotation") ) );
292 for ( int i
= 0; i
< mChilds
.count(); ++i
)
293 mChilds
[ i
]->dump( level
+ 2 );
296 const AuthorGroupItem
* findIndex( const QModelIndex
&index
) const
298 if ( index
== mIndex
)
301 for ( int i
= 0; i
< mChilds
.count(); ++i
) {
302 const AuthorGroupItem
*item
= mChilds
[ i
]->findIndex( index
);
312 return ( mParent
? mParent
->mChilds
.indexOf( const_cast<AuthorGroupItem
*>( this ) ) : 0 );
315 Type
type() const { return mType
; }
316 QModelIndex
index() const { return mIndex
; }
318 void setAuthor( const QString
&author
) { mAuthor
= author
; }
319 QString
author() const { return mAuthor
; }
322 AuthorGroupItem
*mParent
;
325 QList
<AuthorGroupItem
*> mChilds
;
329 class AuthorGroupProxyModel::Private
332 Private( AuthorGroupProxyModel
*parent
)
333 : mParent( parent
), mRoot( 0 ),
334 mGroupByAuthor( false )
342 AuthorGroupProxyModel
*mParent
;
343 AuthorGroupItem
*mRoot
;
347 AuthorGroupProxyModel::AuthorGroupProxyModel( QObject
*parent
)
348 : QAbstractProxyModel( parent
),
349 d( new Private( this ) )
353 AuthorGroupProxyModel::~AuthorGroupProxyModel()
358 int AuthorGroupProxyModel::columnCount( const QModelIndex
& ) const
363 int AuthorGroupProxyModel::rowCount( const QModelIndex
&parentIndex
) const
365 AuthorGroupItem
*item
= 0;
366 if ( !parentIndex
.isValid() )
369 item
= static_cast<AuthorGroupItem
*>( parentIndex
.internalPointer() );
371 return item
? item
->childCount() : 0;
374 QModelIndex
AuthorGroupProxyModel::index( int row
, int column
, const QModelIndex
&parentIndex
) const
376 if ( !hasIndex( row
, column
, parentIndex
) )
377 return QModelIndex();
379 AuthorGroupItem
*parentItem
= 0;
380 if ( !parentIndex
.isValid() )
381 parentItem
= d
->mRoot
;
383 parentItem
= static_cast<AuthorGroupItem
*>( parentIndex
.internalPointer() );
385 AuthorGroupItem
*child
= parentItem
->child( row
);
387 return createIndex( row
, column
, child
);
389 return QModelIndex();
392 QModelIndex
AuthorGroupProxyModel::parent( const QModelIndex
&index
) const
394 if ( !index
.isValid() )
395 return QModelIndex();
397 AuthorGroupItem
*childItem
= static_cast<AuthorGroupItem
*>( index
.internalPointer() );
398 AuthorGroupItem
*parentItem
= childItem
->parent();
400 if ( parentItem
== d
->mRoot
)
401 return QModelIndex();
403 return createIndex( parentItem
->row(), 0, parentItem
);
406 QModelIndex
AuthorGroupProxyModel::mapFromSource( const QModelIndex
&sourceIndex
) const
408 const AuthorGroupItem
*item
= d
->mRoot
->findIndex( sourceIndex
);
410 return QModelIndex();
412 return createIndex( item
->row(), 0, const_cast<AuthorGroupItem
*>( item
) );
415 QModelIndex
AuthorGroupProxyModel::mapToSource( const QModelIndex
&proxyIndex
) const
417 if ( !proxyIndex
.isValid() )
418 return QModelIndex();
420 AuthorGroupItem
*item
= static_cast<AuthorGroupItem
*>( proxyIndex
.internalPointer() );
422 return item
->index();
425 void AuthorGroupProxyModel::setSourceModel( QAbstractItemModel
*model
)
427 if ( sourceModel() ) {
428 disconnect( sourceModel(), SIGNAL( layoutChanged() ), this, SLOT( rebuildIndexes() ) );
429 disconnect( sourceModel(), SIGNAL( modelReset() ), this, SLOT( rebuildIndexes() ) );
430 disconnect( sourceModel(), SIGNAL( rowsInserted( const QModelIndex
&, int, int ) ), this, SLOT( rebuildIndexes() ) );
431 disconnect( sourceModel(), SIGNAL( rowsRemoved( const QModelIndex
&, int, int ) ), this, SLOT( rebuildIndexes() ) );
434 QAbstractProxyModel::setSourceModel( model
);
436 connect( sourceModel(), SIGNAL( layoutChanged() ), this, SLOT( rebuildIndexes() ) );
437 connect( sourceModel(), SIGNAL( modelReset() ), this, SLOT( rebuildIndexes() ) );
438 connect( sourceModel(), SIGNAL( rowsInserted( const QModelIndex
&, int, int ) ), this, SLOT( rebuildIndexes() ) );
439 connect( sourceModel(), SIGNAL( rowsRemoved( const QModelIndex
&, int, int ) ), this, SLOT( rebuildIndexes() ) );
444 static bool isAuthorItem( const QModelIndex
&index
)
446 if ( !index
.isValid() ) {
450 AuthorGroupItem
*item
= static_cast<AuthorGroupItem
*>( index
.internalPointer() );
451 return (item
->type() == AuthorGroupItem::Author
);
454 QItemSelection
AuthorGroupProxyModel::mapSelectionToSource( const QItemSelection
&selection
) const
456 QModelIndexList proxyIndexes
= selection
.indexes();
457 QItemSelection sourceSelection
;
458 for ( int i
= 0; i
< proxyIndexes
.size(); ++i
) {
459 if ( !isAuthorItem( proxyIndexes
.at( i
) ) )
460 sourceSelection
<< QItemSelectionRange( mapToSource( proxyIndexes
.at( i
) ) );
463 return sourceSelection
;
466 QItemSelection
AuthorGroupProxyModel::mapSelectionFromSource( const QItemSelection
&selection
) const
468 return QAbstractProxyModel::mapSelectionFromSource( selection
);
471 QVariant
AuthorGroupProxyModel::data( const QModelIndex
&proxyIndex
, int role
) const
473 if ( isAuthorItem( proxyIndex
) ) {
474 AuthorGroupItem
*item
= static_cast<AuthorGroupItem
*>( proxyIndex
.internalPointer() );
475 if ( role
== Qt::DisplayRole
)
476 return item
->author();
477 else if ( role
== Qt::DecorationRole
)
478 return KIcon( item
->author().isEmpty() ? "user-away" : "user-identity" );
482 return QAbstractProxyModel::data( proxyIndex
, role
);
486 QMap
<int, QVariant
> AuthorGroupProxyModel::itemData( const QModelIndex
&index
) const
488 if ( isAuthorItem( index
) ) {
489 return QMap
<int, QVariant
>();
491 return QAbstractProxyModel::itemData( index
);
495 Qt::ItemFlags
AuthorGroupProxyModel::flags( const QModelIndex
&index
) const
497 if ( isAuthorItem( index
) ) {
498 return Qt::ItemIsEnabled
| Qt::ItemIsSelectable
;
500 return QAbstractProxyModel::flags( index
);
504 void AuthorGroupProxyModel::groupByAuthor( bool value
)
506 if ( d
->mGroupByAuthor
== value
)
509 d
->mGroupByAuthor
= value
;
514 void AuthorGroupProxyModel::rebuildIndexes()
517 d
->mRoot
= new AuthorGroupItem( 0 );
519 if ( d
->mGroupByAuthor
) {
520 QMap
<QString
, AuthorGroupItem
*> authorMap
;
522 for ( int row
= 0; row
< sourceModel()->rowCount(); ++row
) {
523 const QModelIndex idx
= sourceModel()->index( row
, 0 );
524 const QString author
= sourceModel()->data( idx
, AnnotationModel::AuthorRole
).toString();
525 if ( !author
.isEmpty() ) {
526 // We have the annotations as top-level, so introduce authors as new
527 // top-levels and append the annotations
528 AuthorGroupItem
*authorItem
= authorMap
.value( author
, 0 );
530 authorItem
= new AuthorGroupItem( d
->mRoot
, AuthorGroupItem::Author
);
531 authorItem
->setAuthor( author
);
534 d
->mRoot
->appendChild( authorItem
);
536 // Insert to lookup list
537 authorMap
.insert( author
, authorItem
);
540 AuthorGroupItem
*item
= new AuthorGroupItem( authorItem
, AuthorGroupItem::Annotation
, idx
);
541 authorItem
->appendChild( item
);
543 // We have the pages as top-level, so we use them as top-level, append the
544 // authors for all annotations of the page, and then the annotations themself
545 AuthorGroupItem
*pageItem
= new AuthorGroupItem( d
->mRoot
, AuthorGroupItem::Page
, idx
);
546 d
->mRoot
->appendChild( pageItem
);
548 // First collect all authors...
549 QMap
<QString
, AuthorGroupItem
*> pageAuthorMap
;
550 for ( int subRow
= 0; subRow
< sourceModel()->rowCount( idx
); ++subRow
) {
551 const QModelIndex annIdx
= sourceModel()->index( subRow
, 0, idx
);
552 const QString author
= sourceModel()->data( annIdx
, AnnotationModel::AuthorRole
).toString();
554 AuthorGroupItem
*authorItem
= pageAuthorMap
.value( author
, 0 );
556 authorItem
= new AuthorGroupItem( pageItem
, AuthorGroupItem::Author
);
557 authorItem
->setAuthor( author
);
560 pageItem
->appendChild( authorItem
);
562 // Insert to lookup list
563 pageAuthorMap
.insert( author
, authorItem
);
566 AuthorGroupItem
*item
= new AuthorGroupItem( authorItem
, AuthorGroupItem::Annotation
, annIdx
);
567 authorItem
->appendChild( item
);
572 for ( int row
= 0; row
< sourceModel()->rowCount(); ++row
) {
573 const QModelIndex idx
= sourceModel()->index( row
, 0 );
574 const QString author
= sourceModel()->data( idx
, AnnotationModel::AuthorRole
).toString();
575 if ( !author
.isEmpty() ) {
576 // We have the annotations as top-level items
577 AuthorGroupItem
*item
= new AuthorGroupItem( d
->mRoot
, AuthorGroupItem::Annotation
, idx
);
578 d
->mRoot
->appendChild( item
);
580 // We have the pages as top-level items
581 AuthorGroupItem
*pageItem
= new AuthorGroupItem( d
->mRoot
, AuthorGroupItem::Page
, idx
);
582 d
->mRoot
->appendChild( pageItem
);
584 // Append all annotations as second-level
585 for ( int subRow
= 0; subRow
< sourceModel()->rowCount( idx
); ++subRow
) {
586 const QModelIndex subIdx
= sourceModel()->index( subRow
, 0, idx
);
587 AuthorGroupItem
*item
= new AuthorGroupItem( pageItem
, AuthorGroupItem::Annotation
, subIdx
);
588 pageItem
->appendChild( item
);
597 #include "annotationproxymodels.moc"