Commit fbc7d450 authored by Enrico Ros's avatar Enrico Ros

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
parent 451a3091
......@@ -62,6 +62,9 @@
<default>true</default>
</entry>
<entry key="SplitterSizes" type="IntList" />
<entry key="EditToolboxPlacement" type="Int" >
<default>0</default>
</entry>
</group>
<group name="PageView" >
<entry key="ShowScrollBars" type="Bool" >
......
......@@ -12,12 +12,16 @@
#include <qpainter.h>
#include <qpixmap.h>
#include <qimage.h>
#include <klocale.h>
// system includes
#include <math.h>
// local includes
#include "annotations.h"
Annotation::Annotation()
: NormalizedRect()
: NormalizedRect(), m_state( NewBorn )
{
}
......@@ -31,3 +35,91 @@ void mouseReleaseEvent( double x, double y, Qt::ButtonState b );
void overlayPaint( QPainter * painter );
void finalPaint( QPixmap * pixmap, MouseState mouseState );
*/
HighlightAnnotation::HighlightAnnotation( Type type )
: Annotation(), m_type( type )
{
}
QString HighlightAnnotation::usageTip() const
{
return i18n( "Press and drag the mouse to highlight words." );
}
bool HighlightAnnotation::mouseEvent( MouseEvent e, double x, double y, Qt::ButtonState b )
{
// only process events if left button pressed
if ( b != Qt::LeftButton )
return false;
// start operation
if ( e == MousePress && m_state == NewBorn )
{
FloatPoint newPoint;
left = right = newPoint.x = x;
top = bottom = newPoint.y = y;
m_points.append( newPoint );
m_state = Creating;
return false;
}
// add a point to the path, TODO inline redundancy suppression
else if ( e == MouseMove && m_state == Creating )
{
double dist = hypot( x - m_points.last().x, y - m_points.last().y );
if ( dist < 0.02 )
return false;
left = QMIN( left, x );
top = QMIN( y, top );
right = QMAX( x, right );
bottom = QMAX( y, bottom );
FloatPoint newPoint;
newPoint.x = x;
newPoint.y = y;
m_points.append( newPoint );
return true;
}
// terminate precess
else if ( e == MouseRelease && m_state == Creating )
{
if ( m_points.count() > 2 )
m_state = Opened;
else
m_state = NewBorn;
return false;
}
// nothing change -> don't update gfx
return false;
}
void HighlightAnnotation::paintOverlay( QPainter * painter, int xScale, int yScale, const QRect & limits )
{
// draw annotations whith at least 2 points
if ( m_points.count() < 2 )
return;
// use basecolor
painter->setPen( QPen(m_baseColor, 3) );
// perform a rought paint drawing meanline only
//const QRect myRect = geometry( xScale, yScale );
QValueList<FloatPoint>::iterator pIt = m_points.begin(), pEnd = m_points.end();
FloatPoint pA = *pIt;
++pIt;
for ( ; pIt != pEnd; ++pIt )
{
FloatPoint pB = *pIt;
// painter->setPen( QPen(Qt::black, 2) );
// painter->drawLine( (int)(pA.x * (double)xScale)+1, (int)(pA.y * (double)yScale)+1,
// (int)(pB.x * (double)xScale)+1, (int)(pB.y * (double)yScale)+1 );
painter->drawLine( (int)(pA.x * (double)xScale), (int)(pA.y * (double)yScale),
(int)(pB.x * (double)xScale), (int)(pB.y * (double)yScale) );
pA = pB;
}
}
void HighlightAnnotation::paintFinal( QImage & /*backImage*/, int /*xScale*/, int /*yScale*/, const QRect & /*limits*/ )
{
// use m_type for painting
}
......@@ -12,6 +12,8 @@
#include <qstring.h>
#include <qdatetime.h>
#include <qpoint.h>
#include <qvaluelist.h>
#include "page.h"
/**
......@@ -31,10 +33,22 @@ class Annotation : public NormalizedRect
Annotation();
virtual ~Annotation();
enum State { Creating, Modifying, Closed, Opened };
enum MouseState { Normal, Hovered, Pressed };
enum State { NewBorn, Creating, Modifying, Closed, Opened };
enum MouseEvent { MousePress, MouseMove, MouseRelease };
//enum MouseState { Normal, Hovered, Pressed };
enum Flags { Hidden, NoOpenable, Print, Locked, ReadOnly };
// provide some i18n strings
virtual QString usageTip() const = 0;
// event handlers (must update state)
virtual bool mouseEvent( MouseEvent e, double x, double y, Qt::ButtonState b ) = 0;
// paint roughtly over a cleared area
virtual void paintOverlay( QPainter * painter, int xScale, int yScale, const QRect & limits ) = 0;
// cool-paint over a pixmap
virtual void paintFinal( QImage & backImage, int xScale, int yScale, const QRect & limits ) = 0;
State state() const { return m_state; }
const QString & text() const { return m_text; }
const QString & uniqueName() const { return m_uniqueName; }
......@@ -42,19 +56,11 @@ class Annotation : public NormalizedRect
const QDateTime & modifyDate() const { return m_modifyDate; }
const QColor & baseColor() const { return m_baseColor; }
// event handlers (must update state)
virtual void mousePressEvent( double x, double y, Qt::ButtonState b ) = 0;
virtual void mouseMoveEvent( double x, double y, Qt::ButtonState b ) = 0;
virtual void mouseReleaseEvent( double x, double y, Qt::ButtonState b ) = 0;
// paint roughtly over a cleared area
virtual void overlayPaint( QPainter * painter ) = 0;
// cool-paint over a pixmap
virtual void finalPaint( QPixmap * pixmap, MouseState mouseState ) = 0;
void setBaseColor( const QColor & color ) { m_baseColor = color; }
protected:
State m_state;
MouseState m_mouseState;
//MouseState m_mouseState;
QString m_text;
QString m_uniqueName;
QDateTime m_modifyDate;
......@@ -66,6 +72,7 @@ class Annotation : public NormalizedRect
class TextAnnotation : public Annotation
{
//Text (post-it like)
//FreeText (direct on page)
enum Type { InPlace, Popup };
......@@ -87,10 +94,26 @@ class PathAnnotation : public Annotation
//Polygon, PolyLine
};
struct FloatPoint
{
double x, y;
};
class HighlightAnnotation : public Annotation
{
//Highlight, Underline, Squiggly, StrikeOut, BLOCK
enum BrushHor { Horizontal, Vertical };
public:
enum Type { Highlight, Underline, Squiggly, StrikeOut, BLOCK };
HighlightAnnotation( Type type );
// [Annotation] inherited methods
QString usageTip() const;
bool mouseEvent( MouseEvent e, double x, double y, Qt::ButtonState b );
void paintOverlay( QPainter * painter, int xScale, int yScale, const QRect & limits );
void paintFinal( QImage & backImage, int xScale, int yScale, const QRect & limits );
private:
Type m_type;
QValueList<FloatPoint> m_points;
};
class StampAnnotation : public Annotation
......
......@@ -460,6 +460,20 @@ void KPDFDocument::requestTextPage( uint page )
generator->generateSyncTextPage( kp );
}
void KPDFDocument::addPageAnnotation( int page, Annotation * annotation )
{
// find out the page to attach annotation
KPDFPage * kp = pages_vector[ page ];
if ( !generator || !kp )
return;
// add annotation to the page
kp->addAnnotation( annotation );
// notify observers about the change
foreachObserver( notifyPageChanged( page, DocumentObserver::Annotations ) );
}
/* REFERENCE IMPLEMENTATION: better calling setViewport from other code
void KPDFDocument::setNextPage()
{
......
......@@ -24,6 +24,7 @@ class DocumentInfo;
class DocumentSynopsis;
class Generator;
class PixmapRequest;
class Annotation;
class KPrinter;
class KURL;
......@@ -80,6 +81,7 @@ class KPDFDocument : public QObject
void setNextViewport();
void requestPixmaps( const QValueList< PixmapRequest * > & requests );
void requestTextPage( uint page );
void addPageAnnotation( int page, Annotation * annotation );
enum SearchType { NextMatch, PrevMatch, AllDoc, GoogleLike };
bool searchText( int searchID, const QString & text, bool fromStart, bool caseSensitive,
......
......@@ -44,7 +44,7 @@ class DocumentObserver
virtual uint observerId() const = 0;
// commands from the Document to all observers
enum ChangedFlags { Pixmap = 1, Bookmark = 2, Highlights = 4 };
enum ChangedFlags { Pixmap = 1, Bookmark = 2, Highlights = 4, Annotations = 8 };
virtual void notifySetup( const QValueVector< KPDFPage * > & /*pages*/, bool /*documentChanged*/ ) {};
virtual void notifyViewportChanged( bool /*smoothMove*/ ) {};
virtual void notifyPageChanged( int /*pageNumber*/, int /*changedFlags*/ ) {};
......
......@@ -17,6 +17,7 @@
#include "page.h"
#include "pagetransition.h"
#include "link.h"
#include "annotations.h"
#include "conf/settings.h"
#include "xpdf/TextOutputDev.h"
......@@ -44,6 +45,7 @@ KPDFPage::~KPDFPage()
{
deletePixmapsAndRects();
deleteHighlights();
deleteAnnotations();
delete m_text;
delete m_transition;
}
......@@ -231,6 +233,11 @@ void KPDFPage::setHighlight( int s_id, NormalizedRect * &rect, const QColor & co
rect = hr;
}
void KPDFPage::addAnnotation( Annotation * annotation )
{
m_annotations.append( annotation );
}
void KPDFPage::setTransition( KPDFPageTransition * transition )
{
delete m_transition;
......@@ -277,6 +284,15 @@ void KPDFPage::deleteHighlights( int s_id )
}
}
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 **/
......
......@@ -66,11 +66,12 @@ class KPDFPage
void setBookmark( bool state );
void setObjectRects( const QValueList< ObjectRect * > rects );
void setHighlight( int s_id, NormalizedRect * &r, const QColor & color );
//void setAnnotation( Annotation * annotation );
void addAnnotation( Annotation * annotation );
void setTransition( KPDFPageTransition * transition );
void deletePixmap( int p_id );
void deletePixmapsAndRects();
void deleteHighlights( int s_id = -1 );
void deleteAnnotations();
private:
friend class PagePainter;
......@@ -82,7 +83,7 @@ class KPDFPage
TextPage * m_text;
QValueList< ObjectRect * > m_rects;
QValueList< HighlightRect * > m_highlights;
//QValueList< Annotation * > m_annotations;
QValueList< Annotation * > m_annotations;
KPDFPageTransition * m_transition;
};
......
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<kpartgui name="kpdf_part" version="17">
<kpartgui name="kpdf_part" version="18">
<MenuBar>
<Menu name="file"><text>&amp;File</text>
<Action name="save" group="file_save"/>
......@@ -38,6 +38,8 @@
<Action name="mouse_drag"/>
<Action name="mouse_zoom"/>
<Action name="mouse_select"/>
<Separator/>
<Action name="mouse_toggle_annotate"/>
</Menu>
<Menu name="settings"><text>&amp;Settings</text>
<Action name="watch_file" group="show_merge"/>
......@@ -61,6 +63,5 @@
<Action name="mouse_drag"/>
<Action name="mouse_zoom"/>
<Action name="mouse_select"/>
<Action name="mouse_edit"/>
</ToolBar>
</kpartgui>
icons_DATA = highlight_green.png highlight_orange.png highlight_pink.png \
highlight_yellow.png pencil.png pinnote.png
highlight_yellow.png pencil.png pinnote.png coprex-white.png
iconsdir = $(kde_datadir)/kpdf/pics
......@@ -18,6 +18,7 @@
// local includes
#include "pagepainter.h"
#include "core/page.h"
#include "core/annotations.h"
#include "conf/settings.h"
void PagePainter::paintPageOnPainter( const KPDFPage * page, int id, int flags,
......@@ -67,6 +68,7 @@ void PagePainter::paintPageOnPainter( const KPDFPage * page, int id, int flags,
// find out what to paint over the pixmap (manipulations / overlays)
bool paintAccessibility = (flags & Accessibility) && Settings::changeColors() && (Settings::renderMode() != Settings::EnumRenderMode::Paper);
bool paintHighlights = (flags & Highlights) && !page->m_highlights.isEmpty();
bool paintAnnotations = (flags & Annotations) && !page->m_annotations.isEmpty();
bool enhanceLinks = (flags & EnhanceLinks) && Settings::highlightLinks();
bool enhanceImages = (flags & EnhanceImages) && Settings::highlightImages();
// check if there are really some highlightRects to paint
......@@ -89,9 +91,14 @@ void PagePainter::paintPageOnPainter( const KPDFPage * page, int id, int flags,
}
}
}
// check if there are really some annotations to paint
if ( paintAnnotations )
{
// TODO
}
// use backBuffer if 'pixmap direct manipulation' is needed
bool backBuffer = paintAccessibility || paintHighlights;
bool backBuffer = paintAccessibility || paintHighlights || paintAnnotations;
QPixmap * backPixmap = 0;
QPainter * p = destPainter;
if ( backBuffer )
......@@ -197,6 +204,25 @@ void PagePainter::paintPageOnPainter( const KPDFPage * page, int id, int flags,
backPixmap->convertFromImage( backImage );
}
// 2.3. annotations OVERLAY FIXME MOD in page
if ( paintAnnotations )
{
// draw annotations that are inside the 'limits' paint region
QValueList< Annotation * >::const_iterator aIt = page->m_annotations.begin(), aEnd = page->m_annotations.end();
for ( ; aIt != aEnd; ++aIt )
{
Annotation * a = *aIt;
QRect annotRect = a->geometry( width, height );
if ( annotRect.isValid() && annotRect.intersects( limits ) )
{
// find out the annotation rect on pixmap
annotRect = annotRect.intersect( limits );
a->paintOverlay( p, width, height, limits );
}
}
}
// 3. visually enchance links and images if requested
if ( enhanceLinks || enhanceImages )
{
......
......@@ -23,7 +23,8 @@ class PagePainter
// list of flags passed to the painting function. by OR-ing those flags
// you can decide wether or not to permit drawing of a certain feature.
enum PagePainterFlags { Accessibility = 1, EnhanceLinks = 2,
EnhanceImages = 4, Highlights = 8 };
EnhanceImages = 4, Highlights = 8,
Annotations = 16 };
// draw (using painter 'p') the 'page' requested by 'id' using features
// in 'flags'. 'limits' is the bounding rect of the paint operation,
......
/***************************************************************************
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
* Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it> *
* Copyright (C) 2004 by Albert Astals Cid <tsdgeos@terra.es> *
* *
* With portions of code from kpdf/kpdf_pagewidget.cc by: *
......@@ -49,6 +49,7 @@
#include "core/document.h"
#include "core/page.h"
#include "core/link.h"
#include "core/annotations.h"
#include "core/generator.h"
#include "conf/settings.h"
......@@ -89,18 +90,22 @@ public:
// auto scroll
int scrollIncrement;
QTimer * autoScrollTimer;
// annotations
Annotation * localAnnotation;
QRect localAnnotationRect;
PageViewItem * localAnnotationItem;
PageViewEditTools * editToolsWindow;// in pageviewtoolbox.h
// other stuff
QTimer * delayResizeTimer;
bool dirtyLayout;
bool blockViewport; // prevents changes to viewport
bool blockPixmapsRequest; // prevent pixmap requests
PageViewMessage * messageWindow; // in pageviewutils.h
PageViewEditTools * editToolsWindow;// in pageviewtoolbox.h
// actions
KToggleAction * aMouseNormal;
KToggleAction * aMouseSelect;
KToggleAction * aMouseEdit;
KToggleAction * aToggleEditTools;
KSelectAction * aZoom;
KToggleAction * aZoomFitWidth;
KToggleAction * aZoomFitPage;
......@@ -139,12 +144,14 @@ PageView::PageView( QWidget *parent, KPDFDocument *document )
d->viewportMoveTimer = 0;
d->scrollIncrement = 0;
d->autoScrollTimer = 0;
d->localAnnotation = 0;
d->localAnnotationItem = 0;
d->editToolsWindow = 0;
d->delayResizeTimer = 0;
d->dirtyLayout = false;
d->blockViewport = false;
d->blockPixmapsRequest = false;
d->messageWindow = new PageViewMessage(this);
d->editToolsWindow = 0;
d->aPrevAction = 0;
// widget setup: setup focus, accept drops and track mouse
......@@ -173,6 +180,9 @@ PageView::PageView( QWidget *parent, KPDFDocument *document )
PageView::~PageView()
{
// every object except annotation is deleted as qobject child
delete d->localAnnotation;
// delete the local storage structure
delete d;
}
......@@ -216,8 +226,8 @@ void PageView::setupActions( KActionCollection * ac )
d->aMouseSelect = new KRadioAction( i18n("&Select"), "frame_edit", 0, this, SLOT( slotSetMouseSelect() ), ac, "mouse_select" );
d->aMouseSelect->setExclusiveGroup( "MouseType" );
d->aMouseEdit = new KRadioAction( i18n("&Review"), "pencil", 0, this, SLOT( slotSetMouseDraw() ), ac, "mouse_edit" );
d->aMouseEdit->setExclusiveGroup("MouseType");
d->aToggleEditTools = new KToggleAction( i18n("&Review"), "pencil", 0, ac, "mouse_toggle_annotate" );
connect( d->aToggleEditTools, SIGNAL( toggled( bool ) ), SLOT( slotToggleEditTools( bool ) ) );
// Other actions
KAction * su = new KAction( i18n("Scroll Up"), 0, this, SLOT( slotScrollUp() ), ac, "view_scroll_up" );
......@@ -581,7 +591,7 @@ void PageView::keyPressEvent( QKeyEvent * e )
}
else
{
findAheadStop();
slotStopFindAhead();
d->document->resetSearch( PAGEVIEW_SEARCH_ID );
}
}
......@@ -600,7 +610,7 @@ void PageView::keyPressEvent( QKeyEvent * e )
// esc and return: end search
else if( e->key() == Key_Escape || e->key() == Key_Return )
{
findAheadStop();
slotStopFindAhead();
}
// other key: add to text and search
else if( !e->text().isEmpty() )
......@@ -631,7 +641,7 @@ void PageView::keyPressEvent( QKeyEvent * e )
{
// create the timer on demand
d->findTimeoutTimer = new QTimer( this );
connect( d->findTimeoutTimer, SIGNAL( timeout() ), this, SLOT( findAheadStop() ) );
connect( d->findTimeoutTimer, SIGNAL( timeout() ), this, SLOT( slotStopFindAhead() ) );
}
d->findTimeoutTimer->start( 3000, true );
grabKeyboard();
......@@ -730,11 +740,25 @@ void PageView::contentsMouseMoveEvent( QMouseEvent * e )
d->mouseMidStartY = e->globalPos().y();
d->zoomFactor *= ( 1.0 + ( (double)deltaY / 500.0 ) );
updateZoom( ZoomRefreshCurrent );
// rescale annotation (if editing one)
if ( d->localAnnotation && d->localAnnotationItem )
{
PageViewItem * i = d->localAnnotationItem;
d->localAnnotationRect = d->localAnnotation->geometry( i->width(), i->height() );
d->localAnnotationRect.moveBy( i->geometry().left(), i->geometry().top() );
}
// uncomment following line to force a complete redraw
viewport()->repaint( false );
return;
}
// if we're editing an annotation, dispatch event to it
if ( d->localAnnotation )
{
updateAnnotation( e );
return;
}
bool leftButton = e->state() & LeftButton,
rightButton = e->state() & RightButton;
switch ( d->mouseMode )
......@@ -779,9 +803,6 @@ void PageView::contentsMouseMoveEvent( QMouseEvent * e )
if ( (leftButton || d->aPrevAction) && !d->mouseSelectionRect.isNull() )
selectionEndPoint( e->x(), e->y() );
break;
case MouseEdit: // ? update graphics ?
break;
}
}
......@@ -811,6 +832,13 @@ void PageView::contentsMousePressEvent( QMouseEvent * e )
return;
}
// if we're editing an annotation, dispatch event to it
if ( d->localAnnotation )
{
updateAnnotation( e );
return;
}
// update press / 'start drag' mouse position
d->mousePressPos = e->globalPos();
......@@ -842,9 +870,6 @@ void PageView::contentsMousePressEvent( QMouseEvent * e )
selectionStart( e->x(), e->y(), selColor, false );
}
break;
case MouseEdit: // ..to do..
break;
}
}
......@@ -868,6 +893,13 @@ void PageView::contentsMouseReleaseEvent( QMouseEvent * e )
return;
}
// if we're editing an annotation, dispatch event to it
if ( d->localAnnotation )
{
updateAnnotation( e );
return;
}
bool leftButton = e->button() & LeftButton,
rightButton = e->button() & RightButton;
switch ( d->mouseMode )
......@@ -1080,9 +1112,6 @@ void PageView::contentsMouseReleaseEvent( QMouseEvent * e )
d->aPrevAction = 0;
}
}break;
case MouseEdit: // ? apply [tool] ?
break;
}
// reset mouse press / 'drag start' position
......@@ -1204,17 +1233,26 @@ void PageView::paintItems( QPainter * p, const QRect & contentsRect )
}
}
// draw the pixmap (note: this modifies the painter)
// draw the page using the PagePainter whith all flags active
if ( contentsRect.intersects( pixmapGeometry ) )
{
QRect pixmapRect = contentsRect.intersect( pixmapGeometry );
pixmapRect.moveBy( -pixmapGeometry.left(), -pixmapGeometry.top() );
int flags = PagePainter::Accessibility | PagePainter::EnhanceLinks |
PagePainter::EnhanceImages | PagePainter::Highlights;
PagePainter::EnhanceImages | PagePainter::Highlights |
PagePainter::Annotations;
PagePainter::paintPageOnPainter( item->page(), PAGEVIEW_ID, flags, p, pixmapRect,
pixmapGeometry.width(), pixmapGeometry.height() );
}
// draw the unapplied (contructing) annotation if present
if ( d->localAnnotationItem == item && contentsRect.intersects( d->localAnnotationRect ) )
{
QRect annotRect = contentsRect.intersect( d->localAnnotationRect );
annotRect.moveBy( -pixmapGeometry.left(), -pixmapGeometry.top() );
d->localAnnotation->paintOverlay( p, item->width(), item->height(), annotRect );
}
// remove painted area from 'remainingArea' and restore painter
remainingArea -= outlineGeometry.intersect( contentsRect );
p->restore();
......@@ -1324,6 +1362,72 @@ void PageView::selectionClear()
d->mouseSelectionRect.setCoords( 0, 0, -1, -1 );
}
void PageView::updateAnnotation( QMouseEvent * e )
{
// find out the underlying pageItem, if none return
PageViewItem * pageItem = pickItemOnPoint( e->x(), e->y() );
if ( !pageItem )
return;
// restrict placement to a single pageItem while being created
if ( d->localAnnotation->state() != Annotation::NewBorn )
{