1 /***************************************************************************
2 * Copyright (C) 2006 by Pino Toscano <pino@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 "annotationmodel.h"
12 #include <qlinkedlist.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"
28 AnnItem( AnnItem
*parent
, Okular::Annotation
*ann
);
29 AnnItem( AnnItem
*parent
, int page
);
33 QList
< AnnItem
* > children
;
35 Okular::Annotation
*annotation
;
40 class AnnotationModelPrivate
: public Okular::DocumentObserver
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;
56 QPointer
< Okular::Document
> document
;
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 );
81 qDeleteAll( children
);
85 AnnotationModelPrivate::AnnotationModelPrivate( AnnotationModel
*qq
)
86 : q( qq
), root( new AnnItem
)
90 AnnotationModelPrivate::~AnnotationModelPrivate()
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
) )
105 qDeleteAll( root
->children
);
106 root
->children
.clear();
109 rebuildTree( pages
);
112 void AnnotationModelPrivate::notifyPageChanged( int page
, int flags
)
114 // we are strictly interested in annotations
115 if ( !(flags
& Okular::DocumentObserver::Annotations
) )
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() )
127 q
->beginRemoveRows( indexForItem( root
), annItemIndex
, annItemIndex
);
128 delete root
->children
.at( annItemIndex
);
129 root
->children
.removeAt( annItemIndex
);
134 // case 2: no existing branch
135 // => add a new branch, and add the annotations for the page
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
);
147 QLinkedList
< Okular::Annotation
* >::ConstIterator it
= annots
.begin(), itEnd
= annots
.end();
149 for ( ; it
!= itEnd
; ++it
, ++newid
)
151 q
->beginInsertRows( indexForItem( annItem
), newid
, newid
);
152 new AnnItem( annItem
, *it
);
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
;
165 QLinkedList
< Okular::Annotation
* >::ConstIterator it
= annots
.begin(), itEnd
= annots
.end();
166 for ( ; !found
&& it
!= itEnd
; ++it
)
168 if ( ( *it
) == ref
)
173 q
->beginRemoveRows( indexForItem( annItem
), i
- 1, i
- 1 );
174 delete annItem
->children
.at( i
- 1 );
175 annItem
->children
.removeAt( i
- 1 );
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
;
190 int count
= annItem
->children
.count();
191 for ( int i
= 0; !found
&& i
< count
; ++i
)
193 if ( ref
== annItem
->children
.at( i
)->annotation
)
198 q
->beginInsertRows( indexForItem( annItem
), count
, count
);
199 new AnnItem( annItem
, ref
);
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
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() )
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() )
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
)
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()
277 d
->document
->removeObserver( d
);
282 int AnnotationModel::columnCount( const QModelIndex
&parent
) const
288 QVariant
AnnotationModel::data( const QModelIndex
&index
, int role
) const
290 if ( !index
.isValid() )
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
)
307 case Qt::DisplayRole
:
308 return GuiUtils::captionForAnnotation( item
->annotation
);
310 case Qt::DecorationRole
:
311 return KIcon( "okular" );
313 case Qt::ToolTipRole
:
314 return GuiUtils::prettyToolTip( item
->annotation
);
317 return item
->annotation
->author();
326 bool AnnotationModel::hasChildren( const QModelIndex
&parent
) const
328 if ( !parent
.isValid() )
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
)
340 if ( section
== 0 && role
== Qt::DisplayRole
)
341 return "Annotations";
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() )
383 AnnItem
*item
= static_cast< AnnItem
* >( index
.internalPointer() );
384 return item
->annotation
;
387 #include "annotationmodel.moc"