Commit 451a3091 authored by Enrico Ros's avatar Enrico Ros

Opening kdpf_annotations branch. Code changes:

 User Interface: added a toolbox for selecting an annotation tool without
   polluting the toolbar (pageviewtoolbox animated widget). Icons of that
   toolbar are gimped version of ones in nuvola icontheme.
 Core: added annotation class. this will support all features (except for
   silly ones (really there are some!)) from pdf 1.6 specs but abstracted
   in the kdpf way. (api changes in progress...)

About annotations: only some incomplete interfaces are in plasefor now.
Every type of pdf annotations has tens of parameters. I think we'll render
correctly everything but provide only simple and useful tools for making
annotations over the pages, otherwise a 'Qt designer like' property view
will be required to do fine adjustment over the crappy thousands of
settings. (and we're aiming at a quick and simple viewer with lots of
coolness, not chaos).

Note: the code has been wrongly committed to HEAD before,but soon reverted.
Have fun!

svn path=/branches/kpdf_annotations/kdegraphics/kpdf/; revision=388768
parent 7883b068
...@@ -5,7 +5,8 @@ INCLUDES = -I$(srcdir)/generator_pdf -I$(srcdir)/.. -I$(srcdir)/../xpdf -I$(srcd ...@@ -5,7 +5,8 @@ INCLUDES = -I$(srcdir)/generator_pdf -I$(srcdir)/.. -I$(srcdir)/../xpdf -I$(srcd
METASOURCES = AUTO METASOURCES = AUTO
libkpdfcore_la_LIBADD = ./generator_pdf/libgeneratorpdf.la libkpdfcore_la_LIBADD = ./generator_pdf/libgeneratorpdf.la
libkpdfcore_la_SOURCES = document.cpp link.cpp page.cpp pagetransition.cpp libkpdfcore_la_SOURCES = page.cpp document.cpp link.cpp annotations.cpp \
pagetransition.cpp
noinst_LTLIBRARIES = libkpdfcore.la noinst_LTLIBRARIES = libkpdfcore.la
......
/***************************************************************************
* Copyright (C) 2005 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 <qrect.h>
#include <qpainter.h>
#include <qpixmap.h>
#include <qimage.h>
// local includes
#include "annotations.h"
Annotation::Annotation()
: NormalizedRect()
{
}
Annotation::~Annotation()
{
}
/*
void mousePressEvent( double x, double y, Qt::ButtonState b );
void mouseMoveEvent( double x, double y, Qt::ButtonState b );
void mouseReleaseEvent( double x, double y, Qt::ButtonState b );
void overlayPaint( QPainter * painter );
void finalPaint( QPixmap * pixmap, MouseState mouseState );
*/
/***************************************************************************
* Copyright (C) 2005 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. *
***************************************************************************/
#ifndef _KPDF_ANNOTATIONS_H_
#define _KPDF_ANNOTATIONS_H_
#include <qstring.h>
#include <qdatetime.h>
#include "page.h"
/**
* @short Base options for an annotation (highlight, stamp, boxes, ..).
*
* From PDFreferece v.1.6:
* An annotation associates an object such as a note, sound, or movie with a
* location on a page of a PDF document ...
*
* Inherited classes must modify protected variables as appropriate.
* Other fields in pdf reference we dropped here:
* -subtype, rectangle(we are a rect), border stuff
*/
class Annotation : public NormalizedRect
{
public:
Annotation();
virtual ~Annotation();
enum State { Creating, Modifying, Closed, Opened };
enum MouseState { Normal, Hovered, Pressed };
enum Flags { Hidden, NoOpenable, Print, Locked, ReadOnly };
State state() const { return m_state; }
const QString & text() const { return m_text; }
const QString & uniqueName() const { return m_uniqueName; }
const QDateTime & creationDate() const { return m_creationDate; }
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;
protected:
State m_state;
MouseState m_mouseState;
QString m_text;
QString m_uniqueName;
QDateTime m_modifyDate;
QColor m_baseColor;
private:
QDateTime m_creationDate;
};
class TextAnnotation : public Annotation
{
//Text (post-it like)
//FreeText (direct on page)
enum Type { InPlace, Popup };
};
class LineAnnotation : public Annotation
{
//Line (arrows too)
};
class GeomAnnotation : public Annotation
{
//Square, Circle
};
class PathAnnotation : public Annotation
{
//Ink (one or more disjoints paths)
//Polygon, PolyLine
};
class HighlightAnnotation : public Annotation
{
//Highlight, Underline, Squiggly, StrikeOut, BLOCK
enum BrushHor { Horizontal, Vertical };
};
class StampAnnotation : public Annotation
{
// (14 default symbols + ours)
};
class MediaAnnotation : public Annotation
{
//FileAttachment, Sound, Movie
enum Type { FileAttachment, Sound, Movie };
};
#endif
...@@ -260,7 +260,10 @@ void KPDFDocument::addObserver( DocumentObserver * pObserver ) ...@@ -260,7 +260,10 @@ void KPDFDocument::addObserver( DocumentObserver * pObserver )
// if the observer is added while a document is already opened, tell it // if the observer is added while a document is already opened, tell it
if ( !pages_vector.isEmpty() ) if ( !pages_vector.isEmpty() )
{
pObserver->notifySetup( pages_vector, true ); pObserver->notifySetup( pages_vector, true );
pObserver->notifyViewportChanged( false /*disables smoothMove*/ );
}
} }
void KPDFDocument::removeObserver( DocumentObserver * pObserver ) void KPDFDocument::removeObserver( DocumentObserver * pObserver )
......
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<kpartgui name="kpdf_part" version="16"> <kpartgui name="kpdf_part" version="17">
<MenuBar> <MenuBar>
<Menu name="file"><text>&amp;File</text> <Menu name="file"><text>&amp;File</text>
<Action name="save" group="file_save"/> <Action name="save" group="file_save"/>
...@@ -61,5 +61,6 @@ ...@@ -61,5 +61,6 @@
<Action name="mouse_drag"/> <Action name="mouse_drag"/>
<Action name="mouse_zoom"/> <Action name="mouse_zoom"/>
<Action name="mouse_select"/> <Action name="mouse_select"/>
<Action name="mouse_edit"/>
</ToolBar> </ToolBar>
</kpartgui> </kpartgui>
SUBDIRS = icons
INCLUDES = -I$(srcdir)/.. -I$(top_builddir)/kpdf $(all_includes) INCLUDES = -I$(srcdir)/.. -I$(top_builddir)/kpdf $(all_includes)
METASOURCES = AUTO METASOURCES = AUTO
libkpdfui_la_SOURCES = pagepainter.cpp pageview.cpp pageviewutils.cpp \ libkpdfui_la_SOURCES = pagepainter.cpp pageview.cpp pageviewutils.cpp \
minibar.cpp thumbnaillist.cpp searchwidget.cpp \ pageviewtoolbox.cpp minibar.cpp thumbnaillist.cpp \
toc.cpp propertiesdialog.cpp presentationwidget.cpp searchwidget.cpp toc.cpp propertiesdialog.cpp \
presentationwidget.cpp
noinst_LTLIBRARIES = libkpdfui.la noinst_LTLIBRARIES = libkpdfui.la
......
icons_DATA = highlight_green.png highlight_orange.png highlight_pink.png \
highlight_yellow.png pencil.png pinnote.png
iconsdir = $(kde_datadir)/kpdf/pics
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
// local includes // local includes
#include "pageview.h" #include "pageview.h"
#include "pageviewutils.h" #include "pageviewutils.h"
#include "pageviewtoolbox.h"
#include "pagepainter.h" #include "pagepainter.h"
#include "core/document.h" #include "core/document.h"
#include "core/page.h" #include "core/page.h"
...@@ -94,6 +95,7 @@ public: ...@@ -94,6 +95,7 @@ public:
bool blockViewport; // prevents changes to viewport bool blockViewport; // prevents changes to viewport
bool blockPixmapsRequest; // prevent pixmap requests bool blockPixmapsRequest; // prevent pixmap requests
PageViewMessage * messageWindow; // in pageviewutils.h PageViewMessage * messageWindow; // in pageviewutils.h
PageViewEditTools * editToolsWindow;// in pageviewtoolbox.h
// actions // actions
KToggleAction * aMouseNormal; KToggleAction * aMouseNormal;
...@@ -142,6 +144,7 @@ PageView::PageView( QWidget *parent, KPDFDocument *document ) ...@@ -142,6 +144,7 @@ PageView::PageView( QWidget *parent, KPDFDocument *document )
d->blockViewport = false; d->blockViewport = false;
d->blockPixmapsRequest = false; d->blockPixmapsRequest = false;
d->messageWindow = new PageViewMessage(this); d->messageWindow = new PageViewMessage(this);
d->editToolsWindow = 0;
d->aPrevAction = 0; d->aPrevAction = 0;
// widget setup: setup focus, accept drops and track mouse // widget setup: setup focus, accept drops and track mouse
...@@ -213,9 +216,8 @@ void PageView::setupActions( KActionCollection * ac ) ...@@ -213,9 +216,8 @@ void PageView::setupActions( KActionCollection * ac )
d->aMouseSelect = new KRadioAction( i18n("&Select"), "frame_edit", 0, this, SLOT( slotSetMouseSelect() ), ac, "mouse_select" ); d->aMouseSelect = new KRadioAction( i18n("&Select"), "frame_edit", 0, this, SLOT( slotSetMouseSelect() ), ac, "mouse_select" );
d->aMouseSelect->setExclusiveGroup( "MouseType" ); d->aMouseSelect->setExclusiveGroup( "MouseType" );
d->aMouseEdit = new KRadioAction( i18n("Draw"), "edit", 0, this, SLOT( slotSetMouseDraw() ), ac, "mouse_draw" ); d->aMouseEdit = new KRadioAction( i18n("&Review"), "pencil", 0, this, SLOT( slotSetMouseDraw() ), ac, "mouse_edit" );
d->aMouseEdit->setExclusiveGroup("MouseType"); d->aMouseEdit->setExclusiveGroup("MouseType");
d->aMouseEdit->setEnabled( false ); // implement feature before removing this line
// Other actions // Other actions
KAction * su = new KAction( i18n("Scroll Up"), 0, this, SLOT( slotScrollUp() ), ac, "view_scroll_up" ); KAction * su = new KAction( i18n("Scroll Up"), 0, this, SLOT( slotScrollUp() ), ac, "view_scroll_up" );
...@@ -539,12 +541,18 @@ void PageView::viewportPaintEvent( QPaintEvent * pe ) ...@@ -539,12 +541,18 @@ void PageView::viewportPaintEvent( QPaintEvent * pe )
void PageView::viewportResizeEvent( QResizeEvent * ) void PageView::viewportResizeEvent( QResizeEvent * )
{ {
// start a timer that will refresh the pixmap after 0.5s // start a timer that will refresh the pixmap after 0.5s
if ( !d->delayResizeTimer ) if ( !d->items.isEmpty() )
{ {
d->delayResizeTimer = new QTimer( this ); if ( !d->delayResizeTimer )
connect( d->delayResizeTimer, SIGNAL( timeout() ), this, SLOT( slotRelayoutPages() ) ); {
d->delayResizeTimer = new QTimer( this );
connect( d->delayResizeTimer, SIGNAL( timeout() ), this, SLOT( slotRelayoutPages() ) );
}
d->delayResizeTimer->start( 333, true );
} }
d->delayResizeTimer->start( 333, true ); // update geometry of tools slider widget (if any)
if ( d->editToolsWindow )
d->editToolsWindow->anchorChanged();
} }
void PageView::keyPressEvent( QKeyEvent * e ) void PageView::keyPressEvent( QKeyEvent * e )
...@@ -1861,26 +1869,52 @@ void PageView::slotContinousToggled( bool on ) ...@@ -1861,26 +1869,52 @@ void PageView::slotContinousToggled( bool on )
void PageView::slotSetMouseNormal() void PageView::slotSetMouseNormal()
{ {
d->mouseMode = MouseNormal; d->mouseMode = MouseNormal;
// hide the messageWindow
d->messageWindow->hide(); d->messageWindow->hide();
// hide the 'tools overlay' if present
if ( d->editToolsWindow )
{
d->editToolsWindow->hideAndDestroy();
d->editToolsWindow = 0;
}
} }
void PageView::slotSetMouseZoom() void PageView::slotSetMouseZoom()
{ {
d->mouseMode = MouseZoom; d->mouseMode = MouseZoom;
// change the text in messageWindow (and show it if hidden)
d->messageWindow->display( i18n( "Select zooming area. Right-click to zoom out." ), PageViewMessage::Info, -1 ); d->messageWindow->display( i18n( "Select zooming area. Right-click to zoom out." ), PageViewMessage::Info, -1 );
// hide the 'tools overlay' if present
if ( d->editToolsWindow )
{
d->editToolsWindow->hideAndDestroy();
d->editToolsWindow = 0;
}
} }
void PageView::slotSetMouseSelect() void PageView::slotSetMouseSelect()
{ {
d->mouseMode = MouseSelect; d->mouseMode = MouseSelect;
// change the text in messageWindow (and show it if hidden)
d->messageWindow->display( i18n( "Draw a rectangle around the text/graphics to copy." ), PageViewMessage::Info, -1 ); d->messageWindow->display( i18n( "Draw a rectangle around the text/graphics to copy." ), PageViewMessage::Info, -1 );
// hide the 'tools overlay' if present
if ( d->editToolsWindow )
{
d->editToolsWindow->hideAndDestroy();
d->editToolsWindow = 0;
}
} }
void PageView::slotSetMouseDraw() void PageView::slotSetMouseDraw()
{ {
d->mouseMode = MouseEdit; d->mouseMode = MouseEdit;
d->aMouseEdit->setChecked( true ); // hide the messageWindow
d->messageWindow->hide(); d->messageWindow->hide();
// reuse a previous instance if present or create a new one
if ( d->editToolsWindow )
d->editToolsWindow->show();
else
d->editToolsWindow = new PageViewEditTools( this, viewport() );
} }
void PageView::slotScrollUp() void PageView::slotScrollUp()
......
...@@ -68,7 +68,7 @@ class PageView : public QScrollView, public DocumentObserver ...@@ -68,7 +68,7 @@ class PageView : public QScrollView, public DocumentObserver
void rightClick( const KPDFPage *, const QPoint & ); void rightClick( const KPDFPage *, const QPoint & );
protected: protected:
// main draw loop, draws pageViews on viewport // viewport events
void viewportPaintEvent( QPaintEvent * pe ); void viewportPaintEvent( QPaintEvent * pe );
void viewportResizeEvent( QResizeEvent* ); void viewportResizeEvent( QResizeEvent* );
......
/***************************************************************************
* Copyright (C) 2005 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 <qbitmap.h>
#include <qpixmap.h>
#include <qimage.h>
#include <qpainter.h>
#include <qtimer.h>
#include <qtooltip.h>
#include <kiconloader.h>
#include <kimageeffect.h>
#include <klocale.h>
#include <kdeversion.h>
#include <kaccelmanager.h>
#include <kdebug.h>
// system includes
#include <math.h>
// local includes
#include "pageviewtoolbox.h"
//#include "conf/settings.h"
/** ToolboxButton **/
static const int ToolboxButtonIcon = 32;
static const int ToolboxButtonSize = 40;
// a flat button that enlights on hover and has an ID
class ToolboxButton : public QPushButton
{
public:
ToolboxButton( QWidget * parent, const ToolboxItem & item, const QPixmap & backPixmap );
int toolboxID() const;
protected:
void mouseMoveEvent( QMouseEvent * e );
void mouseReleaseEvent( QMouseEvent * e );
void paintEvent( QPaintEvent * e );
private:
int m_id;
bool m_hovering;
const QPixmap & m_background;
};
ToolboxButton::ToolboxButton( QWidget * parent, const ToolboxItem & item, const QPixmap & pix )
: QPushButton( parent ), m_id( item.id ), m_hovering( false ), m_background( pix )
{
setMouseTracking( true );
setToggleButton( true );
resize( ToolboxButtonSize, ToolboxButtonSize );
setPixmap( DesktopIcon( item.pixmap, ToolboxButtonIcon ) );
QToolTip::add( this, item.text );
setWFlags( Qt::WNoAutoErase );
#if KDE_IS_VERSION(3,3,90)
KAcceleratorManager::setNoAccel( this );
#endif
}
int ToolboxButton::toolboxID() const
{
return m_id;
}
void ToolboxButton::mouseMoveEvent( QMouseEvent * e )
{
// check for mouse hovering
const QRect myGeom( 0,0, width(), height() );
bool hover = myGeom.contains( e->pos() );
// if hover state changed update gfx
if ( m_hovering != hover )
{
m_hovering = hover;
update();
}
}
void ToolboxButton::mouseReleaseEvent( QMouseEvent * e )
{
// call default handler
QPushButton::mouseReleaseEvent( e );
// reset hover state when clicking
m_hovering = false;
update();
}
void ToolboxButton::paintEvent( QPaintEvent * e )
{
// always not hovering in disabled state
if ( !isEnabled() )
m_hovering = false;
// paint button in different flavours
if ( isOn() || m_hovering )
{
// if the button is pressed or we're hovering it, use QPushButton style
QPushButton::paintEvent( e );
}
else
{
// else draw button's pixmap over the parent's background (fake transparency)
QPainter p( this );
QRect backRect = e->rect();
backRect.moveBy( x(), y() );
p.drawPixmap( e->rect().topLeft(), m_background, backRect );
drawButtonLabel( &p );
}
}
/** PageViewToolbox **/
static const int ToolboxGridSize = 40;
PageViewToolbox::PageViewToolbox( QWidget * parent, QWidget * anchorWidget, bool vertical )
: QWidget( parent, "", Qt::WNoAutoErase ), m_anchor( anchorWidget ),
m_vertical( vertical ), m_timerID( 0 ), m_hiding( false )
{
}
void PageViewToolbox::setItems( const QValueList<ToolboxItem> & items )
{
#ifndef NDEBUG
if ( !m_buttons.isEmpty() )
kdDebug() << "PageViewToolbox: adding items over existing ones" << endl;
#endif
QValueList<ToolboxItem>::const_iterator it = items.begin(), end = items.end();
for ( ; it != end; ++it )
{
ToolboxButton * button = new ToolboxButton( this, *it, m_pixmap );
connect( button, SIGNAL( clicked() ), this, SLOT( slotButtonClicked() ) );
m_buttons.append( button );
}
}
void PageViewToolbox::show()
{
// rebuild pixmap and mask
buildGfx();
QWidget::show();
// set scroll parameters
m_hiding = false;
m_currentPosition = getStartPoint();
m_endPosition = getEndPoint();
move( m_currentPosition );
// deselect all buttons
QValueList< ToolboxButton * >::iterator it = m_buttons.begin(), end = m_buttons.end();
for ( ; it != end; ++it )
(*it)->setOn( false );
// start scrolling in
if ( m_timerID )
killTimer( m_timerID );
m_timerID = startTimer( 20 );
}
void PageViewToolbox::hideAndDestroy()
{
// set scroll parameters
m_hiding = true;
m_endPosition = getStartPoint();
// start scrolling out
if ( m_timerID )
killTimer( m_timerID );
m_timerID = startTimer( 10 );
}
void PageViewToolbox::anchorChanged()
{
// stop timer
if ( m_timerID )
{
killTimer( m_timerID );
m_timerID = 0;
}
// if was hiding delete this
if ( m_hiding )
delete this;
else
{
// else rebuild widget and set it completely visible
buildGfx();
m_currentPosition = getEndPoint();
move( m_currentPosition );
// repaint all buttons (to update background)
QValueList< ToolboxButton * >::iterator it = m_buttons.begin(), end = m_buttons.end();
for ( ; it != end; ++it )
(*it)->update();
}
}
void PageViewToolbox::paintEvent( QPaintEvent * e )
{
// paint the internal pixmap over the widget
bitBlt( this, e->rect().topLeft(), &m_pixmap, e->rect() );
}
void PageViewToolbox::timerEvent( QTimerEvent * )
{
// move currentPosition towards endPosition
int dX = m_endPosition.x() - m_currentPosition.x(),
dY = m_endPosition.y() - m_currentPosition.y();
dX = dX / 6 + QMAX( -2, QMIN( 2, dX) );
dY = dY / 6 + QMAX( -2, QMIN( 2, dY) );
m_currentPosition.setX( m_currentPosition.x() + dX );
m_currentPosition.setY( m_currentPosition.y() + dY );
// move the widget
move( m_currentPosition );
// handle arrival to the end
if ( m_currentPosition == m_endPosition )
{
killTimer( m_timerID );
m_timerID = 0;
if ( m_hiding )
delete this;
}
}
QPoint PageViewToolbox::getStartPoint()
{
if ( m_vertical )
return QPoint( -width(), (m_anchor->height() - height()) / 2 );
return QPoint( (m_anchor->width() - width()) / 2, -height() );
}
QPoint PageViewToolbox::getEndPoint()
{
if ( m_vertical )
return QPoint( 0, (m_anchor->height() - height()) / 2 );
return QPoint( (m_anchor->width() - width()) / 2, 0 );
}
void PageViewToolbox::buildGfx()