compile
[kdegraphics.git] / okular / ui / annotationmodel.cpp
blob37a48f9c1adab96159c800fbf15346049d514987
1 /***************************************************************************
2 * Copyright (C) 2006 by Pino Toscano <pino@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 "annotationmodel.h"
12 #include <qlinkedlist.h>
13 #include <qlist.h>
14 #include <qpointer.h>
16 #include <kicon.h>
17 #include <klocale.h>
19 #include "core/annotations.h"
20 #include "core/document.h"
21 #include "core/observer.h"
22 #include "core/page.h"
23 #include "ui/guiutils.h"
25 struct AnnItem
27 AnnItem();
28 AnnItem( AnnItem *parent, Okular::Annotation *ann );
29 AnnItem( AnnItem *parent, int page );
30 ~AnnItem();
32 AnnItem *parent;
33 QList< AnnItem* > children;
35 Okular::Annotation *annotation;
36 int page;
40 class AnnotationModelPrivate : public Okular::DocumentObserver
42 public:
43 AnnotationModelPrivate( AnnotationModel *qq );
44 virtual ~AnnotationModelPrivate();
46 virtual uint observerId() const;
47 virtual void notifySetup( const QVector< Okular::Page * > &pages, int setupFlags );
48 virtual void notifyPageChanged( int page, int flags );
50 QModelIndex indexForItem( AnnItem *item ) const;
51 void rebuildTree( const QVector< Okular::Page * > &pages );
52 AnnItem* findItem( int page, int *index ) const;
54 AnnotationModel *q;
55 AnnItem *root;
56 QPointer< Okular::Document > document;
60 AnnItem::AnnItem()
61 : parent( 0 ), annotation( 0 ), page( -1 )
65 AnnItem::AnnItem( AnnItem *_parent, Okular::Annotation *ann )
66 : parent( _parent ), annotation( ann ), page( _parent->page )
68 Q_ASSERT( !parent->annotation );
69 parent->children.append( this );
72 AnnItem::AnnItem( AnnItem *_parent, int _page )
73 : parent( _parent ), annotation( 0 ), page( _page )
75 Q_ASSERT( !parent->parent );
76 parent->children.append( this );
79 AnnItem::~AnnItem()
81 qDeleteAll( children );
85 AnnotationModelPrivate::AnnotationModelPrivate( AnnotationModel *qq )
86 : q( qq ), root( new AnnItem )
90 AnnotationModelPrivate::~AnnotationModelPrivate()
92 delete root;
95 uint AnnotationModelPrivate::observerId() const
97 return ANNOTATIONMODEL_ID;
100 void AnnotationModelPrivate::notifySetup( const QVector< Okular::Page * > &pages, int setupFlags )
102 if ( !( setupFlags & Okular::DocumentObserver::DocumentChanged ) )
103 return;
105 qDeleteAll( root->children );
106 root->children.clear();
107 q->reset();
109 rebuildTree( pages );
112 void AnnotationModelPrivate::notifyPageChanged( int page, int flags )
114 // we are strictly interested in annotations
115 if ( !(flags & Okular::DocumentObserver::Annotations ) )
116 return;
118 QLinkedList< Okular::Annotation* > annots = document->page( page )->annotations();
119 int annItemIndex = -1;
120 AnnItem *annItem = findItem( page, &annItemIndex );
121 // case 1: the page has no more annotations
122 // => remove the branch, if any
123 if ( annots.isEmpty() )
125 if ( annItem )
127 q->beginRemoveRows( indexForItem( root ), annItemIndex, annItemIndex );
128 delete root->children.at( annItemIndex );
129 root->children.removeAt( annItemIndex );
130 q->endRemoveRows();
132 return;
134 // case 2: no existing branch
135 // => add a new branch, and add the annotations for the page
136 if ( !annItem )
138 int i = 0;
139 while ( i < root->children.count() && root->children.at( i )->page < page ) ++i;
141 AnnItem *annItem = new AnnItem();
142 annItem->page = page;
143 annItem->parent = root;
144 q->beginInsertRows( indexForItem( root ), i, i );
145 annItem->parent->children.insert( i, annItem );
146 q->endInsertRows();
147 QLinkedList< Okular::Annotation* >::ConstIterator it = annots.begin(), itEnd = annots.end();
148 int newid = 0;
149 for ( ; it != itEnd; ++it, ++newid )
151 q->beginInsertRows( indexForItem( annItem ), newid, newid );
152 new AnnItem( annItem, *it );
153 q->endInsertRows();
155 return;
157 // case 3: existing branch, less annotations than items
158 // => lookup and remove the annotations
159 if ( annItem->children.count() > annots.count() )
161 for ( int i = annItem->children.count(); i > 0; --i )
163 Okular::Annotation *ref = annItem->children.at( i - 1 )->annotation;
164 bool found = false;
165 QLinkedList< Okular::Annotation* >::ConstIterator it = annots.begin(), itEnd = annots.end();
166 for ( ; !found && it != itEnd; ++it )
168 if ( ( *it ) == ref )
169 found = true;
171 if ( !found )
173 q->beginRemoveRows( indexForItem( annItem ), i - 1, i - 1 );
174 delete annItem->children.at( i - 1 );
175 annItem->children.removeAt( i - 1 );
176 q->endRemoveRows();
179 return;
181 // case 4: existing branch, less items than annotations
182 // => lookup and add annotations if not in the branch
183 if ( annots.count() > annItem->children.count() )
185 QLinkedList< Okular::Annotation* >::ConstIterator it = annots.begin(), itEnd = annots.end();
186 for ( ; it != itEnd; ++it )
188 Okular::Annotation *ref = *it;
189 bool found = false;
190 int count = annItem->children.count();
191 for ( int i = 0; !found && i < count; ++i )
193 if ( ref == annItem->children.at( i )->annotation )
194 found = true;
196 if ( !found )
198 q->beginInsertRows( indexForItem( annItem ), count, count );
199 new AnnItem( annItem, ref );
200 q->endInsertRows();
203 return;
205 // case 5: the data of some annotation changed
206 // TODO: what do we do in this case?
207 // FIXME: for now, update ALL the annotations for that page
208 for ( int i = 0; i < annItem->children.count(); ++i )
210 QModelIndex index = indexForItem( annItem->children.at( i ) );
211 emit q->dataChanged( index, index );
215 QModelIndex AnnotationModelPrivate::indexForItem( AnnItem *item ) const
217 if ( item->parent )
219 int id = item->parent->children.indexOf( item );
220 if ( id >= 0 && id < item->parent->children.count() )
221 return q->createIndex( id, 0, item );
223 return QModelIndex();
226 void AnnotationModelPrivate::rebuildTree( const QVector< Okular::Page * > &pages )
228 if ( pages.isEmpty() )
229 return;
231 emit q->layoutAboutToBeChanged();
232 for ( int i = 0; i < pages.count(); ++i )
234 QLinkedList< Okular::Annotation* > annots = pages.at( i )->annotations();
235 if ( annots.isEmpty() )
236 continue;
238 AnnItem *annItem = new AnnItem( root, i );
239 QLinkedList< Okular::Annotation* >::ConstIterator it = annots.begin(), itEnd = annots.end();
240 for ( ; it != itEnd; ++it )
242 new AnnItem( annItem, *it );
245 emit q->layoutChanged();
248 AnnItem* AnnotationModelPrivate::findItem( int page, int *index ) const
250 for ( int i = 0; i < root->children.count(); ++i )
252 AnnItem *tmp = root->children.at( i );
253 if ( tmp->page == page )
255 if ( index )
256 *index = i;
257 return tmp;
260 if ( index )
261 *index = -1;
262 return 0;
266 AnnotationModel::AnnotationModel( Okular::Document *document, QObject *parent )
267 : QAbstractItemModel( parent ), d( new AnnotationModelPrivate( this ) )
269 d->document = document;
271 d->document->addObserver( d );
274 AnnotationModel::~AnnotationModel()
276 if ( d->document )
277 d->document->removeObserver( d );
279 delete d;
282 int AnnotationModel::columnCount( const QModelIndex &parent ) const
284 Q_UNUSED( parent )
285 return 1;
288 QVariant AnnotationModel::data( const QModelIndex &index, int role ) const
290 if ( !index.isValid() )
291 return QVariant();
293 AnnItem *item = static_cast< AnnItem* >( index.internalPointer() );
294 if ( !item->annotation )
296 if ( role == Qt::DisplayRole )
297 return i18n( "Page %1", item->page + 1 );
298 else if ( role == Qt::DecorationRole )
299 return KIcon( "text-plain" );
300 else if ( role == PageRole )
301 return item->page;
303 return QVariant();
305 switch ( role )
307 case Qt::DisplayRole:
308 return GuiUtils::captionForAnnotation( item->annotation );
309 break;
310 case Qt::DecorationRole:
311 return KIcon( "okular" );
312 break;
313 case Qt::ToolTipRole:
314 return GuiUtils::prettyToolTip( item->annotation );
315 break;
316 case AuthorRole:
317 return item->annotation->author();
318 break;
319 case PageRole:
320 return item->page;
321 break;
323 return QVariant();
326 bool AnnotationModel::hasChildren( const QModelIndex &parent ) const
328 if ( !parent.isValid() )
329 return true;
331 AnnItem *item = static_cast< AnnItem* >( parent.internalPointer() );
332 return !item->children.isEmpty();
335 QVariant AnnotationModel::headerData( int section, Qt::Orientation orientation, int role ) const
337 if ( orientation != Qt::Horizontal )
338 return QVariant();
340 if ( section == 0 && role == Qt::DisplayRole )
341 return "Annotations";
343 return QVariant();
346 QModelIndex AnnotationModel::index( int row, int column, const QModelIndex &parent ) const
348 if ( row < 0 || column != 0 )
349 return QModelIndex();
351 AnnItem *item = parent.isValid() ? static_cast< AnnItem* >( parent.internalPointer() ) : d->root;
352 if ( row < item->children.count() )
353 return createIndex( row, column, item->children.at( row ) );
355 return QModelIndex();
358 QModelIndex AnnotationModel::parent( const QModelIndex &index ) const
360 if ( !index.isValid() )
361 return QModelIndex();
363 AnnItem *item = static_cast< AnnItem* >( index.internalPointer() );
364 return d->indexForItem( item->parent );
367 int AnnotationModel::rowCount( const QModelIndex &parent ) const
369 AnnItem *item = parent.isValid() ? static_cast< AnnItem* >( parent.internalPointer() ) : d->root;
370 return item->children.count();
373 bool AnnotationModel::isAnnotation( const QModelIndex &index ) const
375 return annotationForIndex( index );
378 Okular::Annotation* AnnotationModel::annotationForIndex( const QModelIndex &index ) const
380 if ( !index.isValid() )
381 return 0;
383 AnnItem *item = static_cast< AnnItem* >( index.internalPointer() );
384 return item->annotation;
387 #include "annotationmodel.moc"