okular/core/page.cpp
Enrico Ros fbc7d45083 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

353 lines
10 KiB
C++

/***************************************************************************
* 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. *
***************************************************************************/
// qt/kde includes
#include <qpixmap.h>
#include <qstring.h>
#include <qmap.h>
#include <kdebug.h>
// local includes
#include "page.h"
#include "pagetransition.h"
#include "link.h"
#include "annotations.h"
#include "conf/settings.h"
#include "xpdf/TextOutputDev.h"
/** class KPDFPage **/
KPDFPage::KPDFPage( uint page, float w, float h, int r )
: m_number( page ), m_rotation( r ), m_width( w ), m_height( h ),
m_bookmarked( false ), m_text( 0 ), m_transition( 0 )
{
// if landscape swap width <-> height (rotate 90deg CCW)
if ( r == 90 || r == 270 )
{
m_width = h;
m_height = w;
}
// avoid Division-By-Zero problems in the program
if ( m_width <= 0 )
m_width = 1;
if ( m_height <= 0 )
m_height = 1;
}
KPDFPage::~KPDFPage()
{
deletePixmapsAndRects();
deleteHighlights();
deleteAnnotations();
delete m_text;
delete m_transition;
}
bool KPDFPage::hasPixmap( int id, int width, int height ) const
{
if ( !m_pixmaps.contains( id ) )
return false;
if ( width == -1 || height == -1 )
return true;
QPixmap * p = m_pixmaps[ id ];
return p ? ( p->width() == width && p->height() == height ) : false;
}
bool KPDFPage::hasSearchPage() const
{
return m_text != 0;
}
bool KPDFPage::hasBookmark() const
{
return m_bookmarked;
}
bool KPDFPage::hasObjectRect( double x, double y ) const
{
if ( m_rects.count() < 1 )
return false;
QValueList< ObjectRect * >::const_iterator it = m_rects.begin(), end = m_rects.end();
for ( ; it != end; ++it )
if ( (*it)->contains( x, y ) )
return true;
return false;
}
bool KPDFPage::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
QValueList< HighlightRect * >::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 KPDFPage::hasTransition() const
{
return m_transition != 0;
}
NormalizedRect * KPDFPage::findText( const QString & text, bool strictCase, NormalizedRect * lastRect ) const
{
if ( text.isEmpty() )
return 0;
// create a xpf's Unicode (unsigned int) array for the given text
const QChar * str = text.unicode();
int len = text.length();
QMemArray<Unicode> u(len);
for (int i = 0; i < len; ++i)
u[i] = str[i].unicode();
// find out the direction of search
enum SearchDir { FromTop, NextMatch, PrevMatch } dir = lastRect ? NextMatch : FromTop;
double sLeft, sTop, sRight, sBottom;
if ( dir == NextMatch )
{
sLeft = lastRect->left * m_width;
sTop = lastRect->top * m_height;
sRight = lastRect->right * m_width;
sBottom = lastRect->bottom * m_height;
}
// this loop is only for 'bad case' matches
bool found = false;
while ( !found )
{
if ( dir == FromTop )
found = m_text->findText( const_cast<Unicode*>(static_cast<const Unicode*>(u)), len, gTrue, gTrue, gFalse, gFalse, &sLeft, &sTop, &sRight, &sBottom );
else if ( dir == NextMatch )
found = m_text->findText( const_cast<Unicode*>(static_cast<const Unicode*>(u)), len, gFalse, gTrue, gTrue, gFalse, &sLeft, &sTop, &sRight, &sBottom );
else if ( dir == PrevMatch )
// FIXME: this doesn't work as expected (luckily backward search isn't yet used)
found = m_text->findText( const_cast<Unicode*>(static_cast<const Unicode*>(u)), len, gTrue, gFalse, gFalse, gTrue, &sLeft, &sTop, &sRight, &sBottom );
// if not found (even in case unsensitive search), terminate
if ( !found )
break;
// check for case sensitivity
if ( strictCase )
{
// since we're in 'Case sensitive' mode, check if words are identical
GString * realText = m_text->getText( sLeft, sTop, sRight, sBottom );
found = QString::fromUtf8( realText->getCString() ) == text;
if ( !found && dir == FromTop )
dir = NextMatch;
delete realText;
}
}
// if the page was found, return a new normalizedRect
if ( found )
return new NormalizedRect( sLeft / m_width, sTop / m_height, sRight / m_width, sBottom / m_height );
return 0;
}
const QString KPDFPage::getText( const NormalizedRect & rect ) const
{
if ( !m_text )
return QString::null;
int left = (int)( rect.left * m_width ),
top = (int)( rect.top * m_height ),
right = (int)( rect.right * m_width ),
bottom = (int)( rect.bottom * m_height );
GString * text = m_text->getText( left, top, right, bottom );
QString result = QString::fromUtf8( text->getCString() );
delete text;
return result;
}
const ObjectRect * KPDFPage::getObjectRect( double x, double y ) const
{
QValueList< ObjectRect * >::const_iterator it = m_rects.begin(), end = m_rects.end();
for ( ; it != end; ++it )
if ( (*it)->contains( x, y ) )
return *it;
return 0;
}
const KPDFPageTransition * KPDFPage::getTransition() const
{
return m_transition;
}
void KPDFPage::setPixmap( int id, QPixmap * pixmap )
{
if ( m_pixmaps.contains( id ) )
delete m_pixmaps[id];
m_pixmaps[id] = pixmap;
}
void KPDFPage::setSearchPage( TextPage * tp )
{
delete m_text;
m_text = tp;
}
void KPDFPage::setBookmark( bool state )
{
m_bookmarked = state;
}
void KPDFPage::setObjectRects( const QValueList< ObjectRect * > rects )
{
QValueList< ObjectRect * >::iterator it = m_rects.begin(), end = m_rects.end();
for ( ; it != end; ++it )
delete *it;
m_rects = rects;
}
void KPDFPage::setHighlight( int s_id, NormalizedRect * &rect, const QColor & color )
{
// create a HighlightRect descriptor taking values from params
HighlightRect * hr = new HighlightRect();
hr->s_id = s_id;
hr->color = color;
hr->left = rect->left;
hr->top = rect->top;
hr->right = rect->right;
hr->bottom = rect->bottom;
// append the HighlightRect to the list
m_highlights.append( hr );
// delete old object and change reference
delete rect;
rect = hr;
}
void KPDFPage::addAnnotation( Annotation * annotation )
{
m_annotations.append( annotation );
}
void KPDFPage::setTransition( KPDFPageTransition * transition )
{
delete m_transition;
m_transition = transition;
}
void KPDFPage::deletePixmap( int id )
{
if ( m_pixmaps.contains( id ) )
{
delete m_pixmaps[ id ];
m_pixmaps.remove( id );
}
}
void KPDFPage::deletePixmapsAndRects()
{
// delete all stored pixmaps
QMap<int,QPixmap *>::iterator it = m_pixmaps.begin(), end = m_pixmaps.end();
for ( ; it != end; ++it )
delete *it;
m_pixmaps.clear();
// delete ObjectRects
QValueList< ObjectRect * >::iterator rIt = m_rects.begin(), rEnd = m_rects.end();
for ( ; rIt != rEnd; ++rIt )
delete *rIt;
m_rects.clear();
}
void KPDFPage::deleteHighlights( int s_id )
{
// delete highlights by ID
QValueList< HighlightRect * >::iterator it = m_highlights.begin(), end = m_highlights.end();
while ( it != end )
{
HighlightRect * highlight = *it;
if ( s_id == -1 || highlight->s_id == s_id )
{
it = m_highlights.remove( it );
delete highlight;
}
else
++it;
}
}
void KPDFPage::deleteAnnotations()
{
// delete all stored annotations
QValueList< Annotation * >::iterator aIt = m_annotations.begin(), aEnd = m_annotations.end();
for ( ; aIt != aEnd; ++aIt )
delete *aIt;
m_annotations.clear();
}
/** class NormalizedRect **/
NormalizedRect::NormalizedRect()
: left( 0.0 ), top( 0.0 ), right( 0.0 ), bottom( 0.0 ) {}
NormalizedRect::NormalizedRect( double l, double t, double r, double b )
// note: check for swapping coords?
: left( l ), top( t ), right( r ), bottom( b ) {}
NormalizedRect::NormalizedRect( const QRect & r, double xScale, double yScale )
: left( (double)r.left() / xScale ), top( (double)r.top() / yScale ),
right( (double)r.right() / xScale ), bottom( (double)r.bottom() / yScale ) {}
bool NormalizedRect::contains( double x, double y ) const
{
return x >= left && x <= right && y >= top && y <= bottom;
}
bool NormalizedRect::intersects( const NormalizedRect & r ) const
{
return (r.left < right) && (r.right > left) && (r.top < bottom) && (r.bottom > top);
}
bool NormalizedRect::intersects( double l, double t, double r, double b ) const
{
return (l < right) && (r > left) && (t < bottom) && (b > top);
}
QRect NormalizedRect::geometry( int xScale, int yScale ) const
{
int l = (int)( left * xScale ),
t = (int)( top * yScale ),
r = (int)( right * xScale ),
b = (int)( bottom * yScale );
return QRect( l, t, r - l + 1, b - t + 1 );
}
/** class ObjectRect **/
ObjectRect::ObjectRect( double l, double t, double r, double b, ObjectType type, void * pnt )
// assign coordinates swapping them if negative width or height
: NormalizedRect( r > l ? l : r, b > t ? t : b, r > l ? r : l, b > t ? b : t ),
m_objectType( type ), m_pointer( pnt )
{
}
ObjectRect::~ObjectRect()
{
if ( !m_pointer )
return;
if ( m_objectType == Link )
delete static_cast<KPDFLink*>( m_pointer );
else
kdDebug() << "Object deletion not implemented for type '" << m_objectType << "' ." << endl;
}