Commit 6dd95c9e authored by Enrico Ros's avatar Enrico Ros

Viewport MOD 1. Implemented the DocumentViewport class, owned by Document.

A const reference may be asked. When set all DocumentObservers will be
notified so they can update their gfx if needed. Converted TOC, Links
to the new usage. DocumentViewport can be saved/restored to/from QString
so it can be asked as metadata and saved on document's XML. When loading
a document the viewport is restored exactly where it was when the document
was closed. More fixes with Viewport class. Swapped groupboxes in Perform
dialog. Changed members naming in DocumentObservers and inherited classes.
PageView and ThumbnailsList now linked.

Still regressions / TODOs about the Viewport thing.

svn path=/trunk/kdegraphics/kpdf/; revision=377066
parent 3e317aab
......@@ -86,6 +86,7 @@ More items (first items will enter 'In progress list' first):
-> presentation: add some gfx tools (like a red pencil)
-> presentation: save a flag (to the xml) to open a pdf in presentation mode
-> presentation: link following (difficult due to pagerects related to pageview pixmap only)
-> presentation: wheel not visible on black. gradient appreciated on lighter backgrounds.
-> investigate 'Splash' lack of smoothness at low resolutions (see lines in thumbnails)
-> add search on the toc widget (a prune on type lineedit like in thumbnails widget)
-> goto 'logical' page (usually differs from pdf's page) (req. by Luca Burrelli)
......
This diff is collapsed.
This diff is collapsed.
......@@ -11,13 +11,15 @@
#ifndef _KPDF_DOCUMENT_H_
#define _KPDF_DOCUMENT_H_
#include <qobject.h>
#include <qvaluevector.h>
#include <qstring.h>
#include <qdom.h>
class KPDFPage;
class KPDFLink;
class KPDFDocumentObserver;
class DocumentObserver;
class DocumentViewport;
class DocumentInfo;
class DocumentSynopsis;
class Generator;
......@@ -39,7 +41,7 @@ class KPrinter;
* notifies all the registered DocumentObservers when some content changes.
*
* For a better understanding of hieracies @see README.internals.png
* @see KPDFDocumentObserver, KPDFPage
* @see DocumentObserver, KPDFPage
*/
class KPDFDocument : public QObject // only for a private slot..
{
......@@ -53,23 +55,25 @@ class KPDFDocument : public QObject // only for a private slot..
void closeDocument();
// misc methods
void addObserver( KPDFDocumentObserver * pObserver );
void removeObserver( KPDFDocumentObserver * pObserver );
void addObserver( DocumentObserver * pObserver );
void removeObserver( DocumentObserver * pObserver );
void reparseConfig();
// query methods (const ones)
const DocumentInfo * documentInfo() const;
const DocumentSynopsis * documentSynopsis() const;
const KPDFPage * page( uint page ) const;
const DocumentViewport & viewport() const;
uint currentPage() const;
uint pages() const;
bool okToPrint() const;
QString getMetaData( const QString & key, const QString & option = QString() ) const;
// perform actions on document / pages
void setViewportPage( int page );
void setViewport( const DocumentViewport & viewport );
void requestPixmaps( const QValueList< PixmapRequest * > & requests, bool asyncronous );
void requestTextPage( uint page );
void setCurrentPage( int page, const QRect & viewport = QRect() );
void findText( const QString & text = "", bool caseSensitive = false );
void findTextAll( const QString & pattern, bool caseSensitive );
void toggleBookmark( int page );
......@@ -105,13 +109,39 @@ class KPDFDocument : public QObject // only for a private slot..
/**
* @short A window on the document.
* @short A view on the document.
*
* TODO HACK OVER ME AND FIXME WITH A CHAINSAW
* The Viewport structure is the 'current view' over the document. Contained
* data is broadcasted between observers to syncronize their viewports to get
* the 'I scroll one view and others scroll too' views.
*/
struct DocumentViewport
class DocumentViewport
{
int lastPage;
public:
/** data fields **/
// the page nearest the center of the viewport
int pageNumber;
// if reCenter.enabled, this contains the viewport center
struct {
bool enabled;
double normalizedCenterX;
double normalizedCenterY;
} reCenter;
// if autoFit.enabled, page must be autofitted in the viewport
struct {
bool enabled;
bool width;
bool height;
} autoFit;
/** class methods **/
// allowed constructors, don't use others
DocumentViewport( int pageNumber = -1 );
DocumentViewport( const QString & xmlDesc );
QString toString() const;
bool operator==( const DocumentViewport & vp ) const;
};
/**
......@@ -148,9 +178,9 @@ class DocumentInfo : public QDomDocument
*
* In the tree the tag name is the 'screen' name of the entry. A tag can have
* attributes. Here follows the list of tag attributes with meaning:
* - PageNumber: The internal number of referred page.
* - PageName: A 'named reference' to the page that must be converted using
* getMetaData( "NamedLink", page_name_attribute )
* - Viewport: A string description of the referred viewport
* - ViewportName: A 'named reference' to the viewport that must be converted
* using getMetaData( "NamedViewport", *viewport_name* )
*/
class DocumentSynopsis : public QDomDocument
{
......
......@@ -229,9 +229,9 @@ void PDFGenerator::addSynopsisChildren( QDomNode * parent, GList * items )
{
// no 'destination' but an internal 'named reference'. we could
// get the destination for the page now, but it's VERY time consuming,
// so better storing the reference and provide page number as metadata
// so better storing the reference and provide the viewport as metadata
// on demand
item.setAttribute( "PageName", g->getNamedDest()->getCString() );
item.setAttribute( "ViewportName", g->getNamedDest()->getCString() );
}
else if ( destination->isOk() )
{
......@@ -242,8 +242,9 @@ void PDFGenerator::addSynopsisChildren( QDomNode * parent, GList * items )
Ref ref = destination->getPageRef();
pageNumber = pdfdoc->findPage( ref.num, ref.gen ) - 1;
}
// set page as attribute to node (note: the viewport should be set too)
item.setAttribute( "PageNumber", pageNumber );
// set page as attribute to node
// TODO add other attributes to the viewport
item.setAttribute( "Viewport", DocumentViewport( pageNumber ).toString() );
}
}
......@@ -487,28 +488,28 @@ QString PDFGenerator::getMetaData( const QString & key, const QString & option )
if ( pdfdoc->getCatalog()->getPageMode() == Catalog::FullScreen )
return "yes";
}
else if ( key == "NamedLink" && !option.isEmpty() )
else if ( key == "NamedViewport" && !option.isEmpty() )
{
// asking for the page related to a 'named link destination'. the
// option is the link name. @see addSynopsisChildren.
int pageNumber = -1;
DocumentViewport viewport;
GString * namedDest = new GString( option.latin1() );
docLock.lock();
LinkDest * destination = pdfdoc->findDest( namedDest );
if ( destination )
{
if ( !destination->isPageRef() )
pageNumber = destination->getPageNum() - 1;
viewport.pageNumber = destination->getPageNum() - 1;
else
{
Ref ref = destination->getPageRef();
pageNumber = pdfdoc->findPage( ref.num, ref.gen ) - 1;
viewport.pageNumber = pdfdoc->findPage( ref.num, ref.gen ) - 1;
}
}
docLock.unlock();
delete namedDest;
if ( pageNumber >= 0 )
return QString::number( pageNumber );
if ( viewport.pageNumber >= 0 )
return viewport.toString();
}
return QString();
}
......
......@@ -100,7 +100,6 @@ class PDFGenerator : public Generator
/**
* @short A thread that builds contents for PDFGenerator in the background.
*
*
*/
class PDFPixmapGeneratorThread : public QThread
{
......
......@@ -25,6 +25,7 @@
#include "gp_outputdev.h"
#include "generator_pdf.h"
#include "core/document.h" // for DocumentViewport
#include "core/page.h"
#include "core/link.h"
#include "xpdf/Link.h"
......@@ -343,11 +344,10 @@ KPDFLink * KPDFOutputDev::generateLink( LinkAction * a )
return link;
}
KPDFLinkGoto::Viewport KPDFOutputDev::decodeViewport( GString * namedDest, LinkDest * dest )
DocumentViewport KPDFOutputDev::decodeViewport( GString * namedDest, LinkDest * dest )
// note: this function is called when processing a page, when the MUTEX is already LOCKED
{
KPDFLinkGoto::Viewport vp;
vp.page = -1;
DocumentViewport vp( -1 );
if ( namedDest && !dest )
dest = m_doc->findDest( namedDest );
......@@ -357,11 +357,11 @@ KPDFLinkGoto::Viewport KPDFOutputDev::decodeViewport( GString * namedDest, LinkD
// get destination page number
if ( !dest->isPageRef() )
vp.page = dest->getPageNum() - 1;
vp.pageNumber = dest->getPageNum() - 1;
else
{
Ref ref = dest->getPageRef();
vp.page = m_doc->findPage( ref.num, ref.gen ) - 1;
vp.pageNumber = m_doc->findPage( ref.num, ref.gen ) - 1;
}
// get destination position (fill remaining Viewport fields)
......@@ -379,20 +379,20 @@ KPDFLinkGoto::Viewport KPDFOutputDev::decodeViewport( GString * namedDest, LinkD
case destFit:
case destFitB:
vp.fitWidth = true;
vp.fitHeight = true;
//vp.fitWidth = true;
//vp.fitHeight = true;
break;
case destFitH:
case destFitBH:
// read top, fit Width
vp.fitWidth = true;
//vp.fitWidth = true;
break;
case destFitV:
case destFitBV:
// read left, fit Height
vp.fitHeight = true;
//vp.fitHeight = true;
break;
case destFitR:
......
......@@ -22,11 +22,12 @@
#include <qvaluelist.h>
#include "xpdf/PDFDoc.h" // for 'Object'
#include "xpdf/SplashOutputDev.h"
#include "core/link.h"
class QPixmap;
class TextPage;
class KPDFPageRect;
class KPDFLink;
class DocumentViewport;
/**
* @short A SplashOutputDev renderer that grabs text and links.
......@@ -79,7 +80,7 @@ class KPDFOutputDev : public SplashOutputDev
// generate a valid KPDFLink subclass (or null) from a xpdf's LinkAction
KPDFLink * generateLink( LinkAction * a );
// fills up a Viewport structure out of a given LinkGoto link
KPDFLinkGoto::Viewport decodeViewport( GString *, class LinkDest * );
DocumentViewport decodeViewport( GString *, class LinkDest * );
// generator switches and parameters
bool m_qtThreadSafety;
......
......@@ -12,6 +12,7 @@
#include <qstring.h>
#include <qrect.h>
#include "core/document.h" // for DocumentViewport
/**
* @short Encapsulates data that describes a link.
......@@ -32,25 +33,18 @@ class KPDFLink
class KPDFLinkGoto : public KPDFLink
{
public:
// define a 'Viewport' TODO MERGE WITH DOCUMENT VIEWPORT
struct Viewport {
int page;
bool fitWidth, fitHeight;
QRect rectPercent;
};
// query for goto parameters
bool isExternal() const { return !m_extFileName.isEmpty(); }
const QString & fileName() const { return m_extFileName; }
const Viewport & destViewport() const { return m_vp; }
const DocumentViewport & destViewport() const { return m_vp; }
// create a KPDFLink_Goto
KPDFLinkGoto( QString extFileName, Viewport vp ) { m_extFileName = extFileName; m_vp = vp; }
KPDFLinkGoto( QString extFileName, const DocumentViewport & vp ) { m_extFileName = extFileName; m_vp = vp; }
LinkType linkType() const { return Goto; }
private:
QString m_extFileName;
Viewport m_vp;
DocumentViewport m_vp;
};
......
......@@ -21,28 +21,29 @@
#define THUMBNAILS_ID 4
#define TOC_ID 5
class KPDFPage;
/**
* @short Base class for objects being notified when something changes.
*
* Inherit this class and call KPDFDocument->addObserver( yourClass ) to get
* notified of asyncronous events (new pixmap generated, or changed, etc..).
*/
class KPDFDocumentObserver
class DocumentObserver
{
public:
// you must give each observer a unique ID (used for notifications)
virtual uint observerId() const = 0;
// commands from the Document to all observers
virtual void pageSetup( const QValueVector< class KPDFPage * > & /*pages*/, bool /*documentChanged*/ ) {};
virtual void pageSetCurrent( int /*pageNumber*/, const QRect & /*viewport*/ = QRect() ) {};
enum ChangedFlags { Pixmap = 1, Bookmark = 2, Highlights = 4 };
virtual void notifySetup( const QValueVector< KPDFPage * > & /*pages*/, bool /*documentChanged*/ ) {};
virtual void notifyViewportChanged() {};
virtual void notifyPageChanged( int /*pageNumber*/, int /*changedFlags*/ ) {};
virtual void notifyContentsCleared( int /*changedFlags*/ ) {};
// queries to observers
virtual bool canUnloadPixmap( int /*pageNum*/ ) { return true; }
// monitor changes in pixmaps (generation thread complete)
virtual void notifyPixmapChanged( int /*pageNumber*/ ) {};
virtual void notifyPixmapsCleared() {};
};
#endif
......@@ -223,16 +223,22 @@ Part::~Part()
delete globalParams;
}
void Part::pageSetCurrent( int, const QRect & )
void Part::notifyViewportChanged()
{
// document tells that page has changed, so update actions
updateActions();
// update actions if the page is changed
static int lastPage = -1;
int viewportPage = m_document->viewport().pageNumber;
if ( viewportPage != lastPage )
{
updateActions();
lastPage = viewportPage;
}
}
void Part::goToPage(uint i)
{
if ( i <= m_document->pages() )
m_document->setCurrentPage( i - 1 );
m_document->setViewportPage( i - 1 );
}
void Part::openDocument(KURL doc)
......@@ -260,9 +266,7 @@ bool Part::openFile()
{
bool ok = m_document->openDocument( m_file );
if ( ok && !m_watcher->contains(m_file)) m_watcher->addFile(m_file);
m_find->setEnabled( ok );
m_showProperties->setEnabled( ok );
m_showPresentation->setEnabled( ok );
updateActions();
if ( ok && m_document->getMetaData( "StartFullScreen" ) == "yes" )
slotShowPresentation();
......@@ -330,7 +334,8 @@ bool Part::closeURL()
void Part::updateActions()
{
if ( m_document->pages() > 0 )
bool ok = m_document->pages() > 0;
if ( ok )
{
bool atBegin = m_document->currentPage() < 1;
bool atEnd = m_document->currentPage() >= (m_document->pages() - 1);
......@@ -348,6 +353,9 @@ void Part::updateActions()
m_prevPage->setEnabled( false );
m_nextPage->setEnabled( false );
}
m_find->setEnabled( ok );
m_showProperties->setEnabled( ok );
m_showPresentation->setEnabled( ok );
}
void Part::enableTOC(bool enable)
......@@ -389,29 +397,29 @@ void Part::slotGoToPage()
{
KPDFGotoPageDialog pageDialog( m_pageView, m_document->currentPage() + 1, m_document->pages() );
if ( pageDialog.exec() == QDialog::Accepted )
m_document->setCurrentPage( pageDialog.getPage() - 1 );
m_document->setViewportPage( pageDialog.getPage() - 1 );
}
void Part::slotPreviousPage()
{
if ( !m_document->currentPage() < 1 )
m_document->setCurrentPage( m_document->currentPage() - 1 );
m_document->setViewportPage( m_document->currentPage() - 1 );
}
void Part::slotNextPage()
{
if ( m_document->currentPage() < (m_document->pages() - 1) )
m_document->setCurrentPage( m_document->currentPage() + 1 );
m_document->setViewportPage( m_document->currentPage() + 1 );
}
void Part::slotGotoFirst()
{
m_document->setCurrentPage( 0 );
m_document->setViewportPage( 0 );
}
void Part::slotGotoLast()
{
m_document->setCurrentPage( m_document->pages() - 1 );
m_document->setViewportPage( m_document->pages() - 1 );
}
void Part::slotFind()
......@@ -594,10 +602,8 @@ void Part::slotShowMenu(const KPDFPage *page, const QPoint &point)
case 1:
m_document->toggleBookmark( page->number() );
break;
case 2: // zoom: Fit Width, columns: 1. setActions + relayout + setPage + update
// (FIXME restore faster behavior and txt change as in old pageview implementation)
m_pageView->setZoomFitWidth();
m_document->setCurrentPage( page->number() );
case 2:
m_pageView->fitPageWidth( page->number() );
break;
// case 3: // ToDO switch to edit mode
// m_pageView->slotSetMouseDraw();
......
......@@ -52,7 +52,7 @@ class BrowserExtension;
* @author Wilco Greven <greven@kde.org>
* @version 0.2
*/
class Part : public KParts::ReadOnlyPart, public KPDFDocumentObserver, virtual public kpdf_dcop
class Part : public KParts::ReadOnlyPart, public DocumentObserver, virtual public kpdf_dcop
{
Q_OBJECT
......@@ -64,9 +64,9 @@ public:
// Destructor
~Part();
// inherited from KPDFDocumentObserver
// inherited from DocumentObserver
uint observerId() const { return PART_ID; }
void pageSetCurrent( int pageNumber, const QRect & viewport );
void notifyViewportChanged();
static KAboutData* createAboutData();
......
This diff is collapsed.
......@@ -35,7 +35,7 @@ class PageViewPrivate;
* @short display of course :-)
* ...
*/
class PageView : public QScrollView, public KPDFDocumentObserver
class PageView : public QScrollView, public DocumentObserver
{
Q_OBJECT
......@@ -52,15 +52,15 @@ class PageView : public QScrollView, public KPDFDocumentObserver
void setupActions( KActionCollection * collection );
// used from RMB menu
void setZoomFitWidth();
void fitPageWidth( int page );
// inherited from KPDFDocumentObserver
// inherited from DocumentObserver
uint observerId() const { return PAGEVIEW_ID; }
void pageSetup( const QValueVector<KPDFPage*> & pages, bool documentChanged );
void pageSetCurrent( int pageNumber, const QRect & viewport );
void notifySetup( const QValueVector< KPDFPage * > & pages, bool documentChanged );
void notifyViewportChanged();
void notifyPageChanged( int pageNumber, int changedFlags );
void notifyContentsCleared( int changedFlags );
bool canUnloadPixmap( int pageNum );
void notifyPixmapChanged( int pageNumber );
void notifyPixmapsCleared();
public slots:
void slotSetMouseDraw();
......
......@@ -114,7 +114,7 @@ PresentationWidget::~PresentationWidget()
}
void PresentationWidget::pageSetup( const QValueVector<KPDFPage*> & pageSet, bool /*changed*/ )
void PresentationWidget::notifySetup( const QValueVector< KPDFPage * > & pageSet, bool /*documentChanged*/ )
{
// delete previous frames (if any (shouldn't be))
QValueVector< PresentationFrame * >::iterator fIt = m_frames.begin(), fEnd = m_frames.end();
......@@ -160,17 +160,17 @@ void PresentationWidget::pageSetup( const QValueVector<KPDFPage*> & pageSet, boo
m_metaStrings += i18n( "Click to begin" );
}
bool PresentationWidget::canUnloadPixmap( int pageNumber )
void PresentationWidget::notifyPageChanged( int pageNumber, int changedFlags )
{
// can unload all pixmaps except for the currently visible one
return pageNumber != m_frameIndex;
// check if it's the last requested pixmap. if so update the widget.
if ( (changedFlags & DocumentObserver::Pixmap) && pageNumber == m_frameIndex )
generatePage();
}
void PresentationWidget::notifyPixmapChanged( int pageNumber )
bool PresentationWidget::canUnloadPixmap( int pageNumber )
{
// check if it's the last requested pixmap. if so update the widget.
if ( pageNumber == m_frameIndex )
generatePage();
// can unload all pixmaps except for the currently visible one
return pageNumber != m_frameIndex;
}
......
......@@ -29,18 +29,18 @@ class PresentationFrame;
*
* This is a fullscreen widget that displays
*/
class PresentationWidget : public QWidget, public KPDFDocumentObserver
class PresentationWidget : public QWidget, public DocumentObserver
{
Q_OBJECT
public:
PresentationWidget( KPDFDocument * doc );
~PresentationWidget();
// inherited from KPDFDocumentObserver
// inherited from DocumentObserver
uint observerId() const { return PRESENTATION_ID; }
void pageSetup( const QValueVector<KPDFPage*> & pages, bool documentChanged );
void notifySetup( const QValueVector< KPDFPage * > & pages, bool documentChanged );
void notifyPageChanged( int pageNumber, int changedFlags );
bool canUnloadPixmap( int pageNumber );
void notifyPixmapChanged( int pageNumber );
protected:
// widget events
......
......@@ -76,12 +76,12 @@ ThumbnailList::ThumbnailList( QWidget *parent, KPDFDocument *document )
viewport()->setPaletteBackgroundColor( palette().active().base() );
setFrameStyle( StyledPanel | Raised );
connect( this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotRequestPixmaps(int, int)) );
connect( this, SIGNAL(contentsMoving(int, int)), this, SLOT(slotRequestVisiblePixmaps(int, int)) );
}
//BEGIN KPDFDocumentObserver inherited methods
void ThumbnailList::pageSetup( const QValueVector<KPDFPage*> & pages, bool /*documentChanged*/ )
//BEGIN DocumentObserver inherited methods
void ThumbnailList::notifySetup( const QValueVector< KPDFPage * > & pages, bool /*documentChanged*/ )
{
// delete all the Thumbnails
QValueVector<ThumbnailWidget *>::iterator tIt = m_thumbnails.begin(), tEnd = m_thumbnails.end();
......@@ -132,14 +132,15 @@ void ThumbnailList::pageSetup( const QValueVector<KPDFPage*> & pages, bool /*doc
requestPixmaps( 200 );
}
void ThumbnailList::pageSetCurrent( int pageNumber, const QRect & /*viewport*/ )
void ThumbnailList::notifyViewportChanged()
{
// deselect previous thumbnail
if ( m_selected )
m_selected->setSelected( false );
m_selected = 0;
// select next page
// select the page with viewport and ensure it's centered in the view
int pageNumber = m_document->viewport().pageNumber;
m_vectorIndex = 0;
QValueVector<ThumbnailWidget *>::iterator tIt = m_thumbnails.begin(), tEnd = m_thumbnails.end();
for ( ; tIt != tEnd; ++tIt )
......@@ -156,19 +157,13 @@ void ThumbnailList::pageSetCurrent( int pageNumber, const QRect & /*viewport*/ )
}
}
bool ThumbnailList::canUnloadPixmap( int pageNumber )
void ThumbnailList::notifyPageChanged( int pageNumber, int changedFlags )
{
// if the thubnail 'pageNumber' is one of the visible ones, forbid unloading
QValueList<ThumbnailWidget *>::iterator vIt = m_visibleThumbnails.begin(), vEnd = m_visibleThumbnails.end();
for ( ; vIt != vEnd; ++vIt )
if ( (*vIt)->pageNumber() == pageNumber )
return false;
// if hidden permit unloading
return true;
}
// only handle pixmap changed notifies (the only defined for now)
//if ( !(changedFlags & DocumentObserver::Pixmap) )
// return;
void ThumbnailList::notifyPixmapChanged( int pageNumber )
{
// iterate over visible items: if page(pageNumber) is one of them, repaint it
QValueList<ThumbnailWidget *>::iterator vIt = m_visibleThumbnails.begin(), vEnd = m_visibleThumbnails.end();
for ( ; vIt != vEnd; ++vIt )
if ( (*vIt)->pageNumber() == pageNumber )
......@@ -178,11 +173,24 @@ void ThumbnailList::notifyPixmapChanged( int pageNumber )
}
}
void ThumbnailList::notifyPixmapsCleared()
void ThumbnailList::notifyContentsCleared( int changedFlags )
{
slotRequestPixmaps();
// if pixmaps were cleared, re-ask them
if ( changedFlags & DocumentObserver::Pixmap )
slotRequestVisiblePixmaps();
}
bool ThumbnailList::canUnloadPixmap( int pageNumber )
{
// if the thubnail 'pageNumber' is one of the visible ones, forbid unloading
QValueList<ThumbnailWidget *>::iterator vIt = m_visibleThumbnails.begin(), vEnd = m_visibleThumbnails.end();
for ( ; vIt != vEnd; ++vIt )
if ( (*vIt)->pageNumber() == pageNumber )
return false;
// if hidden permit unloading
return true;
}
//END KPDFDocumentObserver inherited methods
//END DocumentObserver inherited methods