compile
[kdegraphics.git] / okular / ui / annotationproxymodels.cpp
blob22337a5fa63509fa1385d01f07cb0409bf4a621f
1 /***************************************************************************
2 * Copyright (C) 2007 by Tobias Koenig <tokoe@kde.org> *
3 * *
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>
15 #include <kicon.h>
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 ),
27 mCurrentPage( -1 )
29 setDynamicSortFilter( true );
32 void PageFilterProxyModel::groupByCurrentPage( bool value )
34 if ( mGroupByCurrentPage == value )
35 return;
37 mGroupByCurrentPage = value;
39 invalidateFilter();
42 void PageFilterProxyModel::setCurrentPage( int page )
44 if ( mCurrentPage == page )
45 return;
47 mCurrentPage = page;
49 // no need to invalidate when we're not showing the current page only
50 if ( !mGroupByCurrentPage )
51 return;
53 invalidateFilter();
56 bool PageFilterProxyModel::filterAcceptsRow( int row, const QModelIndex &sourceParent ) const
58 if ( !mGroupByCurrentPage )
59 return true;
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 ),
70 mGroupByPage( false )
74 int PageGroupProxyModel::columnCount( const QModelIndex &parentIndex ) const
76 // For top-level and second level we have always only one column
77 if ( mGroupByPage ) {
78 if ( parentIndex.isValid() ) {
79 if ( parentIndex.parent().isValid() )
80 return 0;
81 else {
82 return 1; // second-level
84 } else {
85 return 1; // top-level
87 } else {
88 if ( !parentIndex.isValid() ) // top-level
89 return 1;
90 else
91 return 0;
93 return 1;
96 int PageGroupProxyModel::rowCount( const QModelIndex &parentIndex ) const
98 if ( mGroupByPage ) {
99 if ( parentIndex.isValid() ) {
100 if ( parentIndex.parent().isValid() )
101 return 0;
102 else {
103 return mTreeIndexes[ parentIndex.row() ].second.count(); // second-level
105 } else {
106 return mTreeIndexes.count(); // top-level
108 } else {
109 if ( !parentIndex.isValid() ) // top-level
110 return mIndexes.count();
111 else
112 return 0;
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 ) );
126 else
127 return QModelIndex();
128 } else {
129 if ( row < mTreeIndexes.count() )
130 return createIndex( row, column, 0 );
131 else
132 return QModelIndex();
134 } else {
135 if ( row < mIndexes.count() )
136 return createIndex( row, column, mixIndex( parentIndex.row(), parentIndex.column() ) );
137 else
138 return QModelIndex();
142 QModelIndex PageGroupProxyModel::parent( const QModelIndex &idx ) const
144 if ( mGroupByPage ) {
145 if ( idx.internalId() == 0 ) // top-level
146 return QModelIndex();
147 else
148 return index( idx.internalId() - 1, idx.column() );
149 } else {
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() );
160 } else {
161 return index( sourceIndex.row(), sourceIndex.column() );
163 } else {
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;
185 } else {
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() ];
192 } else {
193 if ( proxyIndex.column() > 0 || proxyIndex.row() >= mIndexes.count() )
194 return QModelIndex();
195 else {
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() ) );
217 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 ) );
235 } else {
236 mIndexes.clear();
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 ) );
246 reset();
249 void PageGroupProxyModel::groupByPage( bool value )
251 if ( mGroupByPage == value )
252 return;
254 mGroupByPage = value;
256 rebuildIndexes();
260 class AuthorGroupItem
262 public:
263 enum Type
265 Page,
266 Author,
267 Annotation
270 AuthorGroupItem( AuthorGroupItem *parent, Type type = Page, const QModelIndex &index = QModelIndex() )
271 : mParent( parent ), mType( type ), mIndex( index )
275 ~AuthorGroupItem()
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 )
287 QString prefix;
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 )
299 return this;
301 for ( int i = 0; i < mChilds.count(); ++i ) {
302 const AuthorGroupItem *item = mChilds[ i ]->findIndex( index );
303 if ( item )
304 return item;
307 return 0;
310 int row() const
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; }
321 private:
322 AuthorGroupItem *mParent;
323 Type mType;
324 QModelIndex mIndex;
325 QList<AuthorGroupItem*> mChilds;
326 QString mAuthor;
329 class AuthorGroupProxyModel::Private
331 public:
332 Private( AuthorGroupProxyModel *parent )
333 : mParent( parent ), mRoot( 0 ),
334 mGroupByAuthor( false )
337 ~Private()
339 delete mRoot;
342 AuthorGroupProxyModel *mParent;
343 AuthorGroupItem *mRoot;
344 bool mGroupByAuthor;
347 AuthorGroupProxyModel::AuthorGroupProxyModel( QObject *parent )
348 : QAbstractProxyModel( parent ),
349 d( new Private( this ) )
353 AuthorGroupProxyModel::~AuthorGroupProxyModel()
355 delete d;
358 int AuthorGroupProxyModel::columnCount( const QModelIndex& ) const
360 return 1;
363 int AuthorGroupProxyModel::rowCount( const QModelIndex &parentIndex ) const
365 AuthorGroupItem *item = 0;
366 if ( !parentIndex.isValid() )
367 item = d->mRoot;
368 else
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;
382 else
383 parentItem = static_cast<AuthorGroupItem*>( parentIndex.internalPointer() );
385 AuthorGroupItem *child = parentItem->child( row );
386 if ( child )
387 return createIndex( row, column, child );
388 else
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();
402 else
403 return createIndex( parentItem->row(), 0, parentItem );
406 QModelIndex AuthorGroupProxyModel::mapFromSource( const QModelIndex &sourceIndex ) const
408 const AuthorGroupItem *item = d->mRoot->findIndex( sourceIndex );
409 if ( !item )
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() ) );
441 rebuildIndexes();
444 static bool isAuthorItem( const QModelIndex &index )
446 if ( !index.isValid() ) {
447 return false;
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" );
479 else
480 return QVariant();
481 } else {
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>();
490 } else {
491 return QAbstractProxyModel::itemData( index );
495 Qt::ItemFlags AuthorGroupProxyModel::flags( const QModelIndex &index ) const
497 if ( isAuthorItem( index ) ) {
498 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
499 } else {
500 return QAbstractProxyModel::flags( index );
504 void AuthorGroupProxyModel::groupByAuthor( bool value )
506 if ( d->mGroupByAuthor == value )
507 return;
509 d->mGroupByAuthor = value;
511 rebuildIndexes();
514 void AuthorGroupProxyModel::rebuildIndexes()
516 delete d->mRoot;
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 );
529 if ( !authorItem ) {
530 authorItem = new AuthorGroupItem( d->mRoot, AuthorGroupItem::Author );
531 authorItem->setAuthor( author );
533 // Add item to tree
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 );
542 } else {
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 );
555 if ( !authorItem ) {
556 authorItem = new AuthorGroupItem( pageItem, AuthorGroupItem::Author );
557 authorItem->setAuthor( author );
559 // Add item to tree
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 );
571 } else {
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 );
579 } else {
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 );
594 reset();
597 #include "annotationproxymodels.moc"