okular/core/page.cpp

867 lines
24 KiB
C++
Raw Normal View History

/***************************************************************************
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include "page.h"
#include "page_p.h"
// qt/kde includes
#include <QtCore/QHash>
#include <QtCore/QSet>
#include <QtCore/QString>
#include <QtCore/QVariant>
#include <QtGui/QPixmap>
#include <QtXml/QDomDocument>
#include <QtXml/QDomElement>
#include <kdebug.h>
// local includes
#include "action.h"
#include "annotations.h"
#include "annotations_p.h"
#include "area.h"
#include "form.h"
#include "form_p.h"
#include "pagecontroller_p.h"
#include "pagesize.h"
#include "pagetransition.h"
#include "rotationjob_p.h"
#include "textpage.h"
#include "textpage_p.h"
#ifdef PAGE_PROFILE
#include <QtCore/QTime>
#endif
using namespace Okular;
static void deleteObjectRects( QLinkedList< ObjectRect * >& rects, const QSet<ObjectRect::ObjectType>& which )
{
QLinkedList< ObjectRect * >::iterator it = rects.begin(), end = rects.end();
for ( ; it != end; )
if ( which.contains( (*it)->objectType() ) )
{
delete *it;
it = rects.erase( it );
}
else
++it;
}
PagePrivate::PagePrivate( Page *page, uint n, double w, double h, Rotation o )
: m_page( page ), m_number( n ), m_orientation( o ),
m_width( w ), m_height( h ),
m_rotation( Rotation0 ), m_maxuniqueNum( 0 ),
m_text( 0 ), m_transition( 0 ), m_textSelections( 0 ),
m_openingAction( 0 ), m_closingAction( 0 ), m_duration( -1 )
{
// avoid Division-By-Zero problems in the program
if ( m_width <= 0 )
m_width = 1;
if ( m_height <= 0 )
m_height = 1;
}
PagePrivate::~PagePrivate()
{
qDeleteAll( formfields );
delete m_openingAction;
delete m_closingAction;
delete m_text;
delete m_transition;
}
void PagePrivate::imageRotationDone( RotationJob * job )
{
QMap< int, PixmapObject >::iterator it = m_pixmaps.find( job->id() );
if ( it != m_pixmaps.end() )
{
PixmapObject &object = it.value();
(*object.m_pixmap) = QPixmap::fromImage( job->image() );
object.m_rotation = job->rotation();
} else {
PixmapObject object;
object.m_pixmap = new QPixmap( QPixmap::fromImage( job->image() ) );
object.m_rotation = job->rotation();
m_pixmaps.insert( job->id(), object );
}
}
QMatrix PagePrivate::rotationMatrix() const
{
QMatrix matrix;
matrix.rotate( (int)m_rotation * 90 );
switch ( m_rotation )
{
case Rotation90:
matrix.translate( 0, -1 );
break;
case Rotation180:
matrix.translate( -1, -1 );
break;
case Rotation270:
matrix.translate( -1, 0 );
break;
default: ;
}
return matrix;
}
/** class Page **/
Page::Page( uint page, double w, double h, Rotation o )
: d( new PagePrivate( this, page, w, h, o ) )
{
}
Page::~Page()
{
deletePixmaps();
deleteRects();
d->deleteHighlights();
Adding support for annotations in framework. Only need to add and implement annotations now (and create the save/load procedure). Annotations: converging to a stable Annotation definition. Changed a bit the paint functions. Added a first 'template' annotation, a simple pen-like segments recorder for framework testing purposes only. This has events filters in place and the rough paint function implemented. PageView: removed the MouseEdit mode and using that button for toggling the editToolBox instead. Added Annotation support. When the Annotation is created, all pageView events flow through that new object. Repaint of damaged/old areas is done internally and is based on the geometry of the annotation we're creating. When an Annotation is complete, it is reparented to the Page that adds it to its internal list. From that point on the annotation will be rendered by pagePainter using the pixmap-based paint function provided by the annotation itself. PagePainter: draws annotations stored in pages when rendering (using the 'rought paint function' till the good pixmap based one will be in place. Page: added preliminary support for adding Annotation(s) to the page and deleting them all. Document: added the pass-through call to add an Annotation to the Page and notify observers. PageViewToolbox: can be draged and attached to any side. Position is remembered between runs (choose your side and that the toolbox will always be there). Available on Right and Bottom sides too. Emits -1 when the current tool is deselected. Misc: added Annotations to both the 'observers changed flags' and the 'pagepainter' ones and updated ui classes accordingly. svn path=/branches/kpdf_annotations/kdegraphics/kpdf/; revision=390638
2005-02-18 18:24:45 +00:00
deleteAnnotations();
deleteTextSelections();
deleteSourceReferences();
delete d;
}
int Page::number() const
{
return d->m_number;
}
Rotation Page::orientation() const
{
return d->m_orientation;
}
Rotation Page::rotation() const
{
return d->m_rotation;
}
Rotation Page::totalOrientation() const
{
return (Rotation)( ( (int)d->m_orientation + (int)d->m_rotation ) % 4 );
}
double Page::width() const
{
return d->m_width;
}
double Page::height() const
{
return d->m_height;
}
double Page::ratio() const
{
return d->m_height / d->m_width;
}
bool Page::hasPixmap( int id, int width, int height ) const
{
QMap< int, PagePrivate::PixmapObject >::const_iterator it = d->m_pixmaps.constFind( id );
if ( it == d->m_pixmaps.constEnd() )
return false;
if ( width == -1 || height == -1 )
return true;
const QPixmap *pixmap = it.value().m_pixmap;
return (pixmap->width() == width && pixmap->height() == height);
}
bool Page::hasTextPage() const
{
return d->m_text != 0;
}
RegularAreaRect * Page::textArea ( TextSelection * selection ) const
{
if ( d->m_text )
return d->m_text->textArea( selection );
return 0;
}
bool Page::hasObjectRect( double x, double y, double xScale, double yScale ) const
{
if ( m_rects.isEmpty() )
return false;
QLinkedList< ObjectRect * >::const_iterator it = m_rects.begin(), end = m_rects.end();
for ( ; it != end; ++it )
if ( (*it)->contains( x, y, xScale, yScale ) )
return true;
return false;
}
bool Page::hasHighlights( int s_id ) const
{
// simple case: have no highlights
if ( m_highlights.isEmpty() )
return false;
// simple case: we have highlights and no id to match
if ( s_id == -1 )
return true;
// iterate on the highlights list to find an entry by id
QLinkedList< HighlightAreaRect * >::const_iterator it = m_highlights.begin(), end = m_highlights.end();
for ( ; it != end; ++it )
if ( (*it)->s_id == s_id )
return true;
return false;
}
bool Page::hasTransition() const
{
return d->m_transition != 0;
}
bool Page::hasAnnotations() const
{
return !m_annotations.isEmpty();
}
RegularAreaRect * Page::findText( int id, const QString & text, SearchDirection direction,
Qt::CaseSensitivity caseSensitivity, const RegularAreaRect *lastRect ) const
{
RegularAreaRect* rect = 0;
if ( text.isEmpty() || !d->m_text )
return rect;
rect = d->m_text->findText( id, text, direction, caseSensitivity, lastRect );
return rect;
}
QString Page::text( const RegularAreaRect * area ) const
{
QString ret;
if ( !d->m_text )
return ret;
if ( area )
{
RegularAreaRect rotatedArea = *area;
rotatedArea.transform( d->rotationMatrix().inverted() );
ret = d->m_text->text( &rotatedArea );
}
else
ret = d->m_text->text();
return ret;
}
void PagePrivate::rotateAt( Rotation orientation )
{
if ( orientation == m_rotation )
return;
deleteHighlights();
m_page->deleteTextSelections();
if ( ( (int)m_orientation + (int)m_rotation ) % 2 != ( (int)m_orientation + (int)orientation ) % 2 )
qSwap( m_width, m_height );
Rotation oldRotation = m_rotation;
m_rotation = orientation;
/**
* Rotate the images of the page.
*/
QMapIterator< int, PagePrivate::PixmapObject > it( m_pixmaps );
while ( it.hasNext() ) {
it.next();
const PagePrivate::PixmapObject &object = it.value();
RotationJob *job = new RotationJob( object.m_pixmap->toImage(), object.m_rotation, m_rotation, it.key() );
job->setPage( this );
PageController::self()->addRotationJob(job);
}
/**
* Rotate the object rects on the page.
*/
const QMatrix matrix = rotationMatrix();
QLinkedList< ObjectRect * >::const_iterator objectIt = m_page->m_rects.begin(), end = m_page->m_rects.end();
for ( ; objectIt != end; ++objectIt )
(*objectIt)->transform( matrix );
if ( m_text )
{
m_text->d->m_transformMatrix = rotationMatrix();
}
QLinkedList< HighlightAreaRect* >::const_iterator hlIt = m_page->m_highlights.begin(), hlItEnd = m_page->m_highlights.end();
for ( ; hlIt != hlItEnd; ++hlIt )
{
(*hlIt)->transform( RotationJob::rotationMatrix( oldRotation, m_rotation ) );
}
}
void PagePrivate::changeSize( const PageSize &size )
{
if ( size.isNull() || ( size.width() == m_width && size.height() == m_height ) )
return;
m_page->deletePixmaps();
// deleteHighlights();
// deleteTextSelections();
m_width = size.width();
m_height = size.height();
if ( m_rotation % 2 )
qSwap( m_width, m_height );
}
const ObjectRect * Page::objectRect( ObjectRect::ObjectType type, double x, double y, double xScale, double yScale ) const
{
QLinkedList< ObjectRect * >::const_iterator it = m_rects.begin(), end = m_rects.end();
for ( ; it != end; ++it )
if ( ( (*it)->objectType() == type ) && (*it)->contains( x, y, xScale, yScale ) )
return *it;
return 0;
}
const PageTransition * Page::transition() const
{
return d->m_transition;
}
const QLinkedList< Annotation* > Page::annotations() const
{
return m_annotations;
}
const Action * Page::pageAction( PageAction action ) const
{
switch ( action )
{
case Page::Opening:
return d->m_openingAction;
break;
case Page::Closing:
return d->m_closingAction;
break;
}
return 0;
}
const QLinkedList< FormField * > Page::formFields() const
{
return d->formfields;
}
void Page::setPixmap( int id, QPixmap *pixmap )
{
if ( d->m_rotation == Rotation0 ) {
QMap< int, PagePrivate::PixmapObject >::iterator it = d->m_pixmaps.find( id );
if ( it != d->m_pixmaps.end() )
{
delete it.value().m_pixmap;
}
else
{
it = d->m_pixmaps.insert( id, PagePrivate::PixmapObject() );
}
it.value().m_pixmap = pixmap;
it.value().m_rotation = d->m_rotation;
} else {
RotationJob *job = new RotationJob( pixmap->toImage(), Rotation0, d->m_rotation, id );
job->setPage( d );
PageController::self()->addRotationJob(job);
delete pixmap;
}
}
void Page::setTextPage( TextPage * textPage )
{
delete d->m_text;
d->m_text = textPage;
if ( d->m_text )
{
d->m_text->d->m_transformMatrix = d->rotationMatrix();
}
}
void Page::setObjectRects( const QLinkedList< ObjectRect * > & rects )
{
QSet<ObjectRect::ObjectType> which;
which << ObjectRect::Action << ObjectRect::Image;
deleteObjectRects( m_rects, which );
/**
* Rotate the object rects of the page.
*/
const QMatrix matrix = d->rotationMatrix();
QLinkedList< ObjectRect * >::const_iterator objectIt = rects.begin(), end = rects.end();
for ( ; objectIt != end; ++objectIt )
(*objectIt)->transform( matrix );
m_rects << rects;
}
void PagePrivate::setHighlight( int s_id, RegularAreaRect *rect, const QColor & color )
{
HighlightAreaRect * hr = new HighlightAreaRect(rect);
hr->s_id = s_id;
hr->color = color;
m_page->m_highlights.append( hr );
}
void PagePrivate::setTextSelections( RegularAreaRect *r, const QColor & color )
{
m_page->deleteTextSelections();
if ( r )
{
HighlightAreaRect * hr = new HighlightAreaRect( r );
hr->s_id = -1;
hr->color = color;
m_textSelections = hr;
delete r;
}
}
void Page::setSourceReferences( const QLinkedList< SourceRefObjectRect * > & refRects )
{
deleteSourceReferences();
foreach( SourceRefObjectRect * rect, refRects )
m_rects << rect;
}
void Page::setDuration( double seconds )
{
d->m_duration = seconds;
}
double Page::duration() const
{
return d->m_duration;
}
void Page::setLabel( const QString& label )
{
d->m_label = label;
}
QString Page::label() const
{
return d->m_label;
}
const RegularAreaRect * Page::textSelection() const
{
return d->m_textSelections;
}
QColor Page::textSelectionColor() const
{
return d->m_textSelections ? d->m_textSelections->color : QColor();
}
void Page::addAnnotation( Annotation * annotation )
Adding support for annotations in framework. Only need to add and implement annotations now (and create the save/load procedure). Annotations: converging to a stable Annotation definition. Changed a bit the paint functions. Added a first 'template' annotation, a simple pen-like segments recorder for framework testing purposes only. This has events filters in place and the rough paint function implemented. PageView: removed the MouseEdit mode and using that button for toggling the editToolBox instead. Added Annotation support. When the Annotation is created, all pageView events flow through that new object. Repaint of damaged/old areas is done internally and is based on the geometry of the annotation we're creating. When an Annotation is complete, it is reparented to the Page that adds it to its internal list. From that point on the annotation will be rendered by pagePainter using the pixmap-based paint function provided by the annotation itself. PagePainter: draws annotations stored in pages when rendering (using the 'rought paint function' till the good pixmap based one will be in place. Page: added preliminary support for adding Annotation(s) to the page and deleting them all. Document: added the pass-through call to add an Annotation to the Page and notify observers. PageViewToolbox: can be draged and attached to any side. Position is remembered between runs (choose your side and that the toolbox will always be there). Available on Right and Bottom sides too. Emits -1 when the current tool is deselected. Misc: added Annotations to both the 'observers changed flags' and the 'pagepainter' ones and updated ui classes accordingly. svn path=/branches/kpdf_annotations/kdegraphics/kpdf/; revision=390638
2005-02-18 18:24:45 +00:00
{
//uniqueName: okular-PAGENUM-ID
if(annotation->uniqueName().isEmpty())
{
QString uniqueName = "okular-";
uniqueName += ( QString::number(d->m_number) + '-' + QString::number(++(d->m_maxuniqueNum)) );
kDebug().nospace() << "inc m_maxuniqueNum=" << d->m_maxuniqueNum;
annotation->setUniqueName( uniqueName );
}
annotation->d_ptr->m_page = d;
Adding support for annotations in framework. Only need to add and implement annotations now (and create the save/load procedure). Annotations: converging to a stable Annotation definition. Changed a bit the paint functions. Added a first 'template' annotation, a simple pen-like segments recorder for framework testing purposes only. This has events filters in place and the rough paint function implemented. PageView: removed the MouseEdit mode and using that button for toggling the editToolBox instead. Added Annotation support. When the Annotation is created, all pageView events flow through that new object. Repaint of damaged/old areas is done internally and is based on the geometry of the annotation we're creating. When an Annotation is complete, it is reparented to the Page that adds it to its internal list. From that point on the annotation will be rendered by pagePainter using the pixmap-based paint function provided by the annotation itself. PagePainter: draws annotations stored in pages when rendering (using the 'rought paint function' till the good pixmap based one will be in place. Page: added preliminary support for adding Annotation(s) to the page and deleting them all. Document: added the pass-through call to add an Annotation to the Page and notify observers. PageViewToolbox: can be draged and attached to any side. Position is remembered between runs (choose your side and that the toolbox will always be there). Available on Right and Bottom sides too. Emits -1 when the current tool is deselected. Misc: added Annotations to both the 'observers changed flags' and the 'pagepainter' ones and updated ui classes accordingly. svn path=/branches/kpdf_annotations/kdegraphics/kpdf/; revision=390638
2005-02-18 18:24:45 +00:00
m_annotations.append( annotation );
AnnotationObjectRect *rect = new AnnotationObjectRect( annotation );
/**
* Rotate the annotation on the page.
*/
const QMatrix matrix = d->rotationMatrix();
rect->transform( matrix );
m_rects.append( rect );
Adding support for annotations in framework. Only need to add and implement annotations now (and create the save/load procedure). Annotations: converging to a stable Annotation definition. Changed a bit the paint functions. Added a first 'template' annotation, a simple pen-like segments recorder for framework testing purposes only. This has events filters in place and the rough paint function implemented. PageView: removed the MouseEdit mode and using that button for toggling the editToolBox instead. Added Annotation support. When the Annotation is created, all pageView events flow through that new object. Repaint of damaged/old areas is done internally and is based on the geometry of the annotation we're creating. When an Annotation is complete, it is reparented to the Page that adds it to its internal list. From that point on the annotation will be rendered by pagePainter using the pixmap-based paint function provided by the annotation itself. PagePainter: draws annotations stored in pages when rendering (using the 'rought paint function' till the good pixmap based one will be in place. Page: added preliminary support for adding Annotation(s) to the page and deleting them all. Document: added the pass-through call to add an Annotation to the Page and notify observers. PageViewToolbox: can be draged and attached to any side. Position is remembered between runs (choose your side and that the toolbox will always be there). Available on Right and Bottom sides too. Emits -1 when the current tool is deselected. Misc: added Annotations to both the 'observers changed flags' and the 'pagepainter' ones and updated ui classes accordingly. svn path=/branches/kpdf_annotations/kdegraphics/kpdf/; revision=390638
2005-02-18 18:24:45 +00:00
}
void PagePrivate::modifyAnnotation(Annotation * newannotation )
{
if(!newannotation)
return;
QLinkedList< Annotation * >::iterator aIt = m_page->m_annotations.begin(), aEnd = m_page->m_annotations.end();
for ( ; aIt != aEnd; ++aIt )
{
if((*aIt)==newannotation)
return; //modified already
if((*aIt) && (*aIt)->uniqueName()==newannotation->uniqueName())
{
int rectfound = false;
QLinkedList< ObjectRect * >::iterator it = m_page->m_rects.begin(), end = m_page->m_rects.end();
for ( ; it != end && !rectfound; ++it )
if ( ( (*it)->objectType() == ObjectRect::OAnnotation ) && ( (*it)->object() == (*aIt) ) )
{
delete *it;
*it = new AnnotationObjectRect( newannotation );
rectfound = true;
}
delete *aIt;
*aIt = newannotation;
break;
}
}
}
bool Page::removeAnnotation( Annotation * annotation )
{
if ( !annotation || ( annotation->flags() & Annotation::DenyDelete ) )
return false;
QLinkedList< Annotation * >::iterator aIt = m_annotations.begin(), aEnd = m_annotations.end();
for ( ; aIt != aEnd; ++aIt )
{
if((*aIt) && (*aIt)->uniqueName()==annotation->uniqueName())
{
int rectfound = false;
QLinkedList< ObjectRect * >::iterator it = m_rects.begin(), end = m_rects.end();
for ( ; it != end && !rectfound; ++it )
if ( ( (*it)->objectType() == ObjectRect::OAnnotation ) && ( (*it)->object() == (*aIt) ) )
{
delete *it;
it = m_rects.erase( it );
rectfound = true;
}
kDebug() << "removed annotation:" << annotation->uniqueName();
delete *aIt;
m_annotations.erase( aIt );
break;
}
}
return true;
}
void Page::setTransition( PageTransition * transition )
{
delete d->m_transition;
d->m_transition = transition;
}
void Page::setPageAction( PageAction action, Action * link )
{
switch ( action )
{
case Page::Opening:
delete d->m_openingAction;
d->m_openingAction = link;
break;
case Page::Closing:
delete d->m_closingAction;
d->m_closingAction = link;
break;
}
}
void Page::setFormFields( const QLinkedList< FormField * >& fields )
{
qDeleteAll( d->formfields );
d->formfields = fields;
QLinkedList< FormField * >::const_iterator it = d->formfields.begin(), itEnd = d->formfields.end();
for ( ; it != itEnd; ++it )
{
(*it)->d_ptr->setDefault();
}
}
void Page::deletePixmap( int id )
{
PagePrivate::PixmapObject object = d->m_pixmaps.take( id );
delete object.m_pixmap;
}
void Page::deletePixmaps()
{
QMapIterator< int, PagePrivate::PixmapObject > it( d->m_pixmaps );
while ( it.hasNext() ) {
it.next();
delete it.value().m_pixmap;
}
d->m_pixmaps.clear();
}
void Page::deleteRects()
{
// delete ObjectRects of type Link and Image
QSet<ObjectRect::ObjectType> which;
which << ObjectRect::Action << ObjectRect::Image;
deleteObjectRects( m_rects, which );
}
void PagePrivate::deleteHighlights( int s_id )
{
// delete highlights by ID
QLinkedList< HighlightAreaRect* >::iterator it = m_page->m_highlights.begin(), end = m_page->m_highlights.end();
while ( it != end )
{
HighlightAreaRect* highlight = *it;
if ( s_id == -1 || highlight->s_id == s_id )
{
it = m_page->m_highlights.erase( it );
delete highlight;
}
else
++it;
}
}
void Page::deleteTextSelections()
{
delete d->m_textSelections;
d->m_textSelections = 0;
}
void Page::deleteSourceReferences()
{
deleteObjectRects( m_rects, QSet<ObjectRect::ObjectType>() << ObjectRect::SourceRef );
}
void Page::deleteAnnotations()
Adding support for annotations in framework. Only need to add and implement annotations now (and create the save/load procedure). Annotations: converging to a stable Annotation definition. Changed a bit the paint functions. Added a first 'template' annotation, a simple pen-like segments recorder for framework testing purposes only. This has events filters in place and the rough paint function implemented. PageView: removed the MouseEdit mode and using that button for toggling the editToolBox instead. Added Annotation support. When the Annotation is created, all pageView events flow through that new object. Repaint of damaged/old areas is done internally and is based on the geometry of the annotation we're creating. When an Annotation is complete, it is reparented to the Page that adds it to its internal list. From that point on the annotation will be rendered by pagePainter using the pixmap-based paint function provided by the annotation itself. PagePainter: draws annotations stored in pages when rendering (using the 'rought paint function' till the good pixmap based one will be in place. Page: added preliminary support for adding Annotation(s) to the page and deleting them all. Document: added the pass-through call to add an Annotation to the Page and notify observers. PageViewToolbox: can be draged and attached to any side. Position is remembered between runs (choose your side and that the toolbox will always be there). Available on Right and Bottom sides too. Emits -1 when the current tool is deselected. Misc: added Annotations to both the 'observers changed flags' and the 'pagepainter' ones and updated ui classes accordingly. svn path=/branches/kpdf_annotations/kdegraphics/kpdf/; revision=390638
2005-02-18 18:24:45 +00:00
{
// delete ObjectRects of type Annotation
deleteObjectRects( m_rects, QSet<ObjectRect::ObjectType>() << ObjectRect::OAnnotation );
Adding support for annotations in framework. Only need to add and implement annotations now (and create the save/load procedure). Annotations: converging to a stable Annotation definition. Changed a bit the paint functions. Added a first 'template' annotation, a simple pen-like segments recorder for framework testing purposes only. This has events filters in place and the rough paint function implemented. PageView: removed the MouseEdit mode and using that button for toggling the editToolBox instead. Added Annotation support. When the Annotation is created, all pageView events flow through that new object. Repaint of damaged/old areas is done internally and is based on the geometry of the annotation we're creating. When an Annotation is complete, it is reparented to the Page that adds it to its internal list. From that point on the annotation will be rendered by pagePainter using the pixmap-based paint function provided by the annotation itself. PagePainter: draws annotations stored in pages when rendering (using the 'rought paint function' till the good pixmap based one will be in place. Page: added preliminary support for adding Annotation(s) to the page and deleting them all. Document: added the pass-through call to add an Annotation to the Page and notify observers. PageViewToolbox: can be draged and attached to any side. Position is remembered between runs (choose your side and that the toolbox will always be there). Available on Right and Bottom sides too. Emits -1 when the current tool is deselected. Misc: added Annotations to both the 'observers changed flags' and the 'pagepainter' ones and updated ui classes accordingly. svn path=/branches/kpdf_annotations/kdegraphics/kpdf/; revision=390638
2005-02-18 18:24:45 +00:00
// delete all stored annotations
QLinkedList< Annotation * >::const_iterator aIt = m_annotations.begin(), aEnd = m_annotations.end();
Adding support for annotations in framework. Only need to add and implement annotations now (and create the save/load procedure). Annotations: converging to a stable Annotation definition. Changed a bit the paint functions. Added a first 'template' annotation, a simple pen-like segments recorder for framework testing purposes only. This has events filters in place and the rough paint function implemented. PageView: removed the MouseEdit mode and using that button for toggling the editToolBox instead. Added Annotation support. When the Annotation is created, all pageView events flow through that new object. Repaint of damaged/old areas is done internally and is based on the geometry of the annotation we're creating. When an Annotation is complete, it is reparented to the Page that adds it to its internal list. From that point on the annotation will be rendered by pagePainter using the pixmap-based paint function provided by the annotation itself. PagePainter: draws annotations stored in pages when rendering (using the 'rought paint function' till the good pixmap based one will be in place. Page: added preliminary support for adding Annotation(s) to the page and deleting them all. Document: added the pass-through call to add an Annotation to the Page and notify observers. PageViewToolbox: can be draged and attached to any side. Position is remembered between runs (choose your side and that the toolbox will always be there). Available on Right and Bottom sides too. Emits -1 when the current tool is deselected. Misc: added Annotations to both the 'observers changed flags' and the 'pagepainter' ones and updated ui classes accordingly. svn path=/branches/kpdf_annotations/kdegraphics/kpdf/; revision=390638
2005-02-18 18:24:45 +00:00
for ( ; aIt != aEnd; ++aIt )
delete *aIt;
m_annotations.clear();
}
void PagePrivate::restoreLocalContents( const QDomNode & pageNode )
{
// iterate over all chilren (annotationList, ...)
QDomNode childNode = pageNode.firstChild();
while ( childNode.isElement() )
{
QDomElement childElement = childNode.toElement();
childNode = childNode.nextSibling();
// parse annotationList child element
if ( childElement.tagName() == "annotationList" )
{
#ifdef PAGE_PROFILE
QTime time;
time.start();
#endif
// iterate over all annotations
QDomNode annotationNode = childElement.firstChild();
while( annotationNode.isElement() )
{
// get annotation element and advance to next annot
QDomElement annotElement = annotationNode.toElement();
annotationNode = annotationNode.nextSibling();
// get annotation from the dom element
Annotation * annotation = AnnotationUtils::createAnnotation( annotElement );
// append annotation to the list or show warning
if ( annotation )
{
annotation->d_ptr->m_page = this;
m_page->m_annotations.append( annotation );
m_page->m_rects.append( new AnnotationObjectRect( annotation ) );
int pos = annotation->uniqueName().lastIndexOf("-");
if(pos != -1)
{
int uniqID=annotation->uniqueName().right(annotation->uniqueName().length()-pos-1).toInt();
if ( m_maxuniqueNum < uniqID )
m_maxuniqueNum = uniqID;
}
kDebug() << "restored annot:" << annotation->uniqueName();
}
else
kWarning().nospace() << "page (" << m_number << "): can't restore an annotation from XML.";
}
#ifdef PAGE_PROFILE
kDebug().nospace() << "annots: XML Load time: " << time.elapsed() << "ms";
#endif
}
// parse formList child element
else if ( childElement.tagName() == "forms" )
{
if ( formfields.isEmpty() )
continue;
QHash<int, FormField*> hashedforms;
QLinkedList< FormField * >::const_iterator fIt = formfields.begin(), fItEnd = formfields.end();
for ( ; fIt != fItEnd; ++fIt )
{
hashedforms[(*fIt)->id()] = (*fIt);
}
// iterate over all forms
QDomNode formsNode = childElement.firstChild();
while( formsNode.isElement() )
{
// get annotation element and advance to next annot
QDomElement formElement = formsNode.toElement();
formsNode = formsNode.nextSibling();
if ( formElement.tagName() != "form" )
continue;
bool ok = true;
int index = formElement.attribute( "id" ).toInt( &ok );
if ( !ok )
continue;
QHash<int, FormField*>::const_iterator wantedIt = hashedforms.find( index );
if ( wantedIt == hashedforms.end() )
continue;
QString value = formElement.attribute( "value" );
(*wantedIt)->d_ptr->setValue( value );
}
}
}
}
void PagePrivate::saveLocalContents( QDomNode & parentNode, QDomDocument & document ) const
{
// only add a node if there is some stuff to write into
if ( m_page->m_annotations.isEmpty() && formfields.isEmpty() )
return;
// create the page node and set the 'number' attribute
QDomElement pageElement = document.createElement( "page" );
pageElement.setAttribute( "number", m_number );
#if 0
// add bookmark info if is bookmarked
if ( d->m_bookmarked )
{
// create the pageElement's 'bookmark' child
QDomElement bookmarkElement = document.createElement( "bookmark" );
pageElement.appendChild( bookmarkElement );
// add attributes to the element
//bookmarkElement.setAttribute( "name", bookmark name );
}
#endif
// add annotations info if has got any
if ( !m_page->m_annotations.isEmpty() )
{
// create the annotationList
QDomElement annotListElement = document.createElement( "annotationList" );
// add every annotation to the annotationList
QLinkedList< Annotation * >::const_iterator aIt = m_page->m_annotations.begin(), aEnd = m_page->m_annotations.end();
for ( ; aIt != aEnd; ++aIt )
{
// get annotation
const Annotation * a = *aIt;
// only save okular annotations (not the embedded in file ones)
if ( !(a->flags() & Annotation::External) )
{
// append an filled-up element called 'annotation' to the list
QDomElement annElement = document.createElement( "annotation" );
AnnotationUtils::storeAnnotation( a, annElement, document );
annotListElement.appendChild( annElement );
kDebug() << "save annotation:" << a->uniqueName();
}
}
// append the annotationList element if annotations have been set
if ( annotListElement.hasChildNodes() )
pageElement.appendChild( annotListElement );
}
// add forms info if has got any
if ( !formfields.isEmpty() )
{
// create the formList
QDomElement formListElement = document.createElement( "forms" );
// add every form data to the formList
QLinkedList< FormField * >::const_iterator fIt = formfields.begin(), fItEnd = formfields.end();
for ( ; fIt != fItEnd; ++fIt )
{
// get the form field
const FormField * f = *fIt;
QString newvalue = f->d_ptr->value();
if ( f->d_ptr->m_default == newvalue )
continue;
// append an filled-up element called 'annotation' to the list
QDomElement formElement = document.createElement( "form" );
formElement.setAttribute( "id", f->id() );
formElement.setAttribute( "value", newvalue );
formListElement.appendChild( formElement );
}
// append the annotationList element if annotations have been set
if ( formListElement.hasChildNodes() )
pageElement.appendChild( formListElement );
}
// append the page element only if has children
if ( pageElement.hasChildNodes() )
parentNode.appendChild( pageElement );
}
const QPixmap * Page::_o_nearestPixmap( int pixID, int w, int h ) const
{
Q_UNUSED( h )
const QPixmap * pixmap = 0;
// if a pixmap is present for given id, use it
QMap< int, PagePrivate::PixmapObject >::const_iterator itPixmap = d->m_pixmaps.find( pixID );
if ( itPixmap != d->m_pixmaps.end() )
pixmap = itPixmap.value().m_pixmap;
// else find the closest match using pixmaps of other IDs (great optim!)
else if ( !d->m_pixmaps.isEmpty() )
{
int minDistance = -1;
QMap< int, PagePrivate::PixmapObject >::const_iterator it = d->m_pixmaps.begin(), end = d->m_pixmaps.end();
for ( ; it != end; ++it )
{
int pixWidth = (*it).m_pixmap->width(),
distance = pixWidth > w ? pixWidth - w : w - pixWidth;
if ( minDistance == -1 || distance < minDistance )
{
pixmap = (*it).m_pixmap;
minDistance = distance;
}
}
}
return pixmap;
}